上一讲已经介绍了基础的OpenGL知识和绘制方面的内容。示例代码都会在我们公司Github找到。Github请搜索Tillusory可以看到。代码都是有注释的,运行环境是Mac OSX的Xcode。win版本的童鞋可以下下来之后放到VS中跑,环境搭建资料很多,有问题的可私信微信公众号。
- 纹理映射 4.1 基本概念 上一讲提到绘制太阳系,那么只是学了上一讲,最多就是画几个球,移动位置,做各种旋转运动等等,这怎么是太阳系。太阳,和各大行星至少要有表面图案吧,那么怎么把这些图案覆盖到球体表面呢?
先来了解下纹理,纹理说白了其实就是一张图,将这张图映射到3D模型上去,增加真实感。图形学硬件要求这些图片像素必须是2的整数幂,并非所有硬件的要求,确保安全而已。
并且,在这里怕大家混淆,讲清楚一个概念。众所周知,屏幕上每个点我们称之为像素(Pixel),但是纹理上的每个点我们不称像素,而是叫纹素(Texel)。使用纹素这个术语,而不是像素来表示纹理对象中的显示元素,主要是为了强调纹理对象的应用方式。纹理对象通常是通过纹理图片读取到的,这个数据保存到一个二维数组中,这个数组中的元素称为纹素(texel),纹素包含颜色值和alpha值。这里先提一下alpha值,颜色参数还记得是rgb吗?其实还有第四个参数,a,这个值就是alpha,控制透明度的。其实这一块搞过U3D开发的其实并不陌生,能讲的也很多,深度测试啊,zbuffer啊,先看下资料吧,后边有需要我细讲。
纹素的坐标我们也不用X,Y来表示,我们使用S,T来表示。ST组成纹理坐标,要想获取纹理对象中的纹素,需要使用纹理坐标(texture coordinate)指定。S,T也可以用U,V来表示,纹理坐标有时也成uv坐标。接触过3D模型的不陌生uv图这种东西,每个模型对应一个uv图,对着uv图设计贴图等等。uv坐标其实就是我们这里说的st坐标,也就是纹理坐标。坐标如下图: 其中左下角为(0,0),右上角为(1,1)。S和T为大于等于0小于等于1的数。通过指定纹理坐标,可以映射到纹素。例如一个256x256大小的二维纹理,坐标(0.5,1.0)对应的纹素即是(128,256)。(256x0.5 = 128, 256x1.0 = 256)。纹理映射时只需要为物体的顶点指定纹理坐标即可,其余部分由片元着色器插值完成。
4.2 读取纹理图片
我们如何在示例代码中添加读取纹理图片的代码,这里我给出一个从BMP文件中读取纹理贴图的方法。这个方法各位可以再Github中看到一个叫bmptotexture.cpp的文件中找到。方法名叫BmpToTexture。使用方法如下:
宽高要求是2的整数幂。如果不是BMP文件也可以使用一些工具转成BMP,比如PS,或者别的工具。
4.3 WRAP参数
纹理坐标都是0-1,那么如果超过0-1怎么办。比如刚刚说的纹理坐标(0.5, 1.0)到纹素的映射,恰好为(128,256)。如果纹理坐标超出[0,0]到[1,1]的范围该怎么处理呢?
这时就用到一个叫WRAP的参数,专门处理这种情况。
先看下具体设置wrap参数的代码:
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, wrap );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, wrap );
其中wrap分为以下两种:
GL_REPEAT:坐标的整数部分被忽略,重复纹理,这是OpenGL纹理默认的处理方式。
GL_CLAMP: 坐标会被截断到[0,1]之间。结果是坐标值大的被截断到纹理的边缘部分,形成了一个拉伸的边缘(stick pattern)。
如下图,左边为GL_CLAMP,右图为GL_REPEAT。