1、本节要实现的内容
前面说过纹理贴图能够大幅提升游戏画面质量,但纹理贴图是没有叠加的。在一些游戏场景中,要求将非常不同的多个纹理(如泥泞的褐色地面、绿草植密布的地面、碎石遍布的地面)叠加(混合)起来显示,实现纹理间能够自然过渡,产生看不出明显的边缘的连续场景,由此多纹理混合技术应运而生,在地形渲染中用得非常广泛。例如,你可以用3个纹理来渲染一片庄园的地面,有的地方长满了绿草、有的地方裸露着泥土、而有的地方还存在砂石,各个场景之间没有明显的过渡痕迹,带来更加逼真的环境效果。
2、需要GLEW库并完成初始化
我们这里主要介绍立即显示模式下纹理融合(着色器方法后续再讲),就要使用GLEW库。GLEW(OpenGL Extension Wrangler)是一个用于管理OpenGL扩展的库,它能够自动识别并加载支持的高级扩展函数。 http://glew.sourceforge.net/里有GLEW库的源码和链接库下载。源文件中需要包含头文件glew.h,并加入库glew32.lib,同时程序运行时需要glew32.dll支持,这3个文件我们之前的运行库中都已包含。文件的放置可以参照C++和OpenGL实现3D游戏编程【连载1】——初探3D世界第5部分“准备工作”设置。
如果要使用GLEW相关的函数,那么一定要先对GLEW初始化。如果不初始化,那么虽然编译相关的程序不会报错,但是在运行时,则会报告错误,或者程序直接无法运行。
- GLEW库文件的引用
头文件glew.h和库glew32.lib的引用可以直接在源文件中添加,引用glew.h文件必须放在最前边,否则会提示错误,大致意思加载顺序错误。
#include "gl/glew.h"
#pragma comment(lib,"glew32.lib")
- GLEW的初始化
初始化的位置问题,初始化函数glewInit()必须放置在SetPixelFormat(hDC)函数之后,否则系统无法运行。如果放在它们前面,就会出现"Missing GL Version"错误。关于这个错误,说明是需要一个OpenGL的环境,可能的原因就是初始化位置过早,OpenGL的环境没有完全建立。
case WM_CREATE:
hDC=GetDC(hWnd);
SetPixelFormat(hDC);
ReleaseDC(hWnd,hDC);
......
//初始化GLEW,初始化必须放置在SetPixelFormat(hDC)函数之后,否则系统无法运行
glewInit();
......
return 0;
初始化glewInit()之后,我们就可以开始着手多重纹理的融合操作了。
3、对纹理单元操作注意事项(glActiveTexture)
启用多重纹理前,我们就必须使用glActiveTexture()来设置多个纹理单元,调用扩展提供的函数glActiveTexture()来指定当前操作的是哪一个纹理单元(GL_TEXTURE0、GL_TEXTURE1这些叫纹理单元),在下一次调用glActiveTexture之前所有的函数适用于当前选定的纹理单元。
glActiveTexture的Active不能理解为激活和使发生作!!!应理解为选择(Select)某纹理单元(TextureUnit)。
比如,你在glActiveTexture(GL_TEXTURE0)后,即表示后续的glEnable(GL_TEXTURE_2D)操作和glBindTexture操作都是针对纹理单元GL_TEXTURE0的,同样后续glDisable(GL_TEXTURE_2D)也是针对GL_TEXTURE0的。实例如下:
glActiveTexture(GL_TEXTURE0); // 选择TEXTURE0为当前纹理单元,后续将对TEXTURE0纹理单元进行设置
glEnable(GL_TEXTURE_2D); // 激活TEXTURE0单元的纹理
glBindTexture(GL_TEXTURE_2D, texture_box0); // 为TEXTURE0单元绑定texture_box0纹理图像
glActiveTexture(GL_TEXTURE1); // 选择TEXTURE1为当前纹理单元,后续将对TEXTURE1纹理单元进行设置
glEnable(GL_TEXTURE_2D); // 激活TEXTURE1单元的纹理
glBindTexture(GL_TEXTURE_2D, texture_box1); // 为TEXTURE1单元绑定texture_box1纹理图像
......
glActiveTexture(GL_TEXTURE1); // 选择TEXTURE1为当前纹理单元,后续将对TEXTURE1纹理单元进行设置
glDisable(GL_TEXTURE_2D); // 关闭禁用TEXTURE1单元的纹理
glActiveTexture(GL_TEXTURE0); // 选择TEXTURE0为当前纹理单元,后续将对TEXTURE0纹理单元进行设置
glDisable(GL_TEXTURE_2D); // 关闭禁用TEXTURE0单元的纹理
理解这一点很重要,否则后续很难理解相关操作。
4、简单的纹理融合
一般情况下,我们可以简单的为每个纹理单元设置一个纹理环境模式 (GL_REPLACE,GL_DECAL,GL_ADD 和 GL_MODULATE),把当前纹理单元(如GL_TEXTURE1)中的纹理与上一个纹理单元中的纹理进行相应的操作,比如GL_REPLACE就是当前纹理单元中的纹理替换此前纹理单元的纹理,GL_MODULATE就是将当前纹理单元(如GL_TEXTURE1)的纹理颜色与上一个纹理单元(如GL_TEXTURE0)的纹理颜色(确切的说应该是纹理单元GL_TEXTURE0渲染后的最终结果颜色)相乘。通常不指定纹理环境模式的情况下,默认为GL_MODULATE方式。
比如我们想对以下两张纹理进行融合:
//绑定纹理资源
glActiveTexture(GL_TEXTURE0);
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D,tex_Box);
//设置贴图方式为替换,就是现在颜色与原来颜色怎么操作
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE,GL_REPLACE);
//绑定纹理资源
glActiveTexture(GL_TEXTURE1);
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D,tex_Earth);
//设置贴图方式为调整,地球纹理和木箱纹理原本色彩叠加,采用相乘的方式
glTexEnvi(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE,GL_MODULATE);
//设置大小
float size=3;
//显示木箱及笑脸
glBegin(GL_QUADS);
//四边形的各个顶点
glMultiTexCoord2f(GL_TEXTURE0,0,0);
glMultiTexCoord2f(GL_TEXTURE1,0,0);
glMultiTexCoord2f(GL_TEXTURE2,0,0);
glVertex3f(-size,-size,0);
//四边形的各个顶点
glMultiTexCoord2f(GL_TEXTURE0,1,0);
glMultiTexCoord2f(GL_TEXTURE1,1,0);
glMultiTexCoord2f(GL_TEXTURE2,1,0);
glVertex3f(size,-size,0);
//四边形的各个顶点
glMultiTexCoord2f(GL_TEXTURE0,1,1);
glMultiTexCoord2f(GL_TEXTURE1,1,1);
glVertex3f(size,size,0);
//四边形的各个顶点
glMultiTexCoord2f(GL_TEXTURE0,0,1);
glMultiTexCoord2f(GL_TEXTURE1,0,1);
glVertex3f(-size,size,0);
glEnd();
//关闭两重纹理
glActiveTexture(GL_TEXTURE1);
glDisable(GL_TEXTURE_2D);
glActiveTexture(GL_TEXTURE0);
glDisable(GL_TEXTURE_2D);
以上操作就会将两个纹理融合成下图中间部位的图像。在GL_MODULATE模式下,程序将当前纹理单元(GL_TEXTURE1)的纹理颜色(木箱)与上一个纹理单元(GL_TEXTURE0)的纹理颜色(地球)相乘。其中,地球的黑色部分vec3(0.0f,0.0f,0.0f)和木箱对应位置的纹理颜色vec3(a,b,c)相乘,那么不管vec3(a,b,c)是什么颜色,则最终的效果都为黑色,这就是混合结构中外围边框为黑色的原因。地球的白色部分vec3(1.0f,1.0f,1.0f)和木箱对应位置的纹理颜色vec3(a,b,c)相乘,那么不管vec3(a,b,c)为什么颜色与vec3(1.0f,1.0f,1.0f)对应分量相乘,则最终的效果仍为vec3(a,b,c)颜色,这就是混合结构中地球原理白色部分能够显示木箱原本颜色的原因。
同样的道理,由于我们草绿色五边形的颜色为RGB(181,230,29),对应的向量vec3(0.70f,0.89f,0.11f)与木箱的纹理颜色vec3(a,b,c)相乘,则最终的颜色呈现为vec3(a * 0.7f,b * 0.89f,c * 0.11f)颜色。
以下是另一个采用GL_MODULATE进行纹理融合后的样子:
glTexEnvi(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE,GL_MODULATE);
5、纹理组合器(自定义纹理融合)
OpenGL 的纹理组合器可以控制多重纹理的片段是如何组合的,比如自定义两个纹理的显示权重,给程序员更多自定义融合的选择。纹理组合器提供了一个新的纹理环境 GL_COMBINE允许我们控制每一个纹理单元的纹理片段是如何组合的,达到自定义生成多种不同的纹理组合方式的效果。
- 第一步, 启用文理融合模式
使用纹理组合器模式首先添加如下代码:
glTexEnvi(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE,GL_COMBINE);
- 第二步,赋值纹理融合函数参数
纹理组合器是通过 glTexEnv函数来确定具体的控制方式的。我们需要设置使用哪个纹理组合器的函数。glTexEnv函数的第二个参数是组合器函数的选择器,可以是 GL_COMBINE_RGB 或 GL_COMBINE_ALPHA。第三个参数是你想使用的纹理环境函数常量,每一种函数常量都对应一种纹理混合计算的方式,比如GL_REPLACE为直接替换;GL_ADD为加; GL_MODULATE为乘;GL_SUBTRACT为减;GL_INTERPOLATE为线性插值。比如,我们选择直接替换,则选用GL_REPLACE如下:
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_REPLACE);
再比如 ,例如你给 RGB 值选择 GL_INTERPOLATE(线性插值) 组合器,你的函数调用如下:
glTexEnvi(GL_TEXTURE_ENV,GL_COMBINE_RGB, GL_INTERPOLATE);
第三个参数和对应的公式如下表:
如果我们选择 GL_INTERPOLATE 方式,是指采用线性插值方式,它的公式是这样的:
C = Arg0 * Arg2 + Arg1 * (1-Arg2);
翻译成代数的是这样:
C = a * ω + b * (1-ω) ;
这下大家可以清楚的看到它是一个线性插值表达式了。其中: Arg0 是第一个操作数,Arg1是第二个操作数,Arg2 是线性插值的系数就是那个ω了。
上表中具体的计算函数中参数Arg0、Arg1、Arg2 需要通过更多的 glTexEnv 调用来设置。通过glTexEnv函数以及参数GL_SOURCEx_RGB 和 GL_SOURCEx_ALPHA 来指定 RGB 和 ALPHA 组合器函数的参数Argx(x 可以是 0、1 或者 2),这些来源的值如下表:
- 第三步,施加额外的控制
我们还可以对给定的源值施加额外的控制,说明来自纹理单元中的什么成分或什么部分。设置这些操作数,我们可以通过glTexEnv函数以及参数 GL_OPERANDx_RGB 或者 GL_OPERANDx_ALPHA来赋值参数,根据角标与Argx(x 可以是 0、1 或者 2)一一对应,可以给这些操作数赋予的值如下表:
好了,说了那么多你可能已经没有耐心了。我们还是来实战一下,看看具体是怎么操作的,已经添加了详细的说明,应该好理解一些。我们刚刚说过,具体的计算函数中参数Arg0、Arg1、Arg2 需要通过更多的 glTexEnv 调用来设置,我们通过下面GL_ADD方式的例子来说明参数Arg0、Arg1的赋值过程,例如:
//设置使用纹理组合器模式
glTexEnvi(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE,GL_COMBINE);
//设置当前为GL_ADD的模式,其对应表中的公式为Arg0 + Arg1,那么Arg0和Arg1值是多少呢?继续下一步
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_ADD);
//设置当前公式中Arg0的值为GL_TEXTURE0的纹理颜色。GL_SOURCE0_RGB与Arg0角标0一一对应
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_TEXTURE0);
//设置当前公式中Arg0的额外的控制参数GL_SRC_COLOR。GL_SOURCE0_RGB与Arg0角标0一一对应
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR);
//设置当前公式中Arg1的值为GL_TEXTURE1的纹理颜色。GL_SOURCE1_RGB与Arg1角标1一一对应
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB, GL_TEXTURE1);
//设置当前公式中Arg1的额外的控制参数GL_SRC_COLOR。GL_SOURCE1_RGB与Arg1角标1一一对应
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR);
通过以上设置,由于已经在两个纹理单元中加载了两个纹理,你在应用纹理融合时,实际的效果就是把两个纹理的颜色值相加,通过纹理组合器达到简单的纹理融合。
6、线性纹理融合(两重纹理融合)
通俗一点来说呢, 如果我们要进行一次多重渲染就是要把两个纹理进行一次数学运算把得到的结果投放到屏幕,我们现在需要实现一个木箱和太阳纹理的纹理融合效果,其中以木箱80%显示效果,太阳20%的显示效果融合。这里融合模式设置为INTERPOLATE 是指线性插值的意思,它的公式是这样的:
C = Arg0 * Arg2 + Arg1 * (1-Arg2);
我们现在需要设置Arg0为GL_TEXTURE0纹理单元对应的tex_Box的纹理颜色,Arg1为GL_TEXTURE1纹理单元对应的tex_Sun的纹理颜色,Arg2设置为线性差值系数0.8,那我们看下面的实例是怎样做到的,详见代码注释。
//线性插值系数,纹理显示的权重,用于赋值Arg2值
float tempBlendValue=0.8;
//绑定纹理资源
glActiveTexture(GL_TEXTURE0);
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D,tex_Box);
//绑定纹理资源
glActiveTexture(GL_TEXTURE1);
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D,tex_Sun);
//指定两重纹理贴图混合的方式
glTexEnvf(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE,GL_COMBINE);
//设置线性插值函数,对应公式为Arg0 * Arg2 + Arg1 * (1-Arg2),包含3个参数,需要逐个设定赋值
glTexEnvi(GL_TEXTURE_ENV,GL_COMBINE_RGB,GL_INTERPOLATE);
//设置当前公式中Arg0的值为GL_TEXTURE0的纹理颜色。GL_SOURCE0_RGB与Arg0角标0一一对应
glTexEnvi(GL_TEXTURE_ENV,GL_SOURCE0_RGB,GL_TEXTURE0);
glTexEnvi(GL_TEXTURE_ENV,GL_OPERAND0_RGB,GL_SRC_COLOR);
//设置当前公式中Arg1的值为GL_TEXTURE1的纹理颜色。GL_SOURCE1_RGB与Arg1角标1一一对应
glTexEnvi(GL_TEXTURE_ENV,GL_SOURCE1_RGB,GL_TEXTURE1);
glTexEnvi(GL_TEXTURE_ENV,GL_OPERAND1_RGB,GL_SRC_COLOR);
//设置当前公式中Arg2的值,设置线性差值的系数,GL_CONSTANT表示数值由后续GL_TEXTURE_ENV_COLOR常量设定。GL_SOURCE2_RGB与Arg2角标2一一对应
glTexEnvi(GL_TEXTURE_ENV,GL_SOURCE2_RGB,GL_CONSTANT);
glTexEnvi(GL_TEXTURE_ENV,GL_OPERAND2_RGB,GL_SRC_COLOR);
//自定义两重纹理的混合比例数组,调整线性差值变量
float tempBlendArray[4]={tempBlendValue,tempBlendValue,tempBlendValue,tempBlendValue};
//设置线性差值的系数常量,设置Arg2的值为tempBlendArray数组值
glTexEnvfv(GL_TEXTURE_ENV,GL_TEXTURE_ENV_COLOR,tempBlendArray);
//设置大小
float size=3;
//显示木箱及笑脸
glBegin(GL_QUADS);
//四边形的各个顶点
glMultiTexCoord2f(GL_TEXTURE0,0,0);
glMultiTexCoord2f(GL_TEXTURE1,0,0);
glVertex3f(-size,-size,0);
//四边形的各个顶点
glMultiTexCoord2f(GL_TEXTURE0,1,0);
glMultiTexCoord2f(GL_TEXTURE1,1,0);
glVertex3f(size,-size,0);
//四边形的各个顶点
glMultiTexCoord2f(GL_TEXTURE0,1,1);
glMultiTexCoord2f(GL_TEXTURE1,1,1);
glVertex3f(size,size,0);
//四边形的各个顶点
glMultiTexCoord2f(GL_TEXTURE0,0,1);
glMultiTexCoord2f(GL_TEXTURE1,0,1);
glVertex3f(-size,size,0);
glEnd();
//关闭两重纹理
glActiveTexture(GL_TEXTURE1);
glDisable(GL_TEXTURE_2D);
glActiveTexture(GL_TEXTURE0);
glDisable(GL_TEXTURE_2D);
线性差值的显示效果如下:
当然,在你不断改变参数tempBlendValue(这就是线性插值公式里的那个系数ω了)的值时,两个纹理的显示权重会不断改变,产生两个纹理不同的显示程度。
7、三重纹理融合
在很多情况下,我们不仅仅需要对两个纹理进行纹理融合,还需要更多的多重纹理,比如不同地面的交界处不仅仅需要两重纹理的融合,三地交接处必然会存在三重纹理的融合操作,那么三重纹理的融合操作怎样实现和操作呢?就比如我们现在需要将以下三个纹理(木箱、花朵、笑脸)进行三维纹理融合。
其实三维纹理的融合和二维纹理的融合差不多,只不过需要继续进行代数运算,以及如何确定各个纹理直接的显示权重,由与系统只有二重线性差值函数GL_INTERPOLATE,我们不得不依靠GL_INTERPOLATE的函数多次重复使用来实现三重线性插值,这里我们看一个实例来说明三重纹理的融合实现和效果。
//预设各个纹理的显示权重,预设显示权重可能和最终的显示权重不一致,比如我们这个例子最总的显示权重应为最后计算出来的0.25*t2 + 0.1875*t1 + 0.2813*t0,也就是说木箱为0.2813,花朵为0.1875,笑脸为0.25,他们最后加起来不到1.0f。
float tex_0Blend = 0.50f;
float tex_1Blend = 0.25f;
float tex_2Blend = 0.25f;
float arr0[4] = {tex_0Blend, tex_0Blend, tex_0Blend, tex_0Blend};
float arr1[4] = {tex_1Blend, tex_1Blend, tex_1Blend, tex_1Blend};
float arr2[4] = {tex_2Blend, tex_2Blend, tex_2Blend, tex_2Blend};
//rn 为混合器n的返回值
//tn 为第n单元的纹理值
//对纹理单元GL_TEXTURE0的操作
glActiveTextureARB(GL_TEXTURE0);
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, tex_Box);
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
//设置GL_MODULATE函数,对应公式为Arg0 * Arg1,包含2个参数,需要逐个设定赋值
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_MODULATE);
//参数Arg0设置
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_CONSTANT);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR);
//参数Arg1设置
glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, arr0);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB, GL_TEXTURE);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR);
//以上为纹理单元0 所作的操作,其公式是:
//r0 = 0.5*t0
//对纹理单元GL_TEXTURE1的操作
glActiveTextureARB(GL_TEXTURE1);
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, tex_Flower);
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
//设置线性插值函数,对应公式为Arg0 * Arg2 + Arg1 * (1-Arg2),包含3个参数,需要逐个设定赋值
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_INTERPOLATE);
//参数Arg0设置
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_TEXTURE);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR);
//参数Arg1设置
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB, GL_PREVIOUS);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR);
//参数Arg2设置
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE2_RGB, GL_CONSTANT);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND2_RGB, GL_SRC_COLOR);
glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, arr1);
//对应的公式是
//r1 = (0.25*t1) + ((1-0.25)*r0) = 0.25*t1 + 0.375*t0
//对纹理单元GL_TEXTURE2的操作
glActiveTextureARB(GL_TEXTURE2);
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, tex_Face);
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
//设置线性插值函数,对应公式为Arg0 * Arg2 + Arg1 * (1-Arg2),包含3个参数,需要逐个设定赋值
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_INTERPOLATE);
//参数Arg0设置
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_TEXTURE);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR);
//参数Arg1设置
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB, GL_PREVIOUS);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR);
//参数Arg2设置
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE2_RGB, GL_CONSTANT);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND2_RGB, GL_SRC_COLOR);
glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, arr2);
//对应的公式是:
//r2 = (0.25*t2) + ((1-0.25)*r1) = 0.25*t2 + 0.1875*t1 + 0.2813*t0
//设置显示矩形大小
float size=3;
//显示木箱及笑脸
glBegin(GL_QUADS);
//四边形的各个顶点
glMultiTexCoord2f(GL_TEXTURE0,0,0);
glMultiTexCoord2f(GL_TEXTURE1,0,0);
glMultiTexCoord2f(GL_TEXTURE2,0,0);
glVertex3f(-size,-size,0);
//四边形的各个顶点
glMultiTexCoord2f(GL_TEXTURE0,1,0);
glMultiTexCoord2f(GL_TEXTURE1,1,0);
glMultiTexCoord2f(GL_TEXTURE2,1,0);
glVertex3f(size,-size,0);
//四边形的各个顶点
glMultiTexCoord2f(GL_TEXTURE0,1,1);
glMultiTexCoord2f(GL_TEXTURE1,1,1);
glMultiTexCoord2f(GL_TEXTURE2,1,1);
glVertex3f(size,size,0);
//四边形的各个顶点
glMultiTexCoord2f(GL_TEXTURE0,0,1);
glMultiTexCoord2f(GL_TEXTURE1,0,1);
glMultiTexCoord2f(GL_TEXTURE2,0,1);
glVertex3f(-size,size,0);
glEnd();
//关闭两重纹理
glActiveTexture(GL_TEXTURE2);
glDisable(GL_TEXTURE_2D);
glActiveTexture(GL_TEXTURE1);
glDisable(GL_TEXTURE_2D);
glActiveTexture(GL_TEXTURE0);
glDisable(GL_TEXTURE_2D);
//设置贴图方式为替换,由于状态机原理,别忘了还原成初始状态,否则会影响后续其他纹理贴图
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE,GL_REPLACE);
根据初始预设各个纹理的显示权重,我们最终显示出了三个纹理的重合图样。但发现这种方法预设显示权重可能和最终的显示权重不一致,比如我们这个例子初始显示权重分别为木箱0.50,花朵0.25,笑脸0.25。最终的显示权重为最后计算出来的0.25t2 + 0.1875t1 + 0.2813*t0,也就是说木箱为0.2813,花朵为0.1875,笑脸为0.25。
8、获取系统支持最大纹理融合数量
获取系统支持的最大纹理单元数量,可以用以下命令:
GLint maxTextureUnits;
glGetIntegerv(GL_MAX_TEXTURE_UNITS, &maxTextureUnits);
实际测试发现GL_MAX_TEXTURE_UNITS总是返回4,后来发现这个属性在OpenGL3中是废弃了,应该使用GL_MAX_TEXTURE_IMAGE_UNITS,也就是:
GLint maxTextureUnits;
glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &maxTextureUnits);
9、小结
这节课详细解释说明了纹理重合的实现方式和用法, 这种技术使用起来相对比较复杂,同时,由于OpenGL自带的混合算法接口存在一定局限性,除了上述介绍的内容就无法进行更多自定义扩展了,因此开发人员也可以在着色器中完成混合,后续的文章中将以例子的形式介绍在着色器中使用纹理混合。但很多时候,为了兼顾旧显卡,这种技术还是很常用的。