Android OpenGL es 3D Rubik Cube 多纹理贴图

Jan 06, 2011

最近在帮实验室的一个android魔方应用修改代码,肤浅的接触了一些opengl es多纹理贴图方面的东西。初学opengl,一点图形编程的基础都没有,走了不少弯路,下面把自己在实际操作中遇到的一些主要问题和解决方法跟大家分享下!

资料:

一个比较有参考价值的代码http://oger.javaeye.com/?show_full=true 如果想系统的学习opengl 方面的知识见nehe论坛:http://nehe.gamedev.net/

由于android apidemo中有关于魔方的示例代码,不过apidemo中的模仿是用颜色填充的不是贴图贴上去的,魔方的贴图是基于android apidemo中的代码做修改实现的。

现在主要对实践中遇到的几个问题坐下总结:

1.由于apidemo中的魔方顶点坐标只是单纯的顶点坐标,不是按面罗列出的,也就是说每个顶点在顶点数组中只出现一次,所以在贴图过程中不能使用gl.glDrawArrays()函数,而应当使用有顶点映射功能的gl.glDrawElements()函数,以左上角第一个小立方体为例说明,它的六个面对应的顶点坐标映射是:

indices1 = ByteBuffer.wrap(new byte[] { 4, 5, 6, 7, });
indices2 = ByteBuffer.wrap(new byte[] { 0, 2, 1, 3, });
indices3 = ByteBuffer.wrap(new byte[] { 4, 6, 0, 2, });
indices4 = ByteBuffer.wrap(new byte[] { 1, 3, 5, 7, });
indices5 = ByteBuffer.wrap(new byte[] { 6, 7, 2, 3, });
indices6 = ByteBuffer.wrap(new byte[] { 4, 0, 5, 1, });

以第3个面为例,贴出的参数为: gl.glDrawElements(GL10.GL_TRIANGLE_STRIP, 4,GL10.GL_UNSIGNED_BYTE, indices3); 下面详细的说明下第二个参数代表的含义,在网上对于第二个参数的理解有两种说法,一种是要贴的图像(不只局限为平面图行)的顶点的个数,一种是平面的个数乘以每个平面顶点的个数。这里需要注意的是第二种说法才是正确的!由于本例中由于是多纹理映射,每个面对应的材质不同所以按面为单位贴,每个正方形面4个顶点,所以对应的参数为4。

原apidemo中的mIndexBuffer是按照三角形做顶点映射的,具体的可以打LOG查看: 在draw()中添加如下代码即可查看:

if(count  == 0)
       for(int i =0 ; i < 12 * 26; ++i)
              Log.i("mindexBuffer","x:" + Integer.toString(mIndexBuffer.get(i * 3)) + " y: "     
                                               + Integer.toString(mIndexBuffer.get(i* 3 + 1)) + " z: "
                                               + Integer.toString(mIndexBuffer.get(i* 3 + 2)));                                                       

2.关于纹理坐标的问题。 由于opengl es没有自动生成纹理坐标的函数(opengl有),需要自己定义纹理坐标。 常见的纹理坐标有两种单位:

gl.glTexCoordPointer(2, GL10.GL_FLOAT, 0, mTextureBuffer);   //floatbuffer类型。以1.0为单位
gl.glTexCoordPointer(2, GL10.GL_GL_FIXED, 0, mTextureBufferFront);  //像素intbuffer类型的。以0x1000即65536为单位。

关于纹理坐标的写法详见我上面发的url。

这里有个非常重要的问题就是顶点坐标的坐标映射对纹理坐标同样有效,也就是说上面提到的indices1,indices2,indices3,indices4,indices5,indices6对纹理坐标同样有效。这样我们得到的第一个小立方体对应的六个面的textureBuffer分别为:

mTextureBufferFront = FloatBuffer.wrap(new float[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 1, 0,});
mTextureBufferBack = FloatBuffer.wrap(new float[] { 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,});
mTextureBufferLeft = FloatBuffer.wrap(new float[] { 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0,});
mTextureBufferRight = FloatBuffer.wrap(new float[] { 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0,});
mTextureBufferTop = FloatBuffer.wrap(new float[] { 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0,});
mTextureBufferBottom = FloatBuffer.wrap(new float[] {0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0,});

3.虚拟机可以正常加载图片,但是真机无法加载图片

不知道是不是内部设计的缺陷,opengl es只能加载名为drawable文件下图片,高中低像素文件夹drawable-hdpi,drawable-mdpi,drawable-ldpi问价下的图片都无法加载;

而且图片最好是png或者bmp,jpg格式的图片由于位深原因可能无法正常贴图;

图片必须得是n2大小的;

常量的话最好使用GL10而不是GL11;

顶点坐标缓存,纹理坐标缓存,索引缓存如果不是native型直接的缓存,会在较高版本的真机运行时出现: “called a GL11 Pointer method with an indirect Buffer. ” 的错误。以纹理坐标为例,正确的写法必须是:

bb = ByteBuffer.allocateDirect(num * 4);
bb.order(ByteOrder.nativeOrder());
mTextureBufferFront = bb.asFloatBuffer();

以上,欢迎相互交流学习! 附上运行效果图:

Get In Touch With Us ...