主要是给自己看的所以写得很随意
相比上一节多的部分
1. vertex data多了纹理坐标
cpp
float vertices[] = {
// positions // colors // texture coords
0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f,1.0f, // top right
0.5f, -0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f,0.0f, // bottom right
-0.5f, -0.5f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f,0.0f, //bottom left
-0.5f, 0.5f, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f,1.0f //top left
};
2.设置顶点属性指针和启用对应顶点属性时多了对于texture的处理
cpp
// texture coord attribute
glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(6 * sizeof(float)));
glEnableVertexAttribArray(2);
3.加载生成纹理
cpp
//Load and create a texture
//-----------------------------------
unsigned int texture1,texture2;
glGenTextures(1, &texture1);
glBindTexture(GL_TEXTURE_2D, texture1);
// set the texture wrapping parameters
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); // set texture wrapping to GL_REPEAT (default wrapping method)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
// set texture filtering parameters
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
//load image, create texture and generate mipmaps
int width, height, nrChannels;
unsigned char* data = stbi_load("container.jpg", &width, &height, &nrChannels, 0);
if (data)
{
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);
// texture 2
// ---------
glGenTextures(1, &texture2);
glBindTexture(GL_TEXTURE_2D, texture2);
// set the texture wrapping parameters
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); // set texture wrapping to GL_REPEAT (default wrapping method)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
// set texture filtering parameters
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
// load image, create texture and generate mipmaps
stbi_set_flip_vertically_on_load(true);
data = stbi_load("awesomeface.png", &width, &height, &nrChannels, 0);
if (data)
{
// note that the awesomeface.png has transparency and thus an alpha channel, so make sure to tell OpenGL the data type is of GL_RGBA
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
glGenerateMipmap(GL_TEXTURE_2D);
}
else
{
std::cout << "Failed to load texture" << std::endl;
}
stbi_image_free(data);
都是一样的流程,生成对象并保存下ID→将生成的对象绑定到当前OpenGL上下文中
纹理这里还设置了环绕模式和缩小放大过滤器
环绕模式
cpp
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
s, t, r和x, y, z等价
纹理坐标设置在范围之外会发生什么
我这里其实没有太理解这个坐标是什么,感觉OpenGL中有很多坐标,希望后续学习中能理解
目前我的理解是纹理坐标就是在输入顶点时同时标明顶点对应了纹理图像的哪个部分 默认的纹理图像左下角是(0,0),右上角是(1,1) ,修改大纹理坐标只是意味着映射时会有超出的部分,我需要选择方式来填充空白的部分
环绕方式 | 描述 |
---|---|
GL_REPEAT | 对纹理的默认行为。重复纹理图像。 |
GL_MIRRORED_REPEAT | 和GL_REPEAT一样,但每次重复图片是镜像放置的。 |
GL_CLAMP_TO_EDGE | 纹理坐标会被约束在0到1之间,超出的部分会重复纹理坐标的边缘,产生一种边缘被拉伸的效果。 |
GL_CLAMP_TO_BORDER | 超出的坐标为用户指定的边缘颜色。 |
过滤器
就是说如果当纹理的像素在渲染时需要覆盖比起实际大小更多的像素或者更少的像素时,需要做一些特殊的处理来避免锯齿状,模糊等问题,这种处理就是纹理过滤(Texture Filtering)
临近过滤是默认的,会选择中心点最接近纹理坐标的像素。 线性过滤会基于纹理坐标附近的纹理像素,计算出一个插值,近似出这些纹理像素之间的颜色。
cpp
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
多级渐远纹理(Mipmap)
使用于纹理被缩小的情况,简单来说就是一系列纹理图像,后一个纹理图像是前一个的1/2。在被渲染的物体离观察者较远的时候,可以选择合适的mipmap,这样可以提高性能
生成纹理
纹理可以通过glTexImage2D来生成
4.应用纹理
顶点着色器
多传进去一个纹理坐标,需要调整一下顶点着色器
cpp
#version 330 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec3 aColor;
layout (location = 2) in vec2 aTexCoord;
out vec3 ourColor;
out vec2 TexCoord;
void main()
{
gl_Position = vec4(aPos, 1.0);
ourColor = aColor;
TexCoord = aTexCoord;
}
片段着色器会把顶点着色器的输出变量TexCoord作为输入变量
片段着色器是在每个像素(或称为片段)上执行的,因此它是处理纹理映射和颜色计算的理想位置。在片段着色器中使用采样器可以对每个像素的纹理采样进行灵活的控制,从而实现各种图形效果,如纹理映射、环境映射、阴影等。 使用采样器将纹理对象传给片段着色器(在这里是sampler2D
)
片段着色器
cpp
#version 330 core
out vec4 FragColor;
in vec3 ourColor;
in vec2 TexCoord;
// texture samplers
uniform sampler2D texture1;
uniform sampler2D texture2;
void main()
{
// linearly interpolate between both textures (80% container, 20% awesomeface)
FragColor = mix(texture(texture1, TexCoord), texture(texture2, TexCoord), 0.2);
}
使用GLSL内建的texture函数来采样纹理的颜色,它第一个参数是纹理采样器,第二个参数是对应的纹理坐标。texture函数会使用之前设置的纹理参数对相应的颜色值进行采样。 这个片段着色器的输出就是纹理的(插值)纹理坐标上的(过滤后的)颜色。