OpenGL: 纹理采样 texture sample

Sampler (GLSL)

Sampler通常是在Fragment shader(片元着色器)内定义的,这是一个uniform类型的变量,即处理不同的片元时这个变量是一致不变的。一个sampler和一个texture对应,类型也是对应的,比如sampler2D 的sampler对应的就是GL_TEXTURE_2D类型的纹理对象。Sampler是个变量,但是它是没有值的,或者說是特殊的一种类型,讨论其数值没有意义,只要明确其同一个texture对应即可。sampler变量在shader内使用的地方就是texture函数。这是一个lookup 函数(我不知道该如何翻译,我的理解是这是一个查找/查询的函数,以给定的纹理坐标去纹理的数据中查到相应的颜色的信息),得到颜色信息渲在每一个片元上。例如,一个三角形,我们只要传递三个纹理坐标给顶点着色器就行了,接着片元着色器会为每个像素生成纹理坐标的插值,根据纹理坐标就得到了每一个像素的颜色值。

纹理采样的机制

Texture Wrapping

纹理坐标通常的范围是从(0, 0)到(1, 1),如果我们把纹理坐标设置为范围以外会发生什么?OpenGL默认的行为是重复这个纹理图像(我们简单地忽略浮点纹理坐标的整数部分),但OpenGL提供了更多的选择:

  • GL_REPEAT:纹理的默认行为。重复纹理图像。
  • GL_MIRRORED_REPEAT:和GL_REPEAT一样,除了重复的图片是镜像放置的。
  • GL_CLAMP_TO_EDGE:纹理坐标会在0到1之间。超出的部分会重复纹理坐标的边缘,就是边缘被拉伸。
  • GL_CLAMP_TO_BORDER:超出的部分是用户指定的边缘的颜色。

在生成纹理对象的时候,需要用 glTexParameter() 设置一系列参数,设置的就是这个参数以及下面的 Texture Filtering 的参数。

Texture Filtering

magnification/minification

组成纹理的图片数据和其要贴上去的形状的大小往往是不一样的。两种情况:

  1. magnification:纹理图片小,贴图区域大,需要放大纹理
  2. minification:反过来,纹理图片大,贴图区域小,缩小纹理显示出来

在做放大和缩小的操作的时候的具体的策略如下:

  • GL_NEAREST:直接选择最临近的像素的颜色,magnification(放大)时:由于多个片元会在同一个纹理像素上面取值,故最终得到的图片颗粒度很大,会有锯齿。
  • GL_LINEAR:根据临近四个的像素点的颜色值,做线性的插值计算,得到最终的颜色。magnification(放大)时:不会产生锯齿,显示更加平滑。

在minification(缩小)时,上面的两种方法其实都不理想,无论如何都会丢失很多图片的细节,OpenGL 用Mipmap来解决这个问题。

Mipmap

它就是一系列纹理,每个后面的一个纹理是前一个的二分之一,这一系列的纹理是OpenGL生成的,生成时进行了图像质量的优化,使其拥有更多的细节。这一系列的纹理是提前生成的,程序运行时只需要从中挑出合适大小的纹理应用即可,而不是运行时进行图像大小的处理,效率上会有提高。

OpenGL渲染的时候,两个不同级别的mipmap之间会产生不真实感的生硬的边界。就像普通的纹理过滤一样,也可以在两个不同mipmap级别之间使用NEAREST和LINEAR过滤。指定不同mipmap级别之间的过滤方式可以使用下面四种选项代替原来的过滤方式:

  • GL_NEAREST_MIPMAP_NEAREST:接收最近的mipmap来匹配像素大小,并使用最临近插值进行纹理采样。
  • GL_LINEAR_MIPMAP_NEAREST:接收最近的mipmap级别,并使用线性插值采样。
  • GL_NEAREST_MIPMAP_LINEAR:在两个mipmap之间进行线性插值,通过最邻近插值采样。
  • GL_LINEAR_MIPMAP_LINEAR:在两个相邻的mipmap进行线性插值,并通过线性插值进行采样。

总结一下:magnification和minification的时候都可以设置NEAREST和LINEAR两种方式;minification的时候还可以设置mipmap的方式,该方法效果更好。关于具体的算法的实现,可以参考《OpenGL ES specification》的8.13-8.14内容。

Sampler object

一个texture对象包括了两部分的属性,一部分是具体的图片信息,另一部分是纹理采样的设置,即上文提到的不同的方式。通常生成纹理的时候是将这两部分一起设置好的,但是后面这部分的内容可以单独拿出来,封装成为一个对象,就是 sampler object .

使用 GenSamplers() 函数创建一个新的sampler对象,然后用 BindSampler() 做绑定操作,将sampler和texture对象绑定起来,然后调用 glSamplerParameterf() 这一类的函数来设置sampler的具体的参数。

texture object, sampler object, program object 的关系如图:

OpenGL: 纹理采样 texture sample
OpenGL: 纹理采样 texture sample


参考资料:

OpenGL 教程--纹理
图片来源

上一篇:OCP-052考试题库汇总(13)-CUUG内部解答版


下一篇:20135327郭皓--Linux内核分析第六周 进程的描述和进程的创建