learnOpenGL笔记——基础篇(4)

纹理坐标(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);

效果如下:


版权声明:本文为giraffe1997原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。