多个纹理
前一篇是一个简单的纹理应用,在片元着色器中没有为采样器设置值,这是因为只使用一个纹理。实际上,OpenGL同时支持多个纹理, 比如支持每个着色器阶段至少16个纹理,再乘以OpenGL支持的着色器阶段数目,最后结果是80个纹理。实际上,OpenGL有80个纹理单元,名字为GL_TEXTURE0到GL_TEXTURE79。当纹理函数,例如调用glBindTexture()的时候,它操作的是绑定到激活纹理单元的纹理。默认激活的纹理单元是GL_TEXTURE0,但是可以通过glActiveTexture()来改变激活的纹理单元。
为了在着色器中使用多个纹理,需要声明多个uniform采样器变量。每个指向一个不同的纹理单元。在应用程序部分,uniform采样器看起来像uniform整数。使用标准的glGetActiveUniform()函数来遍历,也可以使用函数glUniform1i()来改变它的值。分配给uniform采样器的整数值是它指向纹理单元的索引。
在一个着色器中使用多个纹理的步骤如下:首先,使用glActiveTexture()来选择第一个纹理单元;然后使用glBindTexture()来绑定纹理,为每个纹理单元重复这个过程;最后设置uniform采样器的值为纹理单元的索引,这个通过调用函数glUniform1i()来使用。
示例演示
这里为说明使用多个纹理的步骤,在上一篇的例子来使用两个纹理。
1、使用glActiveTexture()选择纹理单元,然后使用glBindTexture()来绑定纹理。
void MyQGLWidget::loadGLTextures()
{
QImage tex, buf;
if(!buf.load(":/new/img/texture.jpg")){
QImage dummy(128, 128, QImage::Format_RGB32);
dummy.fill(Qt::green);
buf = dummy;
}
tex = QGLWidget::convertToGLFormat(buf);
glGenTextures(1, &texture[0]);
glTexImage2D( GL_TEXTURE_2D, 0, 3, tex.width(), tex.height(), 0, GL_RGBA, GL_UNSIGNED_BYTE, tex.bits() );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texture[0]);
if(!buf.load(":/new/img/lena.jpg")){
QImage dummy(128, 128, QImage::Format_RGB32);
dummy.fill(Qt::green);
buf = dummy;
}
tex = QGLWidget::convertToGLFormat(buf);
glGenTextures(1, &texture[1]);
glTexImage2D( GL_TEXTURE_2D, 0, 3, tex.width(), tex.height(), 0, GL_RGBA, GL_UNSIGNED_BYTE, tex.bits() );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, texture[1]);
}
2、调用函数glUniform1i()设置uniform采样器变量的值为纹理单元的索引
GLint texLoc1 = glGetUniformLocation(programID, "tex1");
glUniform1i(texLoc1, 0); //GL_TEXTURE0
GLint texLoc2 = glGetUniformLocation(programID, "tex2");
glUniform1i(texLoc2, 1); //GL_TEXTURE1
3、我们看下片元着色器代码,增加了两个uniform采样器变量tex1和tex2,颜色从每一个采样器变量读取一个纹素并对它们求和得到。
const char* fragmentShaderCode =
"#version 430 \r \n"
""
"in vec2 myTextureCoord;"
"out vec4 fColor;"
"uniform sampler2D tex1;"
"uniform sampler2D tex2;"
""
"void main()"
"{"
" fColor = texture(tex1, myTextureCoord) + texture(tex2, myTextureCoord);"
"}";
运行结果:
创建和初始化纹理
提到创建和初始化纹理。OpenGL中使用纹理的第一步是为纹理对象保留名称并把它们绑定到环境的纹理单元。为了创建纹理,与OpenGL中许多其他对象类似。保留名称并绑定到合适的目标。为纹理对象保留名称,调用glGenTextures(),设置需要保留名称的数目和保存名称的数组地址。
void glGenTextures(GLsizei n, GLuint* textures);
在数组textures中返回n个用于王文丽对象的当前没有使用的名称。
Textures中返回的名称不一定是整数的连续集合。
纹理对象名称被保留后,它们还没有表示纹理,所以没有维数和类型。纹理对象使用保留名创建,第一次使用glBindTexture()来把它绑定到纹理目标。用于初始绑定的目标决定了创建的纹理类型。从那时起,纹理将只会被绑定到这个目标上,直到销毁它为止。
void glBindTexture(GLenum target, GLuint texture);
glBindTexture做了三件事:
1、第一次使用无符号整数值texture而不是0时,创建一个新的纹理对象并赋给那个名称。
2、当绑定先前创建的纹理对象时,将激活这个纹理对象。
3、当texture为0时,OpenGL将删除与激活的纹理单元特定目标关联的所有的绑定,不留下任何绑定。
OpenGL环境支持多个纹理单元。调用glBindTexture将纹理对象绑定到激活的纹理单元。glActiveTexture选择当前活跃的纹理单元。一个纹理可以同时绑定到多个纹理单元。这使相同的纹理数据通过表示绑定纹理的纹理单元的不同采样器来读取。