通常一个纹理映射的步骤是:

  1. 创建纹理对象。就是获得一个新的纹理句柄 ID.
  2. 指定纹理。就是将数据赋值给 ID 的纹理对象,在这一步,图像数据正式加载到了 ID 的纹理对象中。
  3. 设定过滤器。定义了opengl现实图像的效果,如纹理放大时的马赛克消除。
  4. 绑定纹理对象。就是将 ID 的纹理作为下面操作的纹理。
  5. 纹理映射。将已绑定纹理的数据绘制到屏幕上去,在这一步,就能看到贴图的效果了。

一、opengl 中启用纹理映射功能

在默认设置中,纹理映射是关闭的,启用的参数是 GLTEXTURE2D, 还有其他的参数: GL_TEXTURE_1D, GL_TEXTURE_3D, GL_TEXTURE_CUBE_MAP。我们只用到2D纹理,其他不再赘述。

gl.glEnable(GL_TEXTURE_2D)

二、创建纹理

创建纹理,用函数 glGenTextures() 完成,函数返回新创建的纹理的 ID。此函数可以创建 n 个纹理,并将纹理ID 放在 textures 中:

 
void glGenTextures (int n, IntBuffer textures)

范例:

1
2
3
IntBuffer intBuffer = IntBuffer.allocate(1);
gl.glGenTextures(1, intBuffer);
int textureId = intBuffer.get(); // 纹理 ID

指定纹理

OpenGL 提供了三个函数来指定纹理: glTexImage1D(), glTexImage2D(), glTexImage3D(). 这三个版本用于相应维数的纹理,我们用到的是 2D 版本: glTexImage2D().

 
void glTexImage2D (int target, int level, int internalformat, int width, int height, int border, int format, int type, Buffer pixels)

参数过多,可以使用 GLUtils 中的 texImage2D() 函数,好处是直接将 Bitmap 数据作为参数:

 
void texImage2D (int target, int level, Bitmap bitmap, int border)

参数:

target
操作的目标类型,设为 GL_TEXTURE_2D 即可
level
纹理的级别,本节不涉及,设为 0 即可
bitmap
图像
border
边框,一般设为0
1
GLUtils.texImage2D (GL10.GL_TEXTURE_2D, 0, mBitmap, 0);

删除纹理

删除纹理, 第三个参数指明了第二个参数 textures 数组中纹理ID 的步长,一般是紧凑顺序存放,设为0即可。

 
void glDeleteTextures (int n, int[] textures, int offset)

绑定纹理

绑定后,此纹理处于活动状态。在第一次绑定一个纹理对象时, 会将一系列初始值来适应你的应用。绑定比较简单,用函数 glBindTexture():

 
void glBindTexture (int target, int texture)

第一个参数是纹理类型,我们使用 2D 纹理,参数设为 GL_TEXTURE_2D, 第二个参数是纹理对象的 ID。

设置过滤器

有两个版本:float版和int版本。

 
void glTexParameterf (int target, int pname, float param)  
void glTexParameterx (int target, int pname, int param)

一般我们设置两个, 一个放大器的: GL_TEXTURE_MAG_FILTER, 一个缩小器的: GL_TEXTURE_MIN_FILTER.

下面的两行告诉OpenGL在显示图像时,当它比放大得原始的纹理大 ( GL_TEXTURE_MAG_FILTER )或缩小得比原始得纹理小( GL_TEXTURE_MIN_FILTER )时OpenGL采用的滤波方式。

通常这两种情况下我都采用 GL_LINEAR 。这使得纹理从很远处到离屏幕很近时都平滑显示。使用 GL_LINEAR 需要CPU和显卡做更多的运算。

如果您的机器很慢,您也许应该采用 GL_NEAREST 。过滤的纹理在放大的时候,看起来斑驳的很(马赛克)。您也可以结合这两种滤波方式。在近处时使用 GL_LINEAR ,远处时 GL_NEAREST 。

1
2
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR); // 线形滤波
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR); // 线形滤波

 三、纹理映射

用函数 glTexCoordPointer 指定纹理坐标数组,

 
void glTexCoordPointer (int size, int type, int stride, Buffer pointer)

默认这个功能是关闭的,所以需要打开:

1
2
3
4
gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
// ... 
// 关闭
gl.glDisableClientState(GL10.GL_TEXTURE_COORD_ARRAY);

2 常见的几个问题

 

2.1 贴图呈现白色

可能的原因:

  • 未启用 GL_TEXTURE_2D 选项。请使用 glEnable()glDisable() 函数进行开启和关闭。
  • 纹理对象无数据。 使用 GLUtils.texImage2D() 来指定,指定前需 glBindTexture() 激活当前纹理。

2.2 图像扭曲

