本文主要介绍OpenGL中两种技术的使用方法:多重纹理技术和纹理组合器技术,最终根据参考【2】中的代码,实现了两个简单的演示DEMO,其中使用到了《八叉树颜色量化、BMP、TGA文件解析》篇章中提供的图像解析类。
下载地址:https://github.com/twinklingstar20/twinklingstar_cn_demo_multitexture_texture_combiner/
一.多重纹理技术(Multitexture)
1.1 多重纹理技术简介
OpenGL在渲染多边形时允许多张纹理同时被应用,这些纹理在操作管线上一个接一个的被处理。一个纹理单元(texture unit)表示单个纹理的操作,一个纹理单元处理完成后,将它的结果传递给下一个纹理单元,直至所有的纹理单元都被处理完。下图显示了片断(fragment)经历的四个纹理操作过程。
图1.片断经历的四个纹理过程
注意:在反馈模式下,除了第一个纹理单元外多重纹理是处于未定义的状态,即只有第一张纹理单元能用(In feedback mode, multitexturing is undefined beyond the first texture unit)。
1.2 使用多重纹理的几个步骤
(1) 建立每个纹理单元的纹理状态,包括纹理图像数据,过滤器,环境,矩阵,纹理函数生成方式等。可用glActiveTexture()函数以改变当前的纹理状态,然后调用glTexImage*(),glTexParameter*(),glTexEnv*(),glTexGen*(),glBindTexture()方法分配每个纹理单元的纹理信息,纹理状态、纹理坐标和光栅化纹理坐标的查询都应用在当前纹理单上,即glActiveTexture()指定的纹理单元。此外,可以使用glGetIntegerv(GL_MAX_TEXTURE_UNITS,…)显示当前实现中允许的最大纹理单元数,得到的最大纹理单元数至少为2。
注意:glPushAttrib(),glPushClientAttrib(),glPopAttrib(),或者glPopClientAttrib()可以保存或者恢复所有纹理单元的纹理状态,但是纹理矩阵堆栈除外。
(2) 可以用glMultiTexCoord*()函数规定每个顶点上不同纹理单元的纹理坐标。下面的代码片段演示该函数的用法:
1 2 3 4 5 6 7 8 9 10 11 | glBegin(GL_TRIANGLES); glMultiTexCoord2f(GL_TEXTURE0,0.0f,0.0f); glMultiTexCoord2f(GL_TEXTURE1,1.0f,0.0f); glVertex2f(0.0f,0.0f); glMultiTexCoord2f(GL_TEXTURE0,0.5f,1.0f); glMultiTexCoord2f(GL_TEXTURE1,0.5f,0.0f); glVertex2f(50.0f,100.0f); glMultiTexCoord2f(GL_TEXTURE0,1.0f,0.0f); glMultiTexCoord2f(GL_TEXTURE1,1.0f,1.0f); glVertex2f(100.0f,0.0f); glEnd(); |
注意:如果使用glTexCoord*()相当于采用了第一个纹理单元的纹理坐标,即与glMultiTexCoord*(GL_TEXTURE0,…)等价。
(3) 恢复使用单个纹理。在使用多重纹理时,如果想要恢复使用单个纹理,需要禁用除纹理单位0之外的所有纹理单位。如下面的代码片段所示:
1 2 3 4 5 | glActiveTexture(GL_TEXTURE1); glDisable(GL_TEXTURE_2D); glActiveTexture(GL_TEXTURE2); glDisable(GL_TEXTURE_2D); glActiveTexture(GL_TEXTURE0); |
附加说明:
(1)由于微软的VS平台上,只支持OpenGL 1.1版本,所以无法使用glActiveTexture()等函数,所以需要安装上glew库,这个库的安装方法网上很容易搜索到;
(2)将多重纹理技术与组合器函数glTexEnv*()结合使用,可以产生很绚丽的效果,下面介绍纹理组器函数;
(3)multitexture-demo演示了一个多重纹理的简单例子,使用到纹理组合器。
二.纹理组合器(Texture combiner)
2.1 纹理组合器函数简介
OpenGL除了多重纹理外,还提供了一些灵活的纹理组合器函数,允许程序员对片断与纹理值或者其它颜色的混合,对纹理提供了更加精细的控制。这就使得OpenGL的重心从原先的顶点处理(变换、裁剪)过渡到光栅化和片断操作,片断的处理能力越来越强。纹理组合器函数glTexEnv*()的参数比较多,下面会逐步进行介绍。
2.2 glTexEnv*()函数介绍
void glTexEnv{if}(GLenum target,GLenum pname,TYPE param);
void glTexEnv{if}v(GLenum target,GLenum pname,TYPE* param);
target规定纹理环境(texture environment),必需是GL_TEXTURE_ENV,GL_TEXTURE_FILTER_CONTROL,GL_POINT_SPRITE中的任意一个值。对target和pname对应参数进行总结,如表1所示;对pname和param对应参数进行总结,如表2所示。
表1. 参数target和参数pname的对应取值
表2. 参数pname和参数param的对应取值
2.2.1 target的值是GL_TEXTURE_FILTER_CONTROL
如果target是GL_TEXTURE_FILTER_CONTROL时,pname必须是GL_TEXTURE_LOD_BIAS,则param规定了纹理细节层的偏移量,它的用途可以参见文章《基本的二维纹理对象的使用方法介绍》中3.1小节的介绍。
2.2.2 target的值是GL_POINT_SPRITE
如果target是GL_POINT_SPRITE 时,pname必须是GL_COORD_REPLACE,boolean值用于规定是否开启点精灵纹理坐标替换(point sprite texture coordinate replacement)。
2.2.3 target的值是GL_TEXTURE_ENV
如果target是GL_TEXTURE_ENV,pname必须是(1)GL_TEXTURE_ENV_MODE, GL_TEXTURE_ENV_COLOR,(3)GL_COMBINE_RGB,GL_COMBINE_ALPHA,(4)GL_RGB_SCALE,(5)GL_ALPHA_SCALE,(6)GL_SRC0_RGB, GL_SRC1_RGB,(7)GL_SRC2_RGB,(8)GL_SRC0_ALPHA,(9)GL_SRC1_ALPHA,(10)GL_SRC2_ALPHA。
在不同的纹理存储格式下,用于纹理计算的R、G、B、A4个颜色分量的取值。
表3.进行组合操作的源纹理(不同的内部存储格式下)的Cs、As值
C代表一个三元的颜色值(RGB),A是相关的alpha值,从纹理图像中提取的RGBA的值都在范围[0,1]内,下标是p的代表由上一个阶段计算得到的纹理颜色(处理阶段为0的纹理颜色就是起始的原片断颜色),下标是s的代表原纹理颜色(Texture Source Color),下标是c的代表纹理环境的颜色(Texture Environment Color),下标是v的代表通过纹理函数计算后得到的纹理颜色。
表4. 当pname是GL_TEXTURE_ENV_MODE时,几种纹理处理函数的计算公式
如果pname是GL_TEXTURE_ENV_MODE,params是GL_COMBINE,则纹理函数的计算依赖GL_COMBINE_RGB和GL_COMBINE_ALPHA。
接下来描述的是由GL_SRC0_RGB、GL_SRC1_RGB、GL_SRC2_RGB、GL_SRC0_ALPHA、GL_SRC1_ALPHA、GL_SRC2_ALPHA规定的源纹理,通过组合,生成最后的纹理颜色的方法,在下面的表中GL_SRC0_c表示Arg0,GL_SRC1_c表示Arg1,GL_SRC2_c表示Arg2。
当pname是GL_COMBINE_RGB时,param可以接受的几个参数是GL_REPLACE、GL_MODULATE、GL_ADD、GL_ADD_SIGNED、GL_INTERPOLATE、GL_SUBTRACT、GL_DOT3_RGB、GL_DOT3_RGBA。
表5. 规定几种源纹理,通过对这几种纹理的RGB颜色计算,得到最终的纹理颜色
类似的,当pname是GL_COMBINE_ALPHA时,param可以接受的几个参数是GL_REPLACE、GL_MODULATE、GL_ADD、GL_ADD_SIGNED、GL_INTERPOLATE、GL_SUBTRACT。
表6.规定几种源纹理,通过对这几种纹理的alpha颜色计算,得到最终的纹理颜色
下面的表格中Cs表示当前绑定的纹理采样的颜色,Cc表示环境纹理颜色常量(Constant Texture-Environment Color),Cf表示传入片断的主颜色,Cp表示以前的纹理阶段计算出的颜色(如果当前正处理的纹理阶段是0,则它的值为Cf),类似的,As,Ac,Af,Ap表示对应纹理的alpha值。下表是基于源纹理的RGB值和操作,计算得到的Arg0,Arg1,Arg2。
表7. 基于源纹理的RGB值和操作,计算得到的Arg0,Arg1,Arg2
类似的,下表是基于源纹理的Alpha值和操作,计算得到的Arg0,Arg1,Arg2。
表8. 基于源纹理的Alpha值和操作,计算得到的Arg0,Arg1,Arg2
2.3 glTexEnv*()函数使用例子
选择激活纹理组合方法:
1 | glTexEnvf(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE,GL_COMBINE); |
选择纹理组合的运算法则:
1 | glTexEnvf(GL_TEXTURE_ENV,GL_COMBINE_RGB,GL_SUBTRACT); |
设置纹理组合函数中纹理的来源:
1 2 | glTexEnvf(GL_TEXTURE_ENV,GL_SRC0_RGB,GL_TEXTURE); glTexEnvf(GL_TEXTURE_ENV,GL_SRC1_RGB,GL_PREVIOUS); |
设置如何使用纹理组合函数中纹理的来源来计算Arg0和Arg1,如表7所示:
1 2 | glTexEnvf(GL_TEXTURE_ENV,GL_OPERAND0_RGB,GL_SRC_COLOR); glTexEnvf(GL_TEXTURE_ENV,GL_OPERAND1_RGB,GL_SRC_COLOR); |
实现了multitexture-combine-demo这个demo,用组合器生成多重纹理的代码片段如下所示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 | void initTexture() { unsigned int textureId[2]; //创建2个纹理 //生成一个2*2,颜色为(0,0,255)的纹理图片 textureId[0] = genTexture(0,0,255); //读取一个BMP图片,生成纹理 textureId[1] = genTextureFile( "text.bmp" ); //激活第1个纹理 glActiveTexture(GL_TEXTURE0); glEnable(GL_TEXTURE_2D); glBindTexture(GL_TEXTURE_2D,textureId[0]); //用纹理textureId[0]替换环境纹理 glTexEnvi(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE,GL_REPLACE); glActiveTexture(GL_TEXTURE1); glEnable(GL_TEXTURE_2D); glBindTexture(GL_TEXTURE_2D,textureId[1]); //设置组合器,运算法则是“减法”, //textureId[1]是纹理GL_SRC0_RGB,前一个阶段计算的纹理是GL_SRC1_RGB, //根据运算法则GL_SRC0_RGB-GL_SRC1_RGB,字体中间是白色的(255,255,255),减去(0,0,255),会显示出黄色; //字体周围是黑色的,减去(0,0,255)还是黑色的。 glTexEnvf(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE,GL_COMBINE); glTexEnvf(GL_TEXTURE_ENV,GL_COMBINE_RGB,GL_SUBTRACT); glTexEnvf(GL_TEXTURE_ENV,GL_SRC0_RGB,GL_TEXTURE); glTexEnvf(GL_TEXTURE_ENV,GL_OPERAND0_RGB,GL_SRC_COLOR); glTexEnvf(GL_TEXTURE_ENV,GL_SRC1_RGB,GL_PREVIOUS); glTexEnvf(GL_TEXTURE_ENV,GL_OPERAND1_RGB,GL_SRC_COLOR); } void drawScene() { glBegin(GL_QUADS); //分别指定三个纹理的纹理坐标 glMultiTexCoord2f(GL_TEXTURE0,0.0f, 0.0f);glMultiTexCoord2f(GL_TEXTURE1,0.0f, 0.0f); glVertex3f(-1.0f, -1.0f, 1.0f); glMultiTexCoord2f(GL_TEXTURE0,1.0f, 0.0f);glMultiTexCoord2f(GL_TEXTURE1,1.0f, 0.0f); glVertex3f( 1.0f, -1.0f, 1.0f); glMultiTexCoord2f(GL_TEXTURE0,1.0f, 1.0f);glMultiTexCoord2f(GL_TEXTURE1,1.0f, 1.0f); glVertex3f( 1.0f, 1.0f, 1.0f); glMultiTexCoord2f(GL_TEXTURE0,0.0f, 1.0f);glMultiTexCoord2f(GL_TEXTURE1,0.0f, 1.0f); glVertex3f(-1.0f, 1.0f, 1.0f); glEnd(); } |
该DEMO的组合效果如下图所示:
图2. 通过设置组合器,得到最终的结果
至此,演示了组合器的基本用法,可以通过概念组合器不同的参数,得到不同的结果。