纹理坐标(Texture Coordinate):纹理坐标属于二维坐标系,范围通常是从(0, 0)到(1, 1),图形的每个顶点绑定一个纹理坐标,用来标明该从纹理图像的哪个部分采样,也就是映射(Map)。
一个三角形在纹理坐标系中三个顶点对应的纹理坐标如下:
float texCoords[] = {
0.0f, 0.0f, // 左下角顶点
1.0f, 0.0f, // 右下角
0.5f, 1.0f // 上中
};贴图效果如下:

纹理过滤(纹理滤波)
GL_NEAREST(也叫邻近过滤,Nearest Neighbor Filter)是OpenGL默认的纹理过滤方式。当glTexParameteri的过滤参数设置为GL_NEAREST的时候,OpenGL会选择中心点最接近纹理坐标的那个像素。下面的采样点位于左上角,故返回该颜色作为该顶点的颜色。

GL_LINEAR(也叫线性过滤,(Bi)linear Filtering)它基于纹理坐标附近的纹理像素,计算出一个插值,近似出这些纹理像素之间的颜色。一个纹理像素的中心距离纹理坐标越近,那么这个纹理像素的颜色对最终的样本颜色的贡献越大。下图可以看到返回的颜色是邻近像素的混合色:

当进行放大(Magnify)和缩小(Minify)操作的时候可以设置纹理过滤的选项,比如你可以在纹理被缩小的时候使用邻近过滤,被放大时使用线性过滤。需要使用glTexParameter*函数为放大和缩小指定过滤方式:
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);多级渐远纹理(Mipmap)
在一个场景中,远处的物体和近处的物体有相同高的分辨率,由于远处的物体只能产生很少的片段(透视投影中,近大远小,非常远的物体看起来就像一个点),OpenGL使用高分辨率纹理为这些片段后去正确的颜色值是很困难的,它需要对一个跨过纹理很大部分的片段只拾取一个颜色。
Mipmap原理就是把摄像机到物体的距离与阈值作比较,在不同的距离空间内选用不同像素级别的纹理图像进行采样。以下是结合了Mipmap与纹理过滤的采样方法:
| 过滤方式 | 描述 |
|---|---|
| GL_NEAREST_MIPMAP_NEAREST | 使用最邻近的多级渐远纹理来匹配像素大小,并使用邻近插值进行纹理采样 |
| GL_LINEAR_MIPMAP_NEAREST | 使用最邻近的多级渐远纹理级别,并使用线性插值进行采样 |
| GL_NEAREST_MIPMAP_LINEAR | 在两个最匹配像素大小的多级渐远纹理之间进行线性插值,使用邻近插值进行采样 |
| GL_LINEAR_MIPMAP_LINEAR | 在两个邻近的多级渐远纹理之间使用线性插值,并使用线性插值进行采样 |
就像纹理过滤一样,可以使用glTexParameteri将过滤方式设置为前面四种提到的方法之一:
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);生成纹理
大致过程如下:
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;//调用stbi_load需引入stb_image.h,要注意宏的添加!
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);后面的着色器程序以及顶点属性指针都要有所变化,片段着色器如下:
out vec4 FragColor;
in vec3 ourColor;
in vec2 TexCoord;
uniform sampler2D ourTexture;//sampler2D是供纹理对象使用的内建数据类型,叫采样器
void main()
{
//texture函数使用之前设置的纹理参数,利用ourTexture对相应的颜色值进行采样。
FragColor = texture(ourTexture, TexCoord);
}这个片段着色器的输出就是纹理的(插值)纹理坐标上的(过滤后的)颜色。
多个纹理绑定与混合
多个纹理绑定:例如将图片wall,smile绑定到对应的两个纹理
unsigned int texture1, texture2;
// texture 1 操作
glGenTextures(1, &texture1);
glBindTexture(GL_TEXTURE_2D, texture1);
// set the texture wrapping parameters
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
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("wall.jpg", &width, &height, &nrChannels, 0);
// 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);
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
data = stbi_load("smile.png", &width, &height, &nrChannels, 0);在设置fragment_shader时,将绑定在sampler2D (GL_TEXTURE_2D)的纹理取出,利用mix函数进行混合,参数0.4的意思是前一个纹理的颜色的60%乘以后一个的40%
out vec4 FragColor;
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.4);
}也可以把得到的纹理颜色与在vert shader绑定的顶点颜色混合,具体的操作同样在shader中实现:
//texture取得纹理的颜色,ourColor是
FragColor = texture(ourTexture, TexCoord) * vec4(ourColor, 1.0);效果如下:
