纹理
- UV
- 环绕和过滤
- Anisotropic filtering
- Mipmaps
- 压缩纹理
UV
给模型贴纹理时,我们需要通过UV坐标来告诉OpenGL用哪块图像填充三角形。
每个顶点除了位置坐标外还有两个浮点数坐标:U和V。这两个坐标用于访问纹理,如下图所示:
gpu通过uv间的差值映射到对应的纹理像素上!
图片加载
这里就不对图片的加载进行详细的说明了。直接使用stb_image.h
进行加载解释就可以了!
#define STB_IMAGE_IMPLEMENTATION
#include "stb_image.h"
int width, height, nrChannels;
unsigned char *data = stbi_load("container.jpg", &width, &height, &nrChannels, 0);
纹理生成
纹理的生成和各种buffer的生成有点相似
unsigned int texture;
glGenTextures(1, &texture);
glBindTexture(GL_TEXTURE_2D, texture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data);
glGenerateMipmap(GL_TEXTURE_2D);
环绕和过滤
环绕
环绕方式 | 描述 |
---|---|
GL_REPEAT | 对纹理的默认行为。重复纹理图像。 |
GL_MIRRORED_REPEAT | 和GL_REPEAT一样,但每次重复图片是镜像放置的。 |
GL_CLAMP_TO_EDGE | 纹理坐标会被约束在0到1之间,超出的部分会重复纹理坐标的边缘,产生一种边缘被拉伸的效果。 |
GL_CLAMP_TO_BORDER | 超出的坐标为用户指定的边缘颜色。 |
依次对应的效果如下:
![png](/images/computer/render/opengl/opengl_project_create7_1.png “GL_REPEAT | GL_MIRRORED_REPEAT | GL_CLAMP_TO_EDGE | GL_CLAMP_TO_BORDER”) |
可以通过glTexParameteri
来设定这些属性
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_MIRRORED_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_MIRRORED_REPEAT);
过滤
因为纹理的像素基本不可能和需要渲染的区域的像素大小是一致的,在有差异的情况下,gpu的采样方式有一下几种:
- 线性过滤(Linear filtering)
- 邻近过滤(Nearest Neighbor Filtering)
邻近过滤
就是返回uv取点最近的像素的颜色
线性过滤
就是返回纹理坐标附近的纹理像素,计算出一个插值
Anisotropic filtering
这种方法逼近了真正片断中的纹素区块。例如下图中稍稍旋转了的纹理,各向异性过滤将沿蓝色矩形框的主方向,作一定数量的采样(即所谓的”各向异性层级”),计算出其内的颜色。
Mipmaps
可以使用glGenerateMipmap(GL_TEXTURE_2D);
直接生成mipmap,每级都缩小1/2,也可以手动指定每级glTexImage2D
的第二个参数就可以指定。
纹理压缩
纹理的压缩格式之间的差异这里就不做比较了!wikipedia
简单点说就是,压缩纹理后,在文件头里记录了压缩的格式,
通过转换文件头来指定相应的格式使得opengl能识别就可以了!
unsigned int components = (fourCC == FOURCC_DXT1) ? 3 : 4;
// fourCC就是从文件头里解释出来的字节
unsigned int format;
switch(fourCC)
{
case FOURCC_DXT1:
format = GL_COMPRESSED_RGBA_S3TC_DXT1_EXT;
break;
case FOURCC_DXT3:
format = GL_COMPRESSED_RGBA_S3TC_DXT3_EXT;
break;
case FOURCC_DXT5:
format = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT;
break;
default:
free(buffer);
return 0;
}
最后使用glCompressedTexImage2D
来加载压缩的纹理就可以了
unsigned int blockSize = (format == GL_COMPRESSED_RGBA_S3TC_DXT1_EXT) ? 8 : 16;
unsigned int offset = 0;
/* load the mipmaps */
for (unsigned int level = 0; level < mipMapCount && (width || height); +level)
{
unsigned int size = ((width+3)/4)*((height+3)/4)*blockSize;
glCompressedTexImage2D(GL_TEXTURE_2D, level, format, width, height,
0, size, buffer + offset);
offset += size;
width /= 2;
height /= 2;
}
free(buffer);
return textureID;