Textures#
我们了解到,为了增加物体的细节,我们可以为每个顶点设置颜色,从而创建一些有趣的图像。然而,为了获得相当逼真的效果,我们需要大量的顶点才能指定多种颜色。这会带来相当大的额外开销,因为每个模型都需要更多的顶点,并且每个顶点都需要一个颜色属性。
艺术家和程序员通常更喜欢使用纹理。纹理是一种二维图像(甚至还有一维和三维纹理),用于为物体添加细节;可以把纹理想象成一张印有精美砖块图案(例如)的纸,将其整齐地折叠覆盖在你的三维房屋上,使房屋看起来像是有石头外墙一样。因为我们可以在单个图像中添加大量细节,所以无需指定额外的顶点,就能使物体看起来极其精细。
除了图像之外,纹理还可以用来存储大量任意数据,以便发送到着色器,但我们将把这留到另一个话题中讨论。
下面你会看到一张 砖墙的纹理图像,它映射到上一章中的三角形上。

为了将纹理映射到三角形,我们需要告诉三角形的每个顶点它对应纹理的哪一部分。因此,每个顶点都应该关联一个纹理坐标,该坐标指定要从纹理图像的哪一部分进行采样。然后,片段插值会处理其他片段的剩余部分。 先画屏幕(三角形),然后再去纹理图片那里采样
纹理坐标在 x 轴和 y 轴方向上的取值范围均为 0 到 1 (请记住,我们使用的是二维纹理图像)。使用纹理坐标获取纹理颜色的过程称为采样。纹理坐标从纹理图像左下角的 (0,0) 开始,到纹理图像右上角的 (1,1) 结束。下图展示了如何将纹理坐标映射到三角形:

我们为三角形指定了 3 个纹理坐标点。我们希望三角形的左下角与纹理的左下角对应,因此我们使用纹理坐标 (0,0) 作为三角形左下角顶点的坐标。右下角也一样,使用纹理坐标 (1,0) 。三角形的顶部应与纹理图像的顶部中心对应,因此我们使用纹理坐标 (0.5,1.0) 。我们只需将这 3 个纹理坐标传递给顶点着色器,顶点着色器再将这些坐标传递给片段着色器,片段着色器会对每个片段的所有纹理坐标进行插值。
最终得到的纹理坐标如下所示:
float texCoords[] = {
0.0f, 0.0f, // lower-left corner
1.0f, 0.0f, // lower-right corner
0.5f, 1.0f // top-center corner
};纹理采样有多种不同的实现方式,其定义也比较宽泛。因此,我们的任务是告诉 OpenGL 应该如何对纹理 进行采样 。
Texture Wrapping#
纹理坐标通常介于 (0,0) 和 (1,1) 之间,但如果我们指定超出此范围的坐标会发生什么?OpenGL 的默认行为是重复纹理图像(我们基本上忽略浮点纹理坐标的整数部分),但 OpenGL 还提供了更多选项:
- GL_REPEAT :纹理的默认行为。重复纹理图像。
图片不断重复。坐标 1.1 看起来和 0.1 一样(忽略整数部分) - GL_MIRRORED_REPEAT :与 GL_REPEAT 相同,但每次重复都会镜像图像。
图片重复,但一次正、一次反。边缘处能完美衔接,没有裂缝感。 - GL_CLAMP_TO_EDGE :将坐标限制在
0到1之间。结果是,较高的坐标会被限制在边缘,从而形成拉伸的边缘图案。坐标只要大于 1.0,就永远取 1.0 那一像素的颜色。结果是边缘被无限拉伸成直线。 - GL_CLAMP_TO_BORDER :超出范围的坐标现在会赋予用户指定的边框颜色。
图片外面是一片纯色(你可以自己定颜色,比如黑色或透明)。
当使用超出默认范围的纹理坐标时,每个选项都会产生不同的视觉效果。让我们看看它们在示例纹理图像上的效果(原图由 Hólger Rezende 提供):




