0%

texture

Texture

为了给渲染的物体添加更多的细节,我们可以使用更多的vertex和颜色,但是实际情况是,如果我们要渲染对象比较复杂,那么我们需要定义很多的vertex数据和颜色数据。这时候就可以利用texture来简化。

texture通常来说是一个二维图片(虽然也有一维texture和三维texture存在)。

为了将texture绘制到vertex组成的物体上,我们需要告诉每个vertex分别对应texture的那个部分,因此每个顶点就需要一个texture coordinate用于指定vertex对应纹理图片的那个部分,中间区域最后交给fragment interpolation去完成。

texture coordinate一般来说每个轴的曲直范围为[0, 1]闭区间,对于二维texture来说,坐标原点为左下角,右上角的坐标为(1, 1)

texture coordinate

Texture Wrapping

Texture coordinate的取值范围通常来说通常是(0, 0) 到 (1, 1),但是如果我们将坐标设置为大于1的值时,OpenGL的默认做法是重复texture图片,但是OpenGL也提供了其他的处理方式:

  • GL_REPEATE,默认的处理方式,重复texture图片
  • GL_MIRORED_REPEATE,重复texture图片,但是是镜像重复
  • GL_CLAMP_TO_EDGE,将坐标压缩到0到1之间,最终结果是高坐标区域的图片被拉伸
  • GL_CLAMP_TO_BORDER,超出范围的坐标区域会被赋予用户指定的边框颜色

下图分别是四种选项对应的效果

texture wrapping

上面的选项可以针对每一个texture coordinate的坐标轴进行设定
1
2
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_MIRRORED_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_MIRRORED_REPEAT);

如果我们使用GL_CLAMP_TO_BORDER选项,我们还必须指定border的颜色:

1
2
float borderColor[] = { 1.0f, 1.0f, 0.0f, 1.0f };
glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, borderColor);

如果坐标设置为小于1的值,会对texture图片进行裁剪。

Texture filtering

texture coordinate和texture图片的分辨率没有关系,因此OpenGL必须决定如何将texture coordinate映射到texture图片上的具体像素去,这就叫Texture filtering。和Texture wrapping 一样,OpenGL也提供了多种 texture filtering的选项:

  • GL_NEAREST,OpenGL默认的Texture filtering方法,使用这个选项时,OpenGL会选择中心最接近Texture coordinate点的像素点颜色最为最终的texture coordinate点颜色

    nearest texture filtering

  • GL_LINEAR,使用这个选项时,texture coordinate对应颜色为周围像素的线形插值的和,像素点到texture coordinate中心距离越小,贡献的颜色值占比就越多

    linear texture filtering

下图分别是两种选项在实际使用中最终的渲染效果:

texture filtering result

可以看到使用GL_LINEAR选项时,最终效果图更光滑,但是GL_LINEAR需要的计算量更多,GL_NEAREST效果更像8bit像素风。

修改Texture filtreing选项的值仍然可以使用glTexParameteri函数

1
2
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

Mipmaps

通常来说,一个场景会有很多物体对象,很多对象可能使用的是一个texture图片,但是有些对象离视窗很近,这些对象看着比较大一些,有些对象离得比较远,看着会小一些,这些小的物体最终可能只会生成几个fragment,相对于比较大的物体而言,OpenGL针对这些小的物体去获取texture上的颜色会比较困难,因为它必须遍历纹理图片的大部分区域来决定一个fragment的颜色,这不仅会导致内存浪费,同时也会导致小物体渲染效果很差。

为了解决这个问题,OpenGL引入了Mipmaps的概念。所谓Mipmap就是一组texture图片,只不过后一个图片的长宽是前一个图片长宽的一半。在从视窗经过一定距离后,OpenGL在mipmap中会使用一个适合当前距离的texture图片,这样对于较远的,最终比较小的物体来说就有了合适的texture。

一个mipmaps看起来一般是这样的:

mipmaps example

如果我们自己手动来制作一个mipmap贴图的话会很麻烦,但是幸运的是OpenGL为我们提供了根据已有texture对象创建mipmaps的方法。

在渲染是,可能会遇到切换mipmaps级别的情况,这时为了决定最终的颜色值,和原来的texture filtering一样,OpenGL也为mipmaps提供了filtering的方法。我们可以使用下面四种选项:

  • GL_NEAREST_MIPMAP_NEAREST,针对mipmaps使用nearest采样,针对texture filtering使用nearest采样
  • GL_LINEAR_MIPMAP_NEAREST,针对mipmaps使用linear采样,针对texture filtering使用nearest采样
  • GL_NEAREST_MIPMPAP_LINEAR,针对mipmaps使用nearest采样,针对texture filtering使用linear采样
  • GL_LINEAR_MIPMAP_LINEAR,针对mipmaps使用linear采样,针对texture filtering使用linear采样
1
2
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
开发者一个经常犯的错误是将mipmaps filtering的选项设置到放大filter(magnification filter)的选项上,由于mipmaps解决的场景是texture需要缩小的场景,texture放大的场景不需要使用到mipmaps,在magnification filter上设置一个mipmaps的选项会导致 GL_INVALID_ENUM错误。