`
gjhappyyy
  • 浏览: 255883 次
  • 性别: Icon_minigender_2
  • 来自: 北京
社区版块
存档分类
最新评论

OpenGl纹理映射

 
阅读更多

本文主要分析如何将纹理映射到多边形和物体上,会包含几种纹理映射的方式。

准备好了纹理下面我们分析如何进行映射,将纹理数据映射到具体的物体上去。纹理坐标如下图所示。
假设图中的正方向就是我们要将纹理映射上去的物体(地面),那么我们需要按照图中的表示,为每个顶点指定一个纹理坐标,也称之为UV坐标,它的横向为s轴,纵向围t轴,如下图所示。关于stuv坐标可以参考一些3D图形学相关知识。
那么下面我们就按照图中的指示来为地面创建一个纹理坐标数组和缓冲区,代码如下:
byte[] tcs = {0,0, 1,0, 0,1, 1,1};
tcsBuf = ByteBuffer.allocateDirect(tcs.length);
tcsBuf.put(tcs).rewind();
准备好纹理的uv坐标之后,下面是最后一步,如何绘制该物体及纹理。代码如下:
 public void draw()
 {
    gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
    gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
    gl.glVertexPointer(3, GL10.GL_BYTE, 0, vertsBuf); // use floor verts
    gl.glTexCoordPointer(2, GL10.GL_BYTE, 0, tcsBuf); // use tex coords
    gl.glEnable(GL10.GL_TEXTURE_2D);
    setTexture();
    gl.glNormal3f( 0, 1.0f, 0);   // facing up
    gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 0, 4);
    gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);
    gl.glDisableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
    gl.glDisable(GL10.GL_TEXTURE_2D);   // switch off texturing
 } // end of draw()
 private void setTexture()
 {
    gl.glBindTexture(GL10.GL_TEXTURE_2D, texNames[0]); // use the tex name
    // specify the texture for the currently bound tex name
    gl.glTexImage2D(GL10.GL_TEXTURE_2D, 0, GL10.GL_RGB, imWidth, imHeight, 0,
                    GL10.GL_RGB, GL10.GL_UNSIGNED_BYTE, texBuf);
    // set the minification/magnification techniques
    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);
 } // end of setTexture()
其中大部分代码我们已经接触过,标注为红色的是需要我们解释的,glEnableClientState函数就是允许我们设置纹理坐标数组,而函数glTexCoordPointer就是将前面准备的纹理坐标缓冲区告诉Opengl,这样在绘制图形时,Opengl会自动的对纹理进行处理,并展现出来。
运行结果如下图所示。
图中的地面就是我们绘制的一个四边形,然后给映射了一个地面的纹理贴图上去形成的。这里我们是直接将一整张图给映射上去了,那么我们是否可以只映射图像的一部分呢?答案是肯定的,我们只需要改动UV坐标即可。UV坐标对应如下图所示。
这样应该很好理解,相当于我们只是映射了图像上的0.250.75之间的部分(整个图像的宽度和高度为1),即图像的中间部分。当然你同样可以更改这个坐标,来调整自己需要映射的部分,这在实际的游戏开发过程中是经常使用的,尤其是一些2D游戏的开发,会将很多小的图片放置到一个大的图片之上,做成一个图片集,就可以通过这种方式来确定需要映射图片集中的哪一个小的图块。按照上图的指示,我们将代码需要改成float类型的数据即可,如下所示。
float[] tcs = {0.25f,0.25f,0.75f,0.25f,0.25f,0.75f,0.75f,0.75f};
tcsBuf = ByteBuffer.allocateDirect(tcs.length*4).asFloatBuffer();
tcsBuf.put(tcs).rewind();
/////
gl.glTexCoordPointer(2, GL10.GL_FLOAT, 0, tcsBuf);
运行效果如下图所示。
和第一个效果图相比很明显的纹理只去了图像的中间一部分,上面的两种方式都是将纹理映射到一个四边形上,但是在Opengl ES中最基本的应该是三角形,我们是否可以将纹理映射到三角形上呢,同样可以,对于三角形的纹理示意图如下所示。
就这样,同样的步骤适合于几何体中任何三角形,而且你甚至可以通过非常规方式的映射来扭曲纹理,产生各种特殊的效果。总而言之,纹理上的任何一点都可以映射到多边形的任何一点。或者换而言之,你可以对任何地点(uv)使用任何(st)而OpenGL ES则为你进行映射。

我们的纹理坐标系统在两个轴上都是从0.0  1.0,如果设置超出此范围的值会怎么样?根据视图的设置方式有两种选择。一种选择是平铺纹理。按OpenGL的术语,也叫“重复”。如果我们将第一个纹理坐标数组的所有1.0改为2.0
float[] tcs = {0.0f,0.0f, 2.0f,0.0f, 0.0f,2.0f, 2.0f,2.0f};
在加入如下代码来设置重复的效果。
gl.glTexParameterx(GL10.GL_TEXTURE_2D,GL10.GL_TEXTURE_WRAP_S,GL10.GL_REPEAT);
gl.glTexParameterx(GL10.GL_TEXTURE_2D,GL10.GL_TEXTURE_WRAP_T,GL10.GL_REPEAT);
那么运行效果就会产生将多个相同的图像平铺到地面的四边形上,下如图所示。
另一种可能的选择是让OpenGL ES简单地将超过1.0的值限制为1.0,任何低于0.0的值限制为 0.0。这实际会引起边沿像素重复,从而产生奇怪的效果。将代码更改为如下所示:
gl.glTexParameterx(GL10.GL_TEXTURE_2D,GL10.GL_TEXTURE_WRAP_S,GL10.GL_CLAMP_TO_EDGE);
gl.glTexParameterx(GL10.GL_TEXTURE_2D,GL10.GL_TEXTURE_WRAP_T,GL10.GL_CLAMP_TO_EDGE);
运行效果如下图所示,这个表现得非常明显。
到这里,我们几乎分析完了Opengl中纹理映射的各种方式,当然也还会有其他的方式,但是原理都一样,这里就不重复了,前面介绍的也都是将纹理映射在平面物体上,在文章的最后,我们将一个纹理映射到一个立方体上去。
首先定义纹理映射坐标,如下代码所示:
 private byte texCoords[] = { // 4 tex coords for each face
    0,0, 1,0, 0,1, 1,1,   0,0, 1,0, 0,1, 1,1,   0,0, 1,0, 0,1, 1,1,
    0,0, 1,0, 0,1, 1,1,   0,0, 1,0, 0,1, 1,1,   0,0, 1,0, 0,1, 1,1
 };
tcsBuf = ByteBuffer.allocateDirect(texCoords.length);
tcsBuf.put(texCoords).rewind();
然后再绘制时加入和之前同样的代码即可,如下所示。
gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
gl.glTexCoordPointer(2, GL10.GL_BYTE, 0, tcsBuf); // use tex coords
gl.glEnable(GL10.GL_TEXTURE_2D);   // use texturing
指定纹理坐标缓冲区,开启纹理,运行效果就会出现一个贴了纹理的立方体,如下图所示。,
这样的渲染结果看上去比前面几个例子真实多了,这主要就是纹理的用作,因此纹理也是游戏开发中的重点,由于条件限制,大家可以测试更多种类的纹理以及其不同的映射方式。为了使游戏更加完美,下一文我们将介绍更适合游戏中物体的表现方式——模型。
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics