opengl基础学习(7)

纹理

  • UV
  • 环绕和过滤
  • Anisotropic filtering
  • Mipmaps
  • 压缩纹理

UV

给模型贴纹理时,我们需要通过UV坐标来告诉OpenGL用哪块图像填充三角形。

每个顶点除了位置坐标外还有两个浮点数坐标:U和V。这两个坐标用于访问纹理,如下图所示:

png

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取点最近的像素的颜色

png

线性过滤

就是返回纹理坐标附近的纹理像素,计算出一个插值

png

Anisotropic filtering

这种方法逼近了真正片断中的纹素区块。例如下图中稍稍旋转了的纹理,各向异性过滤将沿蓝色矩形框的主方向,作一定数量的采样(即所谓的”各向异性层级”),计算出其内的颜色。

png

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;