纹理
纹理:一张贴到物体上的二维图像。
映射
纹理映射规则:坐标起始于(0, 0),也就是纹理图片的左下角,终始于(1, 1),即纹理图片的右上角;如图:
使用纹理坐标获取纹理颜色叫做**采样(Sampling)**。
环绕方式
纹理坐标的范围通常是从 (0, 0)
到 (1, 1)
,但是如果把纹理坐标设置在范围之外,该如何处理呢?OpenGL
提供了更多的选择:
环绕方式 | 描述 |
---|---|
GL_REPEAT |
重复纹理图像(默认)。 |
GL_MIRRORED_REPEAT |
和 GL_REPEAT 一样,但每次重复图片是镜像放置的。 |
GL_CLAMP_TO_EDGE |
纹理坐标会被约束在 0 到 1 之间,超出的部分会重复纹理坐标的边缘,产生一种边缘被拉伸的效果。 |
GL_CLAMP_TO_BORDER |
超出的坐标为用户指定的边缘颜色。 |
效果如图:
过滤
由于纹理坐标可以是任意浮点值,纹理坐标不依赖于分辨率(Resolution),所以 OpenGL
需要知道怎样将**纹理像素(Texture Pixel)**映射到纹理坐标,其中最常见的两种是:
GL_NEAREST(临近过滤):选择中心点最接近纹理坐标的那个像素,默认行为。
GL_LINEAR(线性过滤):纹理坐标位置附近的几个纹素值进行某种插值计算之后的结果。
Mipmaps
**Mipmaps:**用于解决在场景中,距离比较远的物体的纹理真实映射问题,使看起来更真实,更有距离感。即就是一系列的纹理图片,每一张纹理图的大小都是前一张的1/4,直到剩最后一个像素为止。如图:
对于刚好在两张图片之间的物体,参考前面两种不同的过滤方式,以及 Mipmaps
就有了四种不同的过滤方案:
过滤方式 | 描述 |
---|---|
GL_NEAREST_MIPMAP_NEAREST |
使用最邻近的多级渐远纹理来匹配像素大小,并使用邻近插值进行纹理采样。 |
GL_LINEAR_MIPMAP_NEAREST |
使用最邻近的多级渐远纹理级别,并使用线性插值进行采样。 |
GL_NEAREST_MIPMAP_LINEAR |
在两个最匹配像素大小的多级渐远纹理之间进行线性插值,使用邻近插值进行采样。 |
GL_LINEAR_MIPMAP_LINEAR |
在两个邻近的多级渐远纹理之间使用线性插值,并使用线性插值进行采样。 |
加载图片
使用 stbi_load
函数来加载图片,并且将图片格式信息保存起来。
1 | GLint imgWidth; /* 纹理图片 宽度 */ |
纹理顶点
对于纹理映射,需要告诉 OpenGL
该如何采样纹理图片数据:
1 | GLfloat vertices[] = |
其顶点属性在内存中如图所示:
同时更新属性:
1 | /* 颜色属性 */ |
顶点着色器:
1 |
|
片段着色器:
1 |
|
纹理生成
生成唯一纹理对象 ID;
1
2GLuint textureId;
glGenTextures(1, &textureId);绑定 纹理对象,指明是 2D 纹理
1
glBindTexture(GL_TEXTURE_2D, textureId);
加载纹理图片
1
2
3
4GLint imgWidth;
GLint imgHeight;
GLint nrChannel;
GLubyte* imgData = stbi_load(imgPath, &imgWidth, &imgHeight, &nrChannel, 0);(注意:由于OpenGL 期待原点
(0,0)
位于左下角,而通常一张图片的原点位于左上角,所有默认加载图片会导致上下颠倒,所有在加载图片之前调用stbi_set_flip_vertically_on_load(true)
来解决此问题。)生成纹理
1
glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, imgWidth, imgHeight, 0, pixelFormat, GL_UNSIGNED_BYTE, imgData);
- 参数1:指定了纹理目标;
GL_TEXTURE_2D意味着会生成与当前绑定的纹理对象在同一个目标上的纹理(任何绑定到
GL_TEXTURE_1D和
GL_TEXTURE_3D` 的纹理不会受到影响)。 - 参数2:为纹理指定多级渐远纹理的级别。
- 参数3:纹理保存的格式。
- 参数4:纹理的宽度。
- 参数5:纹理的高度。
- 参数6:设为 0 (历史遗留的问题)。
- 参数7:原图片的格式。
- 参数8:原图片的数据类型。
- 参数9:图形数据。
- 参数1:指定了纹理目标;