
纹理
纹理坐标
在前面的教程中,我们给三角形的每个顶点传递一个颜色值,画出了一个类似调色盘的三角形。如果我们想要画出更加逼真的图像,就需要传递更多的顶点和更多的颜色值。如果绘制一个特定的三角形都需要成千上万个点,那实在是太麻烦了。在OpenGL中通常通过纹理来实现这样的绘制效果。
纹理通常是一张 2D 图片(也可能是 1D 或者 3D),它可以用来添加物体的细节;你可以想象纹理是一张绘有砖块的纸,无缝折叠贴合到你的 3D 的房子上,这样你的房子看起来就像有砖墙外表了。因为我们可以在一张图片上插入非常多的细节,这样就可以让物体非常精细而不用指定额外的顶点。

为了能够把纹理映射(Map)到三角形上,我们需要指定三角形的每个顶点各自对应纹理的哪个部分。这样每个顶点就会关联着一个纹理坐标(Texture Coordinate),用来标明该从纹理图像的哪个部分采样。之后在图形的其它片段上进行片段插值(Fragment Interpolation)。纹理坐标在 x 和 y 轴上,范围为 0 到 1 之间(注意我们使用的是 2D 纹理图像)。使用纹理坐标获取纹理颜色叫做采样(Sampling)。纹理坐标起始于(0, 0),也就是纹理图片的左下角,终止于(1, 1),即纹理图片的右上角。下面的图片展示了我们是如何把纹理坐标映射到三角形上的。

三角形有三个顶点坐标,我们只需要依次指定三个纹理坐标像下面这样:
float texCoords[] = {
0.0f, 0.0f, // 左下角
1.0f, 0.0f, // 右下角
0.5f, 1.0f // 上中
};
纹理环绕方式
纹理坐标通常范围是(0, 1),如果设置数值超出了这个范围,OpenGL 默认的行为是重复这个纹理图像,当然也为我们提供了更多选择:
| 环绕方式 | 描述 |
|---|---|
| GL_REPEAT | 对纹理的默认行为。重复纹理图像。 |
| GL_MIRRORED_REPEAT | 和GL_REPEAT一样,但每次重复图片是镜像放置的。 |
| GL_CLAMP_TO_EDGE | 纹理坐标会被约束在0到1之间,超出的部分会重复纹理坐标的边缘,产生一种边缘被拉伸的效果。 |
| GL_CLAMP_TO_BORDER | 超出的坐标为用户指定的边缘颜色。 |
当纹理坐标超出默认范围时,每个选项都有不同的视觉效果输出。我们来看看这些纹理图像的例子:

我们通过 glTexParameter* 函数对单独的一个坐标轴设置(s、t(如果是使用 3D 纹理那么还有一个 r)它们和 x、y、z 是等价的):
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_MIRRORED_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_MIRRORED_REPEAT);
- 第一个参数指定了纹理目标;使用的是 2D 纹理,因此纹理目标是
GL_TEXTURE_2D。 - 第二个参数需要我们指定设置的选项与应用的纹理轴。我们打算配置的是
WRAP选项,并且指定S和T轴。 - 最后一个参数需要我们传递一个环绕方式(Wrapping),这里环绕方式为
GL_MIRRORED_REPEAT。 - 如果选择
GL_CLAMP_TO_BORDER选项,我们还需要指定一个边缘的颜色。这需要使用glTexParameter函数的fv后缀形式,用GL_TEXTURE_BORDER_COLOR作为它的选项,并且传递一个float数组作为边缘的颜色:
float borderColor[] = {
1.0f, 1.0f, 0.0f, 1.0f };
glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, borderColor);
纹理采样
纹理坐标不依赖分辨率,如果实际绘制到屏幕上的区域很大,但是提供的纹理图片分辨率不够高,就需要通过插值的方式来填充缺失的像素值。OpenGL 提供了很多中采样的方式来进行插值,这里只讨论 GL_NEAREST 和 GL_LINEAR。
GL_NEAREST是 OpenGL 默认的纹理插值方式。当设置为 GL_NEAREST 的时候,OpenGL 会选择中心点最接近纹理坐标的那个像素。下图中你可以看到四个像素,加号代表纹理坐标。左上角那个纹理像素的中心距离纹理坐标最近,所以它会被选择为样本颜色:

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

两种方式的效果如下:

当进行放大(Magnify)和缩小(Minify)操作的时候可以设置纹理插值的选项,比如你可以在纹理被缩小的时候使用邻近插值,被放大时使用线性插值。我们需要使用 glTexParameter* 函数为放大和缩小指定插值方式。
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
多级渐远纹理
当实际屏幕绘制的分辨率太小,和纹理分辨率相差太多的时候,无论设置那种插值方式可能都无法得到较好的效果。OpenGL 使用一种叫做多级渐远纹理(Mipmap)的概念来解决这个问题,它简单来说就是一系列的纹理图像,后一个纹理图像是前一个的二分之一。多级渐远纹理背后的理念很简单:距观察者的距离超过一定的阈值,OpenGL 会使用不同的多级渐远纹理,即最适合物体的距离的那个。由于距离远,解析度不高也不会被用户注意到。同时,多级渐远纹理另一加分之处是它的性能非常好。让我们看一下多级渐远纹理是什么样子的:

OpenGL 提供 glGenerateMipmap 帮我们创建多集渐远纹理。在渲染中切换多级渐远纹理级别(Level)时,OpenGL 在两个不同级别的多级渐远纹理层之间会产生不真实的生硬边界。就像普通的纹理过滤一样,切换多级渐远纹理级别时你也可以在两个不同多级渐远纹理级别之间使用 NEAREST 和 LINEAR 插值。为了指定不同多级渐远纹理级别之间的插值方式,你可以使用下面四个选项中的一个代替原有的方式:
| 插值方式 | 描述 |
|---|---|
| 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

最低0.47元/天 解锁文章
418

被折叠的 条评论
为什么被折叠?