可能的原因:

  • 纹理坐标和顶点坐标对应关系是否正确,调整之
  • 图像的大小不是 2 的次幂, 解决: 内部重新生成一张 2 的次幂的image,调整uv坐标

3 代码实现

先定义一个纹理对象,其基本接口有:

  • 创建+指定。 构造函数完成
  • 绑定。
  • 绘制。

@note: 为了处理 2 的次幂,内部对原始图像不是2的次幂的重新建立了一个图像。详见代码吧。

public class Texture2D {
private int mWidth;
private int mHeight;
private int mPow2Width;
private int mPow2Height;
private float maxU = 1.0f;
private float maxV = 1.0f; private Bitmap mBitmap = null; private int textureId = ; // 删除纹理数据
public void delete(GL10 gl)
{
if (textureId != ){
gl.glDeleteTextures(, new int[]{textureId}, );
textureId = ;
} // bitmap
if (mBitmap != null)
{
if (mBitmap.isRecycled())
mBitmap.recycle();
mBitmap = null;
} } public static int pow2(int size)
{
int small = (int)(Math.log((double)size)/Math.log(2.0f)) ;
if ( ( << small) >= size)
return << small;
else
return << (small + );
} // 构建,推迟到第一次绑定时
public Texture2D(Bitmap bmp)
{
// mBitmap = bmp;
mWidth = bmp.getWidth();
mHeight = bmp.getHeight(); mPow2Height = pow2(mHeight);
mPow2Width =pow2(mWidth); maxU = mWidth/(float)mPow2Width;
maxV = mHeight/(float)mPow2Height; Bitmap bitmap = Bitmap.createBitmap(mPow2Width, mPow2Height,
bmp.hasAlpha() ? Bitmap.Config.ARGB_8888 : Bitmap.Config.RGB_565);
Canvas canvas = new Canvas(bitmap);
canvas.drawBitmap(bmp, , , null);
mBitmap = bitmap;
} // 第一次会加载纹理数据
public void bind(GL10 gl)
{
if (textureId ==)
{
int[] textures = new int[];
gl.glGenTextures(, textures, );
textureId = textures[]; gl.glBindTexture(GL10.GL_TEXTURE_2D, textureId); gl.glTexParameterx(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_LINEAR);
gl.glTexParameterx(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER, GL10.GL_LINEAR); GLUtils.texImage2D(GL10.GL_TEXTURE_2D, , mBitmap, ); mBitmap.recycle();
mBitmap = null;
} gl.glBindTexture(GL10.GL_TEXTURE_2D, textureId);
} // 绘制到屏幕上
public void draw(GL10 gl, float x, float y)
{
gl.glEnable(GL10.GL_TEXTURE_2D);
gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
gl.glEnableClientState(GL10.GL_VERTEX_ARRAY); // 绑定
this.bind(gl); // 映射
FloatBuffer verticleBuffer = FloatBuffer.wrap(new float[]{
x,y,
x+mWidth, ,
x, y+mHeight,
x+mWidth, y+mHeight,
});
FloatBuffer coordBuffer = FloatBuffer.wrap(new float[]{
,,
maxU,,
,maxV,
maxU,maxV,
}); gl.glTexCoordPointer(, GL10.GL_FLOAT, , coordBuffer);
gl.glVertexPointer(, GL10.GL_FLOAT, , verticleBuffer);
gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP,,); gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);
gl.glDisableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
gl.glDisable(GL10.GL_TEXTURE_2D);
} public void draw(GL10 gl, float x, float y, float width, float height)
{
gl.glEnable(GL10.GL_TEXTURE_2D);
gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
gl.glEnableClientState(GL10.GL_VERTEX_ARRAY); // 绑定
bind(gl); // 映射
// 映射
FloatBuffer verticleBuffer = FloatBuffer.wrap(new float[]{
x,y,
x+width, ,
x, y+height,
x+width, y+height,
});
FloatBuffer coordBuffer = FloatBuffer.wrap(new float[]{
,,
maxU,,
,maxV,
maxU,maxV,
}); gl.glTexCoordPointer(, GL10.GL_FLOAT, , coordBuffer);
gl.glVertexPointer(, GL10.GL_FLOAT, , verticleBuffer);
gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP,,); gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);
gl.glDisableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
gl.glDisable(GL10.GL_TEXTURE_2D); gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);
gl.glDisableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
gl.glDisable(GL10.GL_TEXTURE_2D); } }

4 贴图一个机器人

代码很简单了,在场景 scene 的 draw() 中绘制一个 texture2D, 具体下载代码看看吧:

public class AndroidScene extends GlObject{
Texture2D texture; public AndroidScene()
{
super(); // 使用 assets 文件夹下的 androida.jpg
Bitmap androidBitmap = GameSystem.getInstance().getBitmapFromAssets("androida.jpg");
texture = new Texture2D(androidBitmap);
} public void draw(GL10 gl)
{
texture.draw(gl, , );
}
}

这一节有点枯燥,学习愉快。

OpenGL ES 详解纹理生成和纹理映射步骤以及函数的更多相关文章

  1. Android OpenGL ES 3.0 纹理应用

    本文主要演示OpenGL ES 3.0 纹理演示.接口大部分和2.0没什么区别,脚本稍微有了点变化而已. 扩展GLSurfaceView package com.example.gles300; im ...

  2. Qt的Graphics-View框架和OpenGL结合详解

    Qt的Graphics-View框架和OpenGL结合详解 演示程序下载地址:这里 程序源代码下载地址:这里 这是一篇纯技术文,介绍了这一个月来我抽时间研究的成果. Qt中有一个非常炫的例子:Boxe ...

  3. OpenGL ES为缓存提供数据的7个步骤

    OpenGL ES为缓存提供数据的7个步骤: 1.生成glGenBuffers()——请求OpenGL ES为图形处理器控制的缓存生成一个独一无二的标识符. 2.绑定glBindBuffer()——告 ...

  4. 【OpenGL】详解第一个OpenGL程序

    写在前面 OpenGL能做的事情太多了!很多程序也看起来很复杂.很多人感觉OpenGL晦涩难懂,原因大多是被OpenGL里面各种语句搞得头大,一会gen一下,一会bind一下,一会又active一下. ...

  5. Android OpenGL ES(七)----理解纹理与纹理过滤

    1.理解纹理 OpenGL中的纹理能够用来表示图像.照片,甚至由一个数学算法生成的分形数据.每一个二维的纹理都由很多小的纹理元素组成.它们是小块的数据,类似于我们前面讨论过的片段和像素.要使用纹理,最 ...

  6. 一文详解 纹理采样与Mipmap纹理——构建山地渲染效果

    在开发一些相对较大的场景时,例如:一片铺满相同草地纹理的丘陵地形,如果不采用一些技术手段,就会出现远处的丘陵较近处的丘陵相比更加的清晰的视觉效果,而这种效果与真实世界中近处的物体清晰远处物体模糊的效果 ...

  7. spring boot2集成ES详解

    一:运行环境 JDK:1.8 ES:5.6.4 二:学习内容 如何构建spring-data-elasticsearch环境? 如何实现常用的增删改查? 如何实现对象嵌套也就是1对多这种关系? 三:J ...

  8. JavaScript正则表达式详解(二)JavaScript中正则表达式函数详解

    二.JavaScript中正则表达式函数详解(exec, test, match, replace, search, split) 1.使用正则表达式的方法去匹配查找字符串 1.1. exec方法详解 ...

  9. 详解C++中的多态和虚函数

    一.将子类赋值给父类 在C++中经常会出现数据类型的转换,比如 int-float等,这种转换的前提是编译器知道如何对数据进行取舍.类其实也是一种数据类型,也可以发生数据转换,但是这种转换只有在 子类 ...

随机推荐

  1. Linux shell 操作 postgresql,并设置crontab任务

    Linux shell 操作 postgresql:删除间隔日期的数据-删除指定日期的数据-vacuumdb 清理数据库 -清理日志 -定期执行脚本 *修改pg_hba.conf 设置本地连接无密码, ...

  2. Binary Trees

    1. Definiation What is Binary Trees? Collection of node (n>=0) and in which no node can have more ...

  3. Traffic Ccontrol(流量控制)

    linux有一个成熟的带宽供给系统,称为Traffic Control(流量控制).这个系统支持各种方式进行分类.排序.共享和限制出入流量. 一.基础知识 让ip显示我们的链路 ip link  li ...

  4. gcc 编译

    ../gcc-5.2.0/configure --enable-threads=posix --disable-checking --disable-multilib --enable-languag ...

  5. Leetcode016 3Sum Closest

    public class S016 { //借鉴S015的思想,只是稍微有点慢 public int threeSumClosest(int[] nums, int target) { Arrays. ...

  6. CevaEclipse - 编译器attribute扩展

    1.函数与变量的 Section Attribute void foobar (void) __attribute__ ((section (".CSECT mmm"))); vo ...

  7. 安装 sublime package control

    import urllib.request,os; pf = 'Package Control.sublime-package'; ipp = sublime.installed_packages_p ...

  8. 获取当前设备的IP地址

    头文件: #import <ifaddrs.h> #import <arpa/inet.h> #import <net/if.h> 宏定义: #define IOS ...

  9. 使用 NSUserDefaults 读取和写入自定义对象

    众所周知,NSUserDefaults只能保存诸如NSArray.NSDictionary.NSData.NSNumber等基本数据类型,如果我们强制保存自定义的类,就会出现这个错误:Attempt ...

  10. Heap Operations(模拟题)

     Heap Operations time limit per test 1 second memory limit per test 256 megabytes input standard inp ...