为了完成纹理映射,需要为图像的每个顶点指定一个纹理坐标([0,1]),来表明从纹理图像的哪个位置采样。其余部分通过插值得到。
使用纹理坐标获取纹理颜色的过程叫采样(Sampling)。
纹理也可以理解成颜色,采样纹理就是获取那个位置的颜色。
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_MIRRORED_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_MIRRORED_REPEAT);
不太理解
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
// texture是纹理的ID,当前绑定到这个ID上,通过ID就可以访问纹理数据,使用纹理
unsigned int texture;
glGenTextures(1, &texture);
glBindTexture(GL_TEXTURE_2D, texture);
// 为当前绑定的纹理对象设置环绕、过滤方式
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
// 加载并生成纹理
int width, height, nrChannels;
unsigned char *data = stbi_load("container.jpg", &width, &height, &nrChannels, 0);
if (data)
{
//生成
//当调用glTexImage2D时,当前绑定的纹理对象就会被附加上纹理图像。
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data);
//多级渐远纹理
glGenerateMipmap(GL_TEXTURE_2D);
}
else
{
std::cout << "Failed to load texture" << std::endl;
}
stbi_image_free(data);
vertex shader输出纹理坐标TexCoord, fragment shader接受这个变量;
GLSL有一个供纹理对象使用的内建数据类型sampler(采样器),片段着色器通过采样器获取纹理对象。
我们在片段着色器中添加一个uniform sampler2D变量,稍后把纹理对象赋值给它。
//fs
#version 330 core
out vec4 FragColor;
in vec3 ourColor;
in vec2 TexCoord;
uniform sampler2D ourTexture;
void main()
{
//第一个参数是纹理采样器,第二个参数是对应的纹理坐标。
//texture函数会使用之前设置的纹理参数对相应的颜色值进行采样。
FragColor = texture(ourTexture, TexCoord);
}
glBindTexture()会自动把纹理对象赋值给片段着色器的对应的的采样器
//在调用glDrawElements之前绑定纹理了,它会自动把纹理赋值给片段着色器的采样器
glBindTexture(GL_TEXTURE_2D, texture);
glBindVertexArray(VAO);
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
多个纹理:片段着色器中可以设置多个纹理采样器,每个纹理采样器都有其独自的位置,这个位置被称为纹理单元。
通过指定位置访问纹理采样器,每个纹理采样器绑定不同的纹理数据,这样我们就可以使用多个纹理。使用每个纹理采样器之前需要激活。
设置纹理采样器的纹理单元,放在渲染循环的前面:
//设置每个纹理采样器的纹理单元(位置) uniform变量
//只有一个纹理时,默认纹理单元是0,是默认激活的,所以不需要设置纹理单元和激活纹理单元
/*uniform sampler2D texture1; uniform sampler2D texture2;*/
ourShader.use();
glUniform1i(glGetUniformLocation(ourShader.ID, "texture1"), 0); //GL_TEXTURE0
ourShader.setInt("texture2", 1); //GL_TEXTURE1
激活纹理单元,放在渲染循环中:
glActiveTexture(GL_TEXTURE0); //激活纹理单元 GL_TEXTURE0 - GL_TEXTURE16
glBindTexture(GL_TEXTURE_2D, texture1); //绑定纹理,自动把纹理图像赋值给片段着色器的纹理采样器
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, texture2);
8.示例
两个纹理
纹理采样器 - 纹理单元 - 纹理ID
1.生成纹理
2.设置每个纹理采样器对应的纹理单元,
3.使用纹理前,要先激活纹理单元,然后绑定纹理ID,(并把纹理赋值给纹理采样器),然后绘制;
纹理单元相当于一个桥梁,沟通纹理采样器和纹理(通过纹理ID可使用纹理)
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texture1);
FS:
uniform sampler2D texture1;
uniform sampler2D texture2;//texture()函数用于采样,参数1是采样器,参数2是纹理坐标
FragColor = mix(texture(texture1, TexCoord), texture(texture2, TexCoord), 0.2);
//加载并生成纹理,通过ID访问每个纹理
unsigned int texture1,texture2; /ID
//texture1
glGenTextures(1, &texture1);
glBindTexture(GL_TEXTURE_2D, texture1); //将texture1绑定到GL_TEXTURE_2D上
// set the texture wrapping parameters
// set texture filtering parameters
//加载并生成纹理
unsigned char* data = stbi_load("D:/Files-D/Codes/OpenGL/RuMen/RuMen/container.jpg", &width, &height, &nrChannels, 0);if (data) {
//参数:绑定纹理目标,多级渐远纹理的级别,纹理格式,宽,高,总是0,源图格式,源图数据类型,纹理数据
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data);
glGenerateMipmap(GL_TEXTURE_2D);
}
else {
std::cout << "Failed to load texture" << std::endl;
}
stbi_image_free(data);//texture2同样
/设置每个纹理采样器的纹理单元(位置) uniform变量
//只有一个纹理时,默认纹理单元是0,是默认激活的,所以不需要设置纹理单元和激活纹理单元
ourShader.use();
ourShader.setInt("texture1", 0); //GL_TEXTURE0
ourShader.setInt("texture2", 1); //GL_TEXTURE1
//使用
ourShader.use();
glActiveTexture(GL_TEXTURE0); //激活纹理单元
glBindTexture(GL_TEXTURE_2D, texture1); //绑定纹理,自动把纹理图像赋值给片段着色器对应的的纹理采样器
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, texture2);