Opengl中的纹理映射(texture mapping)是它的精华部分,在opengl的书中这一章也是所有章节中最为多的,在所有的opengl技术中,关于纹理映射的技术也是最多最丰富的,同时也是被开发人员使用各种trick最多的,因为纹理映射可以做很多的工作。在opengl的书中关于纹理映射有着很多的高级话题,而在这里,我只记录关于纹理映射的基础。
在纹理矩阵中的一个元素被称为texel,纹理矩阵可是是一维、二维、三维,texel也可以有rgba分量。纹理映射不能工作在索引颜色模式下
纹理映射的一般步骤:
产生纹理物体的名字glgentexture
绑定这个纹理物体glbind
glTexParameter设定纹理的各种参数
为纹理物体指定纹理gltextureimage
打开各种开关
在绘制时为绘制物体产生纹理坐标
1. 产生纹理物体:在opengl中,纹理映射是一个状态,同一时刻只能有一种纹理活动,但是我们的程序中通常需要定义大量的不同的纹理,在这些纹理间需要随时的切换,纹理物体就好比一个存储一种纹理状态的变量,当程序中一个纹理物体被绑定后,所有对纹理的参数的设定都是改变当前这个纹理物体代表的纹理映射的。需要先用glgentexture来产生一个纹理物体的名字。然后用glbind来绑定这个纹理后,就激活了这个纹理,这是程序中的活跃纹理是当前这个纹理,对纹理各种属性的修改也是对这个纹理,直到再绑定另外一个纹理。
2. glTexParameteri()指定纹理的各种属性:这些属性包括:
GL_TEXTURE_MIN/MAG_FILTER 当纹理贴图需要被放大或缩小时的插值方式
此属性通常必须要指定,否则可能看不到贴图
GL_TEXTURE_MIN/MAG_LOD 设定纹理的最小和最大LOD级别(可为正负)
GL_TEXTURE_MAX_LOD 设定最小的那个mipmap的序号
GL_TEXTURE_MAX_LEVE设定最大的那个mipmap的序号
GL_TEXTURE_WRAP_S/T/R 设定纹理坐标的扩展方式,在这个参数下,可以使用的扩展方式有:GL_CLAMP(夹紧), GL_CLAMP_TO_BORDER, GL_CLAMP_TO_EDGE, GL_MIRRORED_REPEAT, or GL_REPEAT(重复),各种铺陈方式的效果如图:
GL_TEXTURE_BORDER_COLOR 设置纹理的边界(BORDER)颜色
GL_TEXTURE_PRIORITY 设置纹理在GPU缓存中的优先级,通常为了性能考虑,纹理会被放在GPU的缓存中,但是存放的数量是有限的,这个参数可以设定在GPU中的存放优先级
GL_GENERATE_MIPMAP 设定当base mipmap改变后是否更新所有的mipmap
GL_TEXTURE_COMPARE_MODE GL_TEXTURE_COMPARE_FUNC和GL_DEPTH_TEXTURE_MODE属性是专门为阴影贴图(shadow map)使用的,将在后面讲解
3使用glTexEnv ( GLenum target, GLenum pname, GLfloat param);来设定纹理值是怎么样起作用的:
glTexEnvf(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE,…)来指定纹理的颜色与物体的原来颜色的混合方式,有GL_ADD, GL_MODULATE, GL_DECAL, GL_BLEND, GL_REPLACE, or GL_COMBINE.
4产生纹理:
前面设定好纹理及生成方式的各种属性后,就该实际的产生纹理了,产生纹理在opengl中有一下几种方式:
从内存数据产生纹理:这是最直接的生成纹理的方式,就是用void glTexImage2D( GLenum target,
GLint level,
GLint internalFormat,
GLsizei width,
GLsizei height,
GLint border,
GLenum format,
GLenum type,
const GLvoid * data)函数,从内存大数据构建纹理,通常你可以读入一个图像然后得到这个图像的数据作为data。
直接产生LOD纹理:用gluBuild2DMipmaps可以从一片内存数据构建一个LOD的纹理
从帧缓存产生纹理:glCopyTexImage2D可以将当前的一块帧缓存作为纹理,使用它时glPixelStore是起作用的。
5为绘制物体产生纹理坐标
产生纹理坐标有两种方式:
用glTexCoord指定当前的纹理坐标,它时一个状态函数,设定后如果不改变,所有之后的绘制的顶点的纹理坐标都是它
自动生成纹理:opengl有着强大的自动为物体生成纹理的机制,生成方式是依靠glTexGeni函数:自动生成纹理的过程是:
用glTexGeni(GL_S,GL_TEXTURE_GEN_MODE,…)设定生成纹理的平面参考方式
它可以是GL_EYE_LINEAR或者GL_OBJECT_LINEAR,即用视平面或者物体平面做参考。
用glTexGenfv(GL_S,GL_EYE_PLANE,param)来设定这个平面,param为{p1,p2,p3,p4},最后生成纹理坐标为p1x0 + p2y0 + p3z0 + p4w0,这个param其实就是平面方程的参数
视平面与物体平面的区别就是视平面中纹理坐标是相对于视坐标系的,视角变了纹理坐标也就随着变了
两种方式产生的纹理的区别如图:
6纹理矩阵:纹理也是一个矩阵,也可以用opengl中的各种矩阵操作来改变它,只不过在修改纹理矩阵的时候要先使用glmatrixmode(GL_TEXTURE)来切换到纹理矩阵
7需要打开的开关
在做纹理映射的时候,需要打开这些开关
最开始要打开
glEnable(GL_TEXTURE_1D/2D/CUBE_MAP);
如果使用了自动产生纹理坐标就还要打开
glEnable(GL_TEXTURE_GEN_S);
glEnable(GL_TEXTURE_GEN_T);
glEnable(GL_TEXTURE_GEN_R);
高级话题:
Cube map:cubemap是假想在绘制物体是可以反光的,它四周(六个面)有一个空间,然后将这六个面的图像映射到这个物体上,最后会产生这个物体反射空间景物的效果,常常可以提高绘制的真实感,CUBE MAP的步骤为:
用glBindTexture(GL_TEXTURE_CUBE_MAP,cubMapTextureName)将一个纹理物体绑定到CUBE MAP纹理上;
用glTexGeni(GL_S/T/R,GL_TEXTURE_GEN_MODE,…)为纹理物体产生纹理坐标,这里的产生方式只能是GL_REFLECTION_MAP或者GL_NORMAL_MAP,这两种的效果区别为:
reflection
normal
glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X,0,GL_RGB…)为每一个面选择那个面的图像数据,一共要贴6个面
注意:在opengl中默认的用这种方式为物体产生坐标是在视坐标系中的,也就是说如果视角转动,纹理不会改变,如果想更加的真是,就应该让这个坐标在世界坐标系下,应该将这个纹理矩阵乘上视矩阵的逆矩阵。
Shadow map:
为了增强真实感,在绘制中产生阴影是及其重要的,其中一个重要的生成阴影的方法就是使用阴影贴图。阴影贴图的技术思想为:
首先将视角定位到光源的位置,视角的朝向为光源的朝向,在这个位置截取当前帧缓存中的深度值作为后面的纹理贴图。
然后为物体产生纹理坐标,产生的方式是按照模型为参考,参考面的参数就是这是modelview矩阵的逆矩阵,这时产生的纹理坐标的R就是该点的深度值。
最后设定glTexParameteri(GL_DEPTH_TEXTURE_MODE为GL_LUMINANCE),它将最后用一个灰度值来绘制点的纹理颜色
设定glTexParameteri(GL_TEXTURE_COMPARE_MODE)为GL_COMPARE_R_TO_TEXTURE,也就是比较该店的R坐标,它此时已经代表了该点的Z值
设定glTexParameteri(GL_TEXTURE_COMPARE_FUNC)为LEQUAL,也就是用该店的R坐标与此时深度图的Z值比较,如果比Z深,则用1来绘制,就是阴影了。
最后将视角切换回来
使用nvdia openg sdk 的nvimage类做辅助:
NVdia OpenGl SDK中的nvImage类封装了一个图片类,它专门用来做opengl中的贴图,这个类作用很大,因为我们在处理纹理映射时,通常要用一些图片文件来做贴图,而读取保存这些图片的数据通常很费力气,这个类为我们提供了一些辅助的功能。它的几个主要功能接口如下:
1. 它可以直接读取解析的图片格式为png hdr(这是一种高动态范围图片,图片质量相当高,颜色级范围很高,非常适用高真实感的cubemap或者天空盒等) dds(D3D的一种压缩图片)
2. 它可以得到当前图像的尺寸、深度、mipmaplevel、faces、Format和InternalFormat这些属性
3. 他可以知道当前图片是否可以做一个大的cubemap,
4. 他可以将一个cubemap的交叉图像(就是将6面绘制在一张图片上的图像)转变为6个face的CUBEMAP,然后可以单独得到各个面,注意:这个类只可以支持宽高比为3:4的交叉图像,并且宽高为2的幂的3倍和4倍。
5. 他可以将当前数据写入PNG图片文件。