
正文
OpenGL ES 详解纹理生成和纹理映射步骤以及函数
提示:扫一扫查出行【扫一扫了解最新限行尾号】
复制提示
通常一个纹理映射的步骤是:
- 创建纹理对象。就是获得一个新的纹理句柄 ID.
- 指定纹理。就是将数据赋值给 ID 的纹理对象,在这一步,图像数据正式加载到了 ID 的纹理对象中。
- 设定过滤器。定义了opengl现实图像的效果,如纹理放大时的马赛克消除。
- 绑定纹理对象。就是将 ID 的纹理作为下面操作的纹理。
- 纹理映射。将已绑定纹理的数据绘制到屏幕上去,在这一步,就能看到贴图的效果了。
一、opengl 中启用纹理映射功能
在默认设置中,纹理映射是关闭的,启用的参数是 GL TEXTURE 2D , 还有其他的参数: 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坐标
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, , );
}
}
这一节有点枯燥,学习愉快。
先定义一个纹理对象,其基本接口有:
- 创建+指定。 构造函数完成
- 绑定。
- 绘制。
@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, , );
}
}
这一节有点枯燥,学习愉快。
代码很简单了,在场景 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, , );
}
}
这一节有点枯燥,学习愉快。






