目录
6.传递纹理对象给纹理单元,并调用glDrawElements绘图
纹理是一个2D图片(甚至也有1D和3D的纹理),通俗的说绘制纹理就是用OpenGL绘制图片。
除了图像以外,纹理也可以被用来储存大量的数据,这些数据可以发送到着色器上。
绘制纹理的步骤
1.创建纹理对象
//生成的OpenGL对象一样,纹理也是使用ID引用的
unsigned int texture;
glGenTextures(1, &texture);
//glGenTextures函数首先需要输入生成纹理的数量,然后把它们储存在第二个参数的unsigned int数组中
2.绑定纹理对象
绑定之后任何的纹理指令都可以配置当前绑定的纹理:
glBindTexture(GL_TEXTURE_2D, texture);
3.给纹理对象附加上纹理图像
//通过glTexImage2D在当前纹理对象上生成纹理
//当调用glTexImage2D时,当前绑定的纹理对象就会被附加上纹理图像
QImage image1 = QGLWidget::convertToGLFormat(QImage(":/image/image/container.jpg"));
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, image1.width(), image1.height(), 0, GL_RGBA, GL_UNSIGNED_BYTE, image1.bits());
glGenerateMipmap(GL_TEXTURE_2D);
当调用glTexImage2D时,当前绑定的纹理对象就会被附加上纹理图像。
4.在片段着色器中定义采样器并使用
GLSL有一个供纹理对象使用的内建数据类型,叫做采样器(Sampler),它以纹理类型作为后缀,比如sampler1D
、sampler3D
,或在我们的例子中的sampler2D
。我们可以简单声明一个uniform sampler2D
把一个纹理添加到片段着色器中,稍后我们会把纹理赋值给这个uniform。
#version 330
out vec4 fragColor;
in vec2 TexCord;
uniform sampler2D texture1;
void main()
{
fragColor = texture(texture1, TexCord);
#使用GLSL内建的texture函数来采样纹理的颜色,它第一个参数是纹理采样器,
第二个参数是对应的纹理坐标。texture函数会使用之前设置的纹理参数对相应的颜色值进行采样。
这个片段着色器的输出就是纹理的(插值)纹理坐标上的(过滤后的)颜色。
}
5.使纹理对象和着色器建立关系
纹理对象的纹理数据是不能直接传递给着色器的,纹理对象可以传递给纹理单元。
采样器可以通过glUniform1i来指定该采样器对应的是哪个纹理单元。
sharedprogram.bind();
//设定采样器对应的纹理单元
sharedprogram.setUniformValue("texture1",0);//指定采样器texture1为纹理单元0
//sharedprogram.setUniformValue("texture2",1);//指定采样器texture2为纹理单元1
sharedprogram.release();
6.传递纹理对象给纹理单元,并调用glDrawElements绘图
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texture1);//传递纹理对象到纹理单元
//glActiveTexture(GL_TEXTURE1);
//glBindTexture(GL_TEXTURE_2D, texture2);
glBindVertexArray(VAO);
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
/********************************************************************/
#version 330
out vec4 fragColor;
in vec2 TexCord;
uniform sampler2D texture1;
uniform sampler2D texture2;
void main()
{
fragColor = mix(texture(texture1, TexCord), texture(texture2, TexCord), 0.2) ;
}
/*********************************************************************/
//创建一个纹理对象
glGenTextures(1, &texture_container);
//绑定纹理对象,让之后任何的纹理指令都可以配置当前绑定的纹理
glBindTexture(GL_TEXTURE_2D, texture_container);
//通过glTexImage2D在当前纹理对象上生成纹理
//当调用glTexImage2D时,当前绑定的纹理对象就会被附加上纹理图像
QImage image1 = QGLWidget::convertToGLFormat(QImage(":/image/image/container.jpg"));
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, image1.width(), image1.height(), 0, GL_RGBA, GL_UNSIGNED_BYTE, image1.bits());
glGenerateMipmap(GL_TEXTURE_2D);
glGenTextures(1, &texture_awesomeface);
glBindTexture(GL_TEXTURE_2D, texture_awesomeface);
QImage image = QGLWidget::convertToGLFormat(QImage(":/image/image/awesomeface.png"));
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, image.width(), image.height(), 0, GL_RGBA, GL_UNSIGNED_BYTE, image.bits());
glGenerateMipmap(GL_TEXTURE_2D);
/*********************************************************************/
sharedprogram.bind();
//设定采样器对应的纹理单元
sharedprogram.setUniformValue("texture1",0);//指定采样器texture1为纹理单元0
sharedprogram.setUniformValue("texture2",1);//指定采样器texture2为纹理单元1
sharedprogram.release();
/*********************************************************************/
sharedprogram.bind();
glActiveTexture(GL_TEXTURE0);// 在绑定纹理之前先激活纹理单元
glBindTexture(GL_TEXTURE_2D, texture_container);//传递纹理给纹理单元
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, texture_awesomeface);
glBindVertexArray(VAO);
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
sharedprogram.release();
纹理坐标
纹理坐标在x和y轴上,范围为0到1之间(注意我们使用的是2D纹理图像)。纹理坐标起始于(0, 0),也就是纹理图片的左下角,终始于(1, 1),即纹理图片的右上角。
上图的纹理坐标如下
float texCoords[] = {
0.0f, 0.0f, // 左下角
1.0f, 0.0f, // 右下角
0.5f, 1.0f // 上中
};
纹理单元
一个纹理的位置值通常称为一个纹理单元(Texture Unit)。一个纹理的默认纹理单元是0,它是默认的激活纹理单元,所以教程前面部分我们没有分配一个位置值。
纹理单元的主要目的是让我们在着色器中可以使用多于一个的纹理。通过把纹理单元赋值给采样器,我们可以一次绑定多个纹理,只要我们首先激活对应的纹理单元。就像glBindTexture一样,我们可以使用glActiveTexture激活纹理单元,传入我们需要使用的纹理单元:
glActiveTexture(GL_TEXTURE0); // 在绑定纹理之前先激活纹理单元
glBindTexture(GL_TEXTURE_2D, texture);
激活纹理单元之后,接下来的glBindTexture函数调用会绑定这个纹理到当前激活的纹理单元,纹理单元GL_TEXTURE0默认总是被激活,所以我们在前面的例子里当我们使用glBindTexture
的时候,无需激活任何纹理单元。
OpenGL至少保证有16个纹理单元供你使用,也就是说你可以激活从GL_TEXTURE0到GL_TEXTRUE15。它们都是按顺序定义的,所以我们也可以通过GL_TEXTURE0 + 8的方式获得GL_TEXTURE8,这在当我们需要循环一些纹理单元的时候会很有用。