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

  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. 8.多线程和Socket通信

    一.多线程 1.进程的概念: 进程就是应用程序的执行实例,有独立的内存空间和系统资源.当一个应用程序没有执行的时候,它就不是一个进程.   2.进行的特征: (1)动态性:动态产生动态消亡. (2)并 ...

  2. 解决Firefox访问12306"连接不受信任"的问题

    用Firefox访问12306.cn, 总是提示"This Connection is Untrusted", 曾经有个"Add Exception" 按钮, ...

  3. Java实现二分查找算法

    Java程序员总该玩点基本的算法. 1.前提:二分查找的前提是需要查找的数组必须是已排序的,我们这里的实现默认为升序 2.原理:将数组分为三部分,依次是中值(所谓的中值就是数组中间位置的那个值)前,中 ...

  4. Pivot Index--Google

    Return the pivot index of the given array of numbers. The pivot index is the index where the sum of ...

  5. Latex 中插入图片no bounding box 解决方案

    在windows下,用latex插入格式为jpg,png等图片会出现no bounding box 的编译错误,此时有两个解决办法: 1.将图片转换为eps格式的图片 \usepackage{grap ...

  6. k近邻模型

    k近邻模型主要包含三个基本要素:距离度量.k值的选择.分类决策规则 模型: k近邻法中,当训练集.距离度量.k值及分类决策规则确定后,对于一个新的输入实例,它所属的类唯一确定,这相当于根据上述要素将特 ...

  7. PHP下利用PHPMailer配合QQ邮箱下的域名邮箱发送邮件

    作 为PHP入门开发者,常常有这种述求:自己的网站中需要添加一个使用自己的域名作为发件人邮件地址的自动发送邮件的方法,用于诸如给用户发送验证码.通知 信息等.比如:我的某个用户注册模块,需要使用reg ...

  8. csdn如何转载别人的文章

    1.找到要转载的文章,用chrome浏览器打开,右键选择审查元素 2.在chrome中下方的框里找到对应的内容,html脚本中找到对应的节点,选中节点,网页上被选中内容会被高亮显示,然后右键菜单选中 ...

  9. 《Windows驱动开发技术详解》之驱动程序的同步处理

    中断请求级 中断请求被分为软件中断和硬件中断两种,这些中断都映射成不同级别的中断请求级.每个中断请求都有各自的优先级别,正在运行的线程随时都可以被中断打断,进入到中断处理程序.优先级高的中断来临时,处 ...

  10. PhpMyAdmin管理SQLSERVER的问题

    由于项目需要对MSSQL做管理(多个版本的) 之前有个websqladmin的一个开源的项目,但是感觉功能太弱了,而且,采用的SQLDMO的方式,有点过时了. 所以考虑对其升级,方案大概有几种: 1. ...