纹理的基础知识

纹理是一种数据存储形式,不仅可以方便地访问特定的数据条目,还可以将多个条目混合(插入)在一起。

在 OpenGL 中,纹理可以用于很多事情,但最常见的是它将图像映射到多边形(例如三角形)。为了将纹理映射到三角形(或另一个多边形),我们必须告诉每个顶点它对应的纹理部分。我们将纹理坐标分配给多边形的每个顶点,然后在该多边形中的所有片段之间进行插值。纹理坐标在 x 和 y 轴上的范围通常为 0 到 1,如下图所示:

StackOverflow 文档

此三角形的纹理坐标如下所示:

GLfloat texCoords[] = {
    0.0f, 0.0f,  // Lower-left corner  
    1.0f, 0.0f,  // Lower-right corner
    0.5f, 1.0f   // Top-center corner
};

将这些坐标放入 VBO(顶点缓冲区对象)并为着色器创建新属性。你应该已经具有顶点位置的至少一个属性,因此为纹理坐标创建另一个属性。

生成纹理

首先要做的是生成一个纹理对象,该对象将由一个 ID 引用,该 ID 将存储在 unsigned int 纹理中

GLuint texture;
glGenTextures(1, &texture); 

之后必须绑定所有后续纹理命令将配置此纹理:

glBindTexture(GL_TEXTURE_2D, texture); 

加载图片

要加载图像,你可以创建自己的图像加载器,也可以使用图像加载库,例如 c ++中的 SOIL (简单 OpenGL 图像库)或 java 中的 TWL PNGDecoder

使用 SOIL 加载图像的示例如下:

int width, height;
unsigned char* image = SOIL_load_image("image.png", &width, &height, 0, SOIL_LOAD_RGB); 

现在你可以将此图像指定给纹理对象:

glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, image);

之后你应该取消绑定纹理对象:

glBindTexture(GL_TEXTURE_2D, 0); 

包裹纹理坐标的参数

如上所示,纹理的左下角有 UV(st) 坐标(0,0),纹理的右上角有坐标(1,1),但网格的纹理坐标可以在任何范围。要处理这个问题,必须定义纹理坐标如何包裹到纹理。

对纹理卷绕参数坐标可以与设置 glTextureParameter 使用 GL_TEXTURE_WRAP_SGL_TEXTURE_WRAP_TGL_TEXTURE_WRAP_R

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);

可能的参数是:

  • GL_CLAMP_TO_EDGE 使纹理坐标被限制在 [1 / 2N,1 - 1 / 2N]范围内,其中 N 是方向上纹理的大小。

  • GL_CLAMP_TO_BORDERGL_CLAMP_TO_EDGE 相同,但在夹紧的情况下,取出的纹理元素数据被 GL_TEXTURE_BORDER_COLOR 指定的颜色替换。

  • GL_REPEAT 导致纹理坐标的整数部分被忽略。纹理是平铺的

StackOverflow 文档

  • GL_MIRRORED_REPEAT:如果纹理坐标的整数部分是偶数,则忽略它。相反,如果纹理坐标的整数部分是奇数,则纹理坐标设置为 1 - frac(s)fract(s) 是纹理坐标的小数部分。这会导致纹理每隔一次镜像一次。

StackOverflow 文档

  • GL_MIRROR_CLAMP_TO_EDGE 使纹理坐标重复为 GL_MIRRORED_REPEAT,对于纹理的一次重复,此时要被钳位的坐标如 GL_CLAMP_TO_EDGE

注意 GL_TEXTURE_WRAP_SGL_TEXTURE_WRAP_TGL_TEXTURE_WRAP_R 的默认值是 GL_REPEAT

应用纹理

最后要做的是在绘制调用之前绑定纹理:

glBindTexture(GL_TEXTURE_2D, texture);