`
mmdev
  • 浏览: 12890156 次
  • 性别: Icon_minigender_1
  • 来自: 大连
文章分类
社区版块
存档分类
最新评论

在BlackBerry上使用OpenGL绘图(十一):纹理数组

 
阅读更多

11 纹理数组

现实世界中的物体由不同的材质组成,表面可能会有不同的花纹,这些特性在模拟的3D世界中无法通过简单的平面实现。为了更好地在3D世界中模拟现实物体,设计人员引入了纹理的概念,主要思路是通过2维图片绘制类似于某种材质表面的图像,然后将2维图片贴附在平面上,形成与现实物体类似的表面。

在OpenGL中使用特定方法为某个平面指定纹理,然而在OpenGL ES中,为了节省资源,只提供了指定纹理数组的方法glTexCoordPointer()。纹理数组的使用和法向量数组的使用类似,通过一个数组为不同的顶点指定纹理参数,由系统计算出纹理图片在模型表面的展现形式。

在详细了解纹理数组的使用之前,首先需要了解在OpenGL ES中使用纹理数组的前提条件。

为了使用纹理数组,开发人员首先要启动纹理支持,纹理支持的启动通过在glEnable方法中使用GL10.GL_TEXTURE_2D实现。OpenGLDemo 样例中,该方法在CubeRenderer的contextCreated方法中调用:

gl.glEnable(GL10.GL_TEXTURE_2D);

参数GL10.GL_TEXTURE_2D表示使用2维纹理。在OpenGL中,纹理可以是一维的,也可以是二维的或者是三维的。一维纹理就像一条线,可以认为是宽度为1的二维纹理,一般使用在边框等细长的表面上。二维纹理像是一张图,使用于模型的表面。三维纹理则像一个立方体,可以指定立方体内任一点的纹理特点。三维纹理可以作用于模型内部,使用这种纹理后对模型取一个截面仍能看到纹理效果,这是二维纺理所不能做到的。

对于一维纹理,需要时可以通过二维纹理来实现,而三维纹理对于嵌入设备而言太过于复杂,所以在OpenGL ES中只支持二维纹理。虽然在OpenGL ES中只支持二维纹理,在使用它之前还是需要通过方法glEnable(GL10.GL_TEXTURE_2D)显式地启用它。

启用纹理支持后需要创建纹理所使用的图片,一些简单的纹理图片可以直接通过程序计算得出,比如黑白相间的国际象棋棋盘这样的图片,而复杂的纹理图片一般都通过导入图像资源的方式实现,如实例中使用的BlackBerry的图标。

CubeRenderer的contextCreated中的以下方法导入了资源文件夹中名为“BlackBerry.png”的图片。

net.rim.device.api.system.Bitmap bitmap = net.rim.device.api.system.Bitmap

.getBitmapResource("BlackBerry.png");

导入图片后需要创建和绑定纹理,在OpenGL中每个纹理都有自己的ID,ID不能重复。为了保证纹理的ID不重复,一般都是使用方法glGenTextures方法生成纹理ID,该方法接受三个参数,第一个参数为要生成纹理的个数,第二个参数是存诸纹理ID的整型数组,第三个参数指定在整型数组中的起点。第三个参数可以省略,省略时表示以0作为起点。CubeRenderer的contextCreated方法通过以下代码生成一个纹理ID

int[] textures = new int[1];

gl.glGenTextures(1, textures, 0);

int textureId = textures[0];

在生成纹理ID的过程中先定义成员数为1的整型数组,然后调用glGenTextures方法生成一个纹理ID,将结果保存在textures中,保存时以0作为起点,就是将结果保留在textures[0]中。最后将textures[0]中的值赋予变量textureId。

生成纹理ID之后通过方法glBindTexture绑定纹理。方法glBindTexture有两个作用:第一个作用是创建纹理,当一个纹理ID第一次被绑定时系统会创建一个纹理;第二个作用是绑定纹理,指明之后的操作都使用纹理ID对应的纹理。最后通过GLUtils的glTexImage2D方法将导入的图像载入到纹理中。代码如下:

gl.glBindTexture(GL10.GL_TEXTURE_2D, textureId);

net.rim.device.api.opengles.GLUtils.glTexImage2D(gl, 0, GL10.GL_RGB,GL10.GL_UNSIGNED_SHORT_5_6_5, bitmap, null);

注意其中变量bitmap指向的就是之前导入的BlackBerry图标。

绑定纹理后,CubeRenderer中的contextCreated方法还对纹理变量做了一些设置,具体作用读者可以暂时不去了解,希望详细了解以下代码作用的读者请参考API文档。

gl.glTexEnvx(GL10.GL_TEXTURE_ENV, GL10.GL_TEXTURE_ENV_MODE,

GL10.GL_MODULATE);

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);

纹理设置好之后就可以开始使用它,OpenGLDemo样例是在CubeRenderer的render方法中使用纹理。如上所述,在OpenGL ES中只支持纹理数组,所以在render方法中需要先启动纹理数组的支持,方法如下:

gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY);

之后可以为模型指定纹理数组,样例OpenGLDemo在CubeRenderer的initialize方法中通过Cube的createTexCoordBuffer静态方法生成了纹理数数,具体代码和之前分析的顶点数组生成代码类似。

_cubeTexCoords = Cube.createTexCoordBuffer();

为了更好地理解纹理数组的使用,在简化样例中我们直接在CubeRenderer的render方法中生成纹理数组。需要再一次强调的的render方法每次绘制都会被调用,在render方法中生成纹理会导致效率降底。简化样例中之所以在render方法中生成纹理数是为了让读者更直观地理解纹理数组的使用,在现实项目中一定不要在render过程中生成纹理数组。

二维纹理数组是一个浮点数组,在二维纹理数组中,每两个成员对应一个项点。如数组{a1,a2,a3,a4,a5,a6}对应的是三个项点,其中a1,a2对应项点数组中的第一个顶点,a3,a4对应第二个顶点,a5,a6对应第三个项点。每个顶点对应的两个浮点数指定该项点的纹理坐标,就是顶点在纹理图片上的X和Y值。其中的X和Y值是一个相对值,系统自动将纹理图片的左上角定义为{0,0},左下角定义为{0,1},右上角定义为{1,0},右下角定义为{1,1}。如下图中的A,B,C和D点。

图18-10 纹理坐标

如果在模型中定义一个正角等腰三角形,顶点如图18-10的A,B,C点,同时希望纹理图片按上图的方式贴附在三角形上,则顶点A,B,C对应的纹理数组为{0,0,0,1,1,0}。

纹理数组中的数值并不一定需要是0或者是1,按应用需要,开发人员可以计算出顶点在纹理图片中相对位置,从而指定顶点的纹理坐标。如图18-11的纹理效果,开发人员需要纹理图片的一部分出现在所定义的三角形中,其中A点对应的纹理坐标是{0.25,0.25},B点对应的纹理坐标是{0.5,0.5},C点对应的纹理坐标是{0.75,0,25}。则该平面对应的纹理数组为{0.25,0.25,0.5,0.5,0.75,0.25}。

图18-11 更复杂的纹理坐标

以18.5小节的简化样例简化样例为基础加入纹理坐标。在该简休样例的模型中有三个三角平面,顶点数组如下,一共有9个顶点,27个成员。

float[] _vertices = {

-0.5f, 0.5f, 0.5f, -0.5f, -0.5f, 0.5f, 0.5f, 0.5f, 0.5f,

-0.5f, 0.5f, 0.5f, -0.5f, 0.5f, -0.5f, -0.5f, -0.5f, 0.5f,

-0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, -0.5f, 0.5f, -0.5f };

则定义纹理数组如下,共有9个顶点,定义18个成员

float[] _texCoords = {

0, 0, 0, 1, 1, 0,

0, 0, 0, 1, 1, 0,

0, 0, 0, 1, 1, 0, };

FloatBuffer texCoordBuffer = ByteBuffer.allocateDirect(

_texCoords.length * 4).asFloatBuffer();

texCoordBuffer.put(_texCoords);

texCoordBuffer.rewind();

定义纹理数组之后,在方法glDrawArrays调用之前通过方法glTexCoordPointer方法使用该纹理数组,glTexCoordPointer有三个参数。第一个参数用于指定几个成员对应一个顶点,现在我们使用的方法是两个成员对应一个顶点。第二个参数用于指定数组成员的类型,GL10.GL_FLOAT表示数组成员是浮点型的。第三个参数则是纹理数组。

gl.glTexCoordPointer(2, GL10.GL_FLOAT, 0, texCoordBuffer);

gl.glDrawArrays(GL10.GL_TRIANGLES, 0, vertexNumber);

运行结果如图18-12:

图18-12 简化样例,增加纹理效果后

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics