|
纹理处理指的是通过使用一些图像、函数或者其他某些数据来源来调整表面的外观表现,使其渲染出来的图像看起来更加正确
纹理处理管线(The Texturing Pipeline)
每个纹理的处理过程称为纹理管线,纹理图像的像素我们一般还称之为纹素(texel)
上图叙述整个纹理管线,纹理处理的首先是将一个空间中的坐标转换为纹理坐标,通过一个投影函数(projector function),这个处理被称为texture mapping,我们有时也会称纹理图片为texture map,这并不严格正确。第二步通信函数(corresponder function)转换至纹理空间索引,第三步获取对应的纹理值,最后有可能再进行一个值转换(value transform function),最终返回用于shader计算的值
投影函数(projector function)
第一步将表面坐标投影到纹理坐标空间,通常得到是一个二维坐标 ,投影函数把三维的坐标投影到二维UV坐标上,常见的有球面UV展开,圆柱UV展开和平面UV展开,如下图
一般美术可以通过编辑顶点位置的相同方式编辑UV坐标,美术需要分割模型,使用不同的投影函数,如下图
实时渲染中,一般u,v基本在建模的时候就处理好了,储存在顶点中,不过如果在shader中计算其实也是有好处的,能提高精度,还能应用一些效果,比如动画。其他一些渲染方法比如使用环境贴图时,我们则会在PS中另外计算投影函数。
球面投影函数将点集转换为一个虚拟球面,在顶点插值时候会有一些问题。
圆柱投影的u值计算方法和球面投影一样,v则是距离中轴线的距离,适用于那些有天然的轴线的模型,当表面几乎垂直于中轴的时候,会有扭曲。
平面投影则是相当于一个正交投影,这种类型的投影对一些贴花比较有用。
网格展开是一个很大的研究领域,叫参数化网格(mesh parameterization),好的展开算法能极大的简化美术的工作。
纹理坐标有时可以是三维的,uvw或者strq,q是齐次坐标。uvw可以是定义为方向向量,比如采样cube map。
一维的纹理也是有用的,比如代表海拔高度的纹理(相当于LUT),或者渲染线段,其中一个用法就是下雨时用一张半透明的1D纹理渲染雨线
通信函数(corresponder function)
通信函数将纹理坐标转换为纹理空间位置,提供了一定灵活性;通信函数主要有两种功能,一种是定纹理只有一部分可以被用来显示,或者进行一些矩阵变化来任意的应用纹理;另外的一大类则是定义纹理的应用方式:wrap、mirror、clamp、border等如下图
- wrap(DirectX)或repeat(OpenGL)或tile: 纹理在物体表面上进行重复。
- mirror : 纹理在物体表面进行重复,但会被翻转,例如,图像通常是从0到1,然后在1和2之间是反向的,然后在2和3之间是正常的,然后是相反的,等等
- clamp(DirectX)或clamp to edge(OpenGL) : 在范围[0,1]外的值将限制在这个范围内。
- border(DirectX)或clamp to border(OpenGL) : 在范围[0,1]外的值将使用边缘处的颜色
纹理值(Texture Values)
生成纹理空间坐标后,使用坐标来获取纹理值,即对于图像纹理,会检索对应的像素值,程序纹理则是进行对应的计算。常见的纹理值是RGB和RGBA,还有很多类型的数据都可以储存在纹理中。从纹理中获取的值也可以进行转换,如将法线值无符号范围(0.0到1.0)的数据重新映射到有符号范围(1.0到1.0)
图像纹理(Image Texturing )
像素着色器通过将纹理坐标值传递给调用(如texture2D)来访问纹理。这些值位于(u,v)纹理坐标中,由一个响应函数映射到一个范围 ,GPU负责将该值转换为texel坐标,不同API中的纹理坐标系之间有两个主要区别;在DirectX中,纹理的左上角为(0,0),而OpenGL中左下角为(0,0)
依赖纹理读取(dependent texture read)有两个含义。在移动设备中,当获取纹理值时,不使用始未经过任何修改的纹理坐标时都会发生依赖纹理读取,在一些旧设备上不发生依赖纹理读取时效率会高很多。另一个含义是在早期GPU中,依赖纹理读取指的是当一个纹理坐标依赖与一些之前的纹理值,例如纹理改变了表面的坐标,进而影响的cube map的采样结果。
降低纹理采样结果的走样是希望得到的结果,对于输入进行滤波和对于输出进行滤波是有区别的,只要输入和输出是线性相关的,那么对于单个纹理值进行滤波就等同于对于最终颜色进行滤波,但是对于其他非线性相关的值例如法线,标准滤波方式就会产生走样
简单的是当视口区域和纹理图片尺寸相差不大时,基本是还原整张图片的,但当区域很大时,就会发生放大(magnification),反之则是缩小(minification)
放大(Magnification)
放大最常用的放大过滤技术是nearest neighbor和bilinear interpolation,还可以使用cubic convolution提高质量,以下是三种的技术的结果
- nearest neighbor(最邻近法),单个纹理会变得明显即像素化,放大时使用距离每个像素中心最近的纹理,从而看起是块状的,所以看上去质量很差,因为它只需要一个texel
- bilinear interpolation(双线性插值),对于每个像素,这种过滤会找到四个相邻的texel,并在二维中进行线性插值,效果更模糊
双线性插值原理如下图
假设要获取(81.92, 74.24)的值,可以得到以四个相邻的中心定义一个新的坐标系,纹理坐标 为(0.42,0.74)。 标记纹理获取函数,所以
使用detail texture(细节纹理)可以解决图像模糊问题,通过在放大后的纹理叠加很好的表面细节纹理,这些细节以不同的比例作为单独的纹理覆盖在放大纹理上。细节纹理的高频重复图案与低频放大纹理相结合,具有类似于使用单个高分辨率纹理的视觉效果
如上图纹理由棋盘格图案,使用双线性插值可提供不同的灰度采样穿过纹理。通过重新映射,例如,所有低于0.4的灰度都是黑色的大于0.6的灰色为白色,介于两者之间的灰色会拉伸以填充间隙纹理看起来更像一个棋盘,同时也提供了一些混合texel
- 三次卷积插值,用一个平滑曲线来插值2x2的texel组,常用的是smoothstep曲线和quintic曲线
由于smoothstep曲线的特性是x=0和x=1的一阶导数为0,同时在0~1间平滑。quintic 0和1的二阶导数也为0,如果纹理位于RGB空间中的平面上,则这种类型的插值将提供一种平滑但仍呈阶梯状的外观
缩小(Minification)
缩小纹理可能会导致多个纹理覆盖一个像素,为了让每个像素获取正确的颜色,所以需要将多个纹理的效果综合起来;使用与相应的放大过滤器类似方法,最邻近的方法会产生伪像,由于影响像素的许多纹理元素中只有一个被选择来表示表面,当表面相对于观察者移动时,这种伪像甚至更加明显,并且是所谓的temporal aliasing
双线性插值它混合了四个纹理元素,而不是只使用一个,但是当一个像素受到四个以上纹理元素的影响时,过滤器很快就会失败并产生混叠
所有纹理抗锯齿算法背后的基本思想是相同的:预处理纹理并创建数据结构,这将有助于计算一组纹理元素对一个像素的影响的快速近似值。对于实时工作,这些算法具有使用固定数量的时间和资源来执行的特征。通过这种方式,每个像素获取固定数量的样本,并将其组合以计算(潜在大量的)纹理元素的效果
Mipmapping 多级渐远纹理
最流行的纹理抗锯齿方法称为mipmapping。现在已生产的所有图形加速器均以某种形式实现了该功能。“ Mip”代表multum in parvo,拉丁语则代表“小地方的许多东西,这是一个过程的好名字,在该过程中,原始纹理被反复过滤成较小的图像。
使用mipmapping最小化滤波时,原始的贴图被扩展为一组更小版本的贴图,在进行实际渲染之前。纹理(零级)被下采样到原始面积的四分之一,每个新的纹理像素值通常被计算为原始纹理中四个相邻纹理像素的平均值。新的一级纹理有时称为原始纹理的子纹理。递归执行缩小,直到纹理的一个或两个维度等于一个纹理像素, 整个图像集通常称为mipmap chain
在组成高质量mipmaps时最重要的两个元素是良好的滤波以及伽马校正,通常获取每2×2组像素,并将它们平均以得到Mip像素值得到Mipmap级别;使用的滤波器是一个盒装滤波器,这是最差的滤波器。它的结果质量很差,因会导致走样问题,最好使用高斯、兰索斯、凯撒或类似的滤波器。另外,一些图形API本身就在GPU上就提供了一些不错的滤波器。在纹理边处,必须注意在滤波期间是要纹理重复还是单个复制
对于非线性空间中的纹理编码,在滤波时忽略伽马校正会修改mipmap的可见亮度;当我们远离物体时,使用不正确的Mipmap级别的话,物体会整体看起来很暗,对比度和细节也会受到影响。针对这一理由,将这样的纹理从sRGB转换到线性空间是很重要的,在线性空间进行所有的mipmap滤波,将最终的结果转换为sRGB空间。大多数API支持SRGB纹理,所以可以在线性空间生成正确的Mipmap,并且将结果存储在sRGB中。当SRGB纹理被访问时,它们的值首先转换到下线性空间,这样放大和缩小操作会恰当执行
在纹理映射时访问这一结构的基本过程是很直接的。一个屏幕像素围住纹理上的一片区域。当像素区域映射到纹理后,它包含一个或多个像素。使用像素网格边界并不能确保完全正确,但在这里使用可以很简单地进行展示。网格外的纹素可以影响像素的颜色。这一过程的目的是粗略的决定纹理影响像素的程度。有两种通用的方法,它们计算
的值(OpenGL称为 ,也可称为纹理的细分级别)。其中一种方法像素网格的四边形的最长的边来近似模拟像素的覆盖。另一种方法使用4个微分值中的最大值来进行测定, ,即每个微分描述了每个轴向上纹理坐标的改变量,例如, 是每个像素沿x屏幕轴向u纹理值得变化量
使用Shader Model3.0或更新的版本,像素着色器可以获得这些梯度值。由于这些值基于邻近像素值的差来计算,在动态流分支中,像素着色器无法访问这些值。对于在这样的环节中执行的纹理读取,导数必须提早计算。注意由于顶点着色器不能访问梯度信息,梯度或细分级别需要由顶点着色器自己计算,在进行顶点纹理映射的时候提供给GPU
计算坐标d的目的是确定沿着mipmap的金字塔轴在哪里采样;最终为了将像素和texel的比例至少控制在1:1范围内,来实现尼奎斯特速率;这里的重要原则是,当像素单元包含更多的纹理和d时,将访问一个更小、更模糊的纹理版本。(u, v, d)三元组用于访问mipmap。值d类似于纹理级别,但它不是一个整数值,而是级别之间距离的分数值。对d位置上的纹理层和下的纹理层进行采样。(u, v)位置用于从这两个纹理级别中的每一个检索bilinearly插值样本。然后根据每个纹理层到d的距离,对得到的样本进行线性插值。整个过程称为三线性插值,每像素执行一次
将细分级别偏移(LOD bias)加到 上,它会影响纹理可察觉到的清晰度,个对任意给定的纹理的好的LOD 偏移会随图片类型和使用方式变化。例如,那些从开始有点模糊的图片可以使用一个负的偏移值,而那种用于纹理映射的有锯齿感的图片可以使用正的偏移值。可以为整个纹理设置一个全局偏移,或者在像素着色器中逐像素设置。对于更好的控制,用于计算它的坐标或导数可以由用户自己配置
mipmapping优点是,不需要分别对影响像素的所有texel进行求和,而是访问和插值预组合的texel集。这个过程需要一定的时间,无论缩小多少。但有一个主要问题是过度模糊,假设一个像素单元在u方向上覆盖了大量的texel,而在v方向上只覆盖了texel;这种情况通常发生在观察者沿纹理表面几乎是边缘朝上看时,但事实上可能需要沿着纹理的一个轴进行缩小,然后沿着另一个轴进行放大。访问mipmap的效果是检索纹理上的正方形区域;检索矩形区域是不可能的。为了避免混叠,我们选择纹理上像素单元的近似覆盖率的最大度量。这导致检索到的样本通常比较模糊
区域面积和表 Summed-Area Table
为了不过度模糊,使用Summed-Area Table,首先创建一个与纹理大小相同的数组,但包含存储的颜色的更多精度(例如,红色、绿色和蓝色各16位或更多)。在这个数组的每个位置,必须计算和存储由这个位置和texel(0,0)(原点)组成的矩形中所有对应纹理的texel的和。在纹理处理过程中,像素单元在纹理上的投影被一个矩形绑定。然后访问sum-area表来确定这个矩形的平均颜色,该颜色作为像素的纹理颜色传递回来;根据下图计算矩形的纹理坐标计算平均值
然后使用公式
其中,x和y是矩形的texel坐标, 是对该texel的区域求和值,。这一等式会将右上角的整体区域的和,减去区域A和区域B。区域C会减去两次,因此需要加上一次
结果如下图,到地平线的线条在右边边缘附近更清晰,但中间的对角线仍然模糊。问题在于,当沿着纹理的对角线查看纹理时,会生成一个大矩形,许多纹理位于像素附近的地方都被计算
求和面积表是所谓的各向异性过滤(anisotropic fifiltering)算法的一个示例。此类算法在非正方形区域上检索纹理像素值。但是,SAT能够在主要水平和垂直方向上最有效地做到这一点。 但是,SAT能够在主要水平和垂直方向上最有效地做到这一点。 还要注意,对于面积为16×16或更小的纹理,求和区域表至少需要两倍的内存,而较大的纹理则需要更高的精度。
汇总面积表可以在现代GPU上实现,并以合理的整体内存成本提供更高的质量。改进的过滤对于高级渲染技术的质量至关重要。例如,Hensley等人提供了一种有效的实现方式,并展示了总面积采样如何改善光泽反射。可以通过SAT改进面积采样的其他算法,例如景深,阴影图和模糊反射
无约束各向异性滤波Unconstrained Anisotropic Filtering
对于当前的图形硬件,进一步改善纹理过滤的最常用方法是重用现有的mipmap硬件;其基本思想是将像素单元反投影,然后对纹理上的这个四边形(quad)进行多次采样,并将这些采样组合在一起。如上所述,每个mipmap示例都有一个位置和一个与之相关的方形区域。该算法不是使用单一的mipmap样本来近似这个四边形的覆盖范围,而是使用几个正方形来覆盖这个四边形。短边的四分之一可以用来确定d(不像在mipmapping中,通常使用较长的边);这使得每个mipmap样本的平均面积更小(因此更不模糊)。四边形的长边用于创建一条各向异性线,平行于长边并穿过四边形的中间;当各向异性的数量在1:1和2:1之间时,沿着这条线取两个样本
在各向异性比例较高的情况下,沿轴取的样品较多。该方案允许各向异性线在任意方向运行,不受面积和表的限制。它也不需要比mipmap更多的纹理内存,因为它使用mipmap算法进行采样,各向异性滤波如下图
体积贴图 Volume Textures
纹理图像是一个带扩展的三维纹理数据,可以通过(u,v,w)访问;大多数的GPU对于体积纹理支持mipmapping。由于在一个体积纹理的单个mipmap级别内的滤波包含三次线性插值,在mipmap等级间的滤波要求四次线性插值。由于这包含平均来自16个纹素的值,因此可能导致精度问题,可以使用更高精度的体积纹理来解决;尽管体积纹理由更高的存储要求,且滤波的话开销更大,但它们仍有一些独特的优势。找到一个三维网格的一个良好的二维参数化的复杂的过程可以跳过,因为三维位置可以直接作为纹理坐标使用。这避免了二维参数化过程中常会出现失真和接缝问题。一个体积纹理也可以用来表示例如木材或大理石材质的体积结构。一个使用这样纹理的模型就像是从该材质雕刻出来的。对于表面纹理映射使用体积纹理是非常低效的,因为采样的最大的优势没有被使用。Benson和Davis和DeBry等人讨论了使用系数八叉树结构的存储纹理数据的方法。这一模式非常适配交互式三维纹理绘制系统,同时表面并不需要在创建时明确纹理坐标,八叉树可以在任何级别下都能保持细节。
立方体贴图 Cube Maps
有六个矩形纹理,每个纹理对应一个面称为立方体贴图;立方体贴图可以使用一个三维纹理坐标矢量来采样,它可以通过一个从立方体中心设想外部的射线的方向来标识。射线与立方体交叉的点被找到,然后做接下来的工作。纹理坐标的最大坐标可以用来选取相关联的面(如(-3.2,5.1,-8.4)对应-z面)。剩余的两个坐标会除以最大坐标值的绝对值。现在坐标的范围为-1到1,然后可以简单的映射到[0,1]用于计算纹理坐标,常用于环境映射
Texture Representation
通过纹理图集,纹理数组和无绑定纹理,可以提高应用程序处理纹理性能
为了能够为GPU分配尽可能多的工作,通常最好更改状态尽可能少;为此,可以将多个图像放入一个较大的纹理中,称为纹理图集
注意,子纹理的形状可以是任意的,如下图所示。 Néoll和Stricker介绍了子纹理放置图集的优化。由于mipmap的上层可能包含几个单独的不相关的形状,因此在生成和访问mipmap时也需要格外小心。Manson和Schaefer提出了一种通过考虑表面的参数化来优化mipmap创建的方法,该方法可以产生更好的结果。Burley和Lacewell提出了一个称为Ptex的系统,其中细分曲面中的每个四边形都有自己的小纹理。优点是,这避免了在网格上分配唯一的纹理坐标,并且在纹理图集的不连续部分的接缝处没有瑕疵。为了能够跨四边形进行过滤,Ptex使用了邻接数据结构。当最初的目标是生产渲染时,Hillesland提出了packed Ptex,它将每个面的子纹理放入纹理图集中,并使用相邻面的填充来避免过滤时的间接作用。 Yuksel提出了网格颜色纹理,该纹理在Ptex的基础上有提升。 Toth提供了更好质量的跨位面过滤对于类似于 Ptex 系统,通过实现了一个方法,如果值的超出了 的范围,那么就在过滤开始的时候舍弃它。
使用图集的一个困难是 wrapping/repeat 和 镜像模式(mirror modes),这不会适当地影响子纹理,而只会影响整个纹理。为图集生成Mipmap时,可能会发生另一个问题,其中一个子纹理会渗入另一个子纹理。但是,可以通过以下方式避免这种情况:在将每个子纹理放入大型纹理图集之前,分别为每个子纹理生成mipmap层次结构,并对子纹理使用2的幂次方分辨率。
解决这些问题的一种更简单的解决方案是使用一种称为纹理数组texture arrays的API构造,该构造完全避免了mipmapping和重复模式的任何问题。可以看下图的右半部分。纹理数组中的所有子纹理都必须具有相同的尺寸,格式,mipmap层次结构和MSAA设置。就像纹理图集一样,仅对纹理数组执行一次设置,然后可以使用着色器中的索引访问任何数组元素。 这比绑定每个子纹理快5倍。
API也支持无绑定纹理,这也可以帮助避免状态更改成本。如果没有无绑定纹理,则使用API将纹理绑定到特定的纹理单元。一个问题是纹理单元数量的上限,这会使程序员感到麻烦。 驱动程序确保纹理位于GPU端。 使用无绑定纹理时,纹理数量没有上限,因为每个纹理仅由一个64位指针(有时称为“句柄”)与其数据结构相关联。可以通过许多不同方式来访问这些句柄,例如,通过uniforms,通过变化的数据,来自其他纹理或从着色器存储缓冲区对象( shader storage buffffer object)(SSBO)。应用程序需要确保纹理驻留在GPU端。 无绑定纹理避免了驱动程序中的任何类型的绑定成本,从而使渲染速度更快。
Texture Compression
直接攻击(attacks)内存和带宽问题以及缓存问题的一种解决方案是固定速率纹理压缩(texture compression)。通过让GPU即时解码压缩的纹理,纹理可以需要更少的纹理内存,因此可以有效的增加缓存大小。至少同样重要的是,此类纹理的使用效率更高,因为它们在访问时消耗的内存带宽更少。一个相关但不同的用例是添加压缩以提供更大的纹理。 例如,在 分辨率下每纹理像素使用3个字节的非压缩纹理将占用768 kB。 使用纹理压缩时,压缩比为6:1, 纹理将仅占用512 kB。
在图像文件格式(例如JPEG和PNG)中使用了多种图像压缩方法,但是在硬件中实现这些方法的解码成本很高(有关纹理转码的资料,请参阅第19.10.1节)。S3开发了一种称为S3纹理压缩(S3 Texture Compression)(S3TC)的方案,该方案被选作DirectX的标准,并称为DXTC。在DirectX 10中,它称为BC(用于块压缩(for Block Compression))。此外,它是OpenGL中的事实上的标准,因为几乎所有GPU都支持它。它的优点是可以创建固定大小的压缩图像,具有独立编码的片段,并且解码简单(因此快速)。图像的每个压缩部分都可以独立处理。 没有共享的查找表或其他依赖项,从而简化了解码。
DXTC / BC压缩方案有七个变体,它们具有一些共同的属性。编码是在4×4 纹素块(也称为图块(tiles))上完成的。 每个块被单独编码。编码基于插值。 对于每个编码量,存储两个参考值(例如,颜色)。将为该块中的16个纹素的每一个保存一个插值因子。它沿着两个参考值之间的直线选择一个值,例如,一个颜色等于或从两种存储的颜色插值。压缩来自仅存储两种颜色中每个像素较短的索引值。
确切的编码在这七个变体之间有所不同,下图总结了这些变体。请注意,“ DXT”表示DirectX 9中的名称,“ BC”表示DirectX 10及更高版本中的名称。 从表中可以看出,BC1具有两个16位参考RGB值(5位红色,6个绿色,5个蓝色),每个纹理像素具有2位插值因子,可以从参考值之一或两个值的中间值进行选择。与未压缩的24位RGB纹理相比,这表示6:1的纹理压缩率。BC2以与BC1相同的方式对颜色进行编码,但是每个texel(bpt)添加4位以用于量化(原始)alpha。 BC2以与BC1相同的方式对颜色进行编码,但是每个texel(bpt)添加4位以用于量化(原始)alpha。 对于BC3,每个块都具有与DXT1块相同的RGB数据编码。另外,使用两个8位参考值和每个像素3位插值因子对alpha数据进行编码。每个纹理像素可以选择参考alpha值或六个中间值之一。BC4有一个通道,在BC3中编码为alpha。 BC5包含两个通道,每个通道都像BC3中那样进行编码。
BC6H用于高动态范围(HDR)纹理,其中每个纹理像素最初每个R,G和B通道都有16位浮点值。 此模式使用16个字节,导致8 bpt。它具有用于单行的一种模式(类似于上述技术),以及用于两行的另一种模式,其中每个块可以从一小组分区中进行选择。两种参考颜色也可以进行增量编码以获得更高的精度,并且根据所使用的模式,它们也可以具有不同的精度。在BC7中,每个块可以包含一到三行,并存储8 bpt。目标是对8位RGB和RGBA纹理进行高质量的纹理压缩。 它与BC6H共享许多属性,但是是LDR纹理的格式,而BC6H是HDR的格式。 请注意,在OpenGL中,BC6H和BC7分别称为BPTC FLOAT和BPTC。 这些压缩技术可以应用于立方体或体积纹理以及二维纹理。
这些压缩方案的主要缺点是它们是有损的。 也就是说,通常无法从压缩版本中取回原始图像。对于BC1-BC5,仅使用四个或八个插值来表示16个像素。 如果图块中包含大量不同的值,则会有一些损失。 实际上,如果正确使用,这些压缩方案通常会提供可接受的图像保真度。
BC1-BC5的问题之一是,用于块的所有颜色都位于RGB空间中的直线上。 例如,红色,绿色和蓝色不能在一个块中表示。 BC6H和BC7支持更多的线路,因此可以提供更高的质量。
对于OpenGL ES,选择了另一种称为Ericsson纹理压缩(Ericsson texture compression)(ETC)的压缩算法以包含在API中。该方案具有与S3TC相同的功能,即快速解码,随机访问,无间接查找和固定速率。它将4×4像素的块编码为64位,即每个texel使用4位。基本思想如图下图所示。每个2×4块(或4×2块,取决于提供最佳质量的块)存储基色。每个块还从一个小的静态查找表中选择一组四个常量,并且块中的每个纹理像素都可以选择添加此表中的值之一。 这修改了每个像素的亮度。 图像质量与DXTC相当。
在OpenGL ES 3.0中包含的ETC2中,将未使用的bit联合起来,然后使用它们为原始的ETC算法添加更多的模式。未使用的bit组合是压缩表示(例如64位),其被解压缩为与另一压缩表示相同的图像。例如,在BC1中,将两个参考颜色设置为相同是没有用的,因为这将指示常量的色块,而这反过来又可以获得,只要一个参考颜色包含那种恒定的颜色。在ETC中,还可以从带有标志号的第一种颜色对一种颜色进行增量编码,因此计算可能会上溢或下溢。这种情况被用来表示其他压缩模式。 ETC2添加了两个新模式,每个块具有四种颜色(以不同方式派生),而最终模式是RGB空间中的一个平面,用于处理平滑过渡。Ericsson Alpha压缩(Ericsson alpha compression)(EAC)压缩具有一个分量(例如Alpha)的图像。这种压缩类似于基本的ETC压缩,但仅用于一个分量,因此生成的图像每个texel存储4位。可以选择将其与ETC2结合使用,此外,可以使用两个EAC通道来压缩法线(有关此主题的更多信息,请参见下文)。所有ETC1,ETC2和EAC都是OpenGL 4.0核心配置文件,OpenGL ES 3.0,Vulkan和Metal的一部分。
压缩法线贴图需要格外小心。 专为RGB颜色设计的压缩格式通常没办法很好的作用在 xyz 数据。大多数方法都利用这样的事实,即已知法线为单位长度,并进一步假定其z分量为正(对切线空间法线的合理假设)。 这仅允许存储法线的x和y分量。 z分量是动态导出的:
由于仅存储两个分量,而不是三个,因此它本身会导致较少的压缩。由于大多数GPU本身都不支持三组件纹理,因此这也避免了浪费组件的可能性(或必须在第四组件中包装其他数量的组件)。 通常通过将x和y分量存储在BC5 / 3Dc格式的纹理中来实现进一步的压缩。参见下图。由于每个块的参考值划分了最小和最大x和y分量值,因此可以将它们视为在xy平面上定义边界框。 three-bit插值因子允许在每个轴上选择八个值,因此将边界框划分为8×8的可能法线网格。或者,可以使用两个EAC通道(分别用于x和y),然后按照上述定义计算z。
在不支持BC5 / 3Dc或EAC格式的硬件上,常见的后备方法是使用DXT5格式的纹理并将两个组件存储在绿色和alpha组件中(因为这些组件的存储精度最高)。 其他两个组件未使用。
PVRTC是一种纹理压缩格,式,提供于叫做 PowerVR的 Imagination Technologies’硬件上,其最广泛的用途是用于iPhone和iPad。它为每个纹理像素提供2位和4位的方案,并压缩4×4纹理像素的块。关键思想是提供图像的两个低频(平滑)信号,这些信号是使用相邻的纹素数据块和插值获得的。 然后,每像素的1或2位用于在图像上的两个信号之间进行插值。
自适应可伸缩纹理压缩(Adaptive scalable texture compression)(ASTC)的不同之处在于,它将n×m纹理像素的块压缩为128位。块大小从4×4到12×12不等,这导致了不同的比特率,从每texel低至0.89位到每texel高达8位。ASTC使用多种技巧来实现紧凑的索引表示,并且每个块可以选择行数和端点编码。另外,ASTC可以处理每个纹理1至4个通道以及LDR和HDR纹理。 ASTC是OpenGL ES 3.2及更高版本的一部分。
上面介绍的所有纹理压缩方案都是有损的,并且在压缩纹理时,可以在此过程上花费不同的时间。在压缩上花费数秒甚至数分钟,就可以获得更高的质量;因此,这通常是作为脱机预处理完成的,并存储起来供以后使用。 或者,一个只能花费几毫秒的时间,结果质量较低,但是纹理可以实时压缩并立即使用。 一个例子是天空盒,每隔一秒左右就会重新生成一次,此时云层可能已经略微移动了。 解压过程非常快,因为它是使用固定功能的硬件完成的。 这种差异称为数据压缩不对称(data compression asymmetry),其中压缩可以而且确实比解压缩花费更长的时间。
Kaplanyan提出了几种可以改善压缩纹理质量的方法。对于包含颜色的纹理和法线贴图,建议使用每个组件16位的方式创作贴图。对于彩色纹理,然后执行直方图重归一化(histogram renormalization)(在这16位上),然后使用着色器中的比例和偏置常数(每个纹理)反转其效果。直方图归一化是一种将图像中使用的值扩展到整个范围的技术,这实际上是一种对比度增强。 每个分量使用16位可确保在重新归一化后直方图中没有闲置的时隙,这减少了许多纹理压缩方案可能引入的条带失真。 如下图所示。
此外,如果75%的像素高于116/255,Kaplanyan建议对纹理使用线性颜色空间,否则将纹理存储在sRGB中。对于法线贴图,他还指出BC5 / 3Dc通常独立于y来压缩x,这意味着并非总能找到最佳法线。 相反,他建议对法线使用以下误差度量:
其中n是原始法线,nc是相同的法线压缩,然后解压。
应当注意,也可以在不同的颜色空间中压缩纹理,这可以用来加速纹理压缩。 常用的变换是RGB→YCoCg:
其中,Y是亮度项,Co和Cg是色度项。 逆变换也很简单:
这相当于增加了一些。这两个变换是线性的,这可以从公式6.6中看出,它是矩阵向量乘法,其本身是线性的。这很重要,因为可以存储YCoCg,而不是将RGB存储在纹理中。纹理硬件仍可以在YCoCg空间中执行过滤,然后像素着色器可以根据需要转换回RGB。应该注意的是,这种变换本身是有损的,但是这可能很重要,也可能不重要。
还有另一个可逆的RGB→YCoCg变换,总结为
》向右移动。这意味着可以在例如24位RGB颜色和相应的YCoCg表示之间进行来回转换而不会造成任何损失。应该注意的是,如果RGB中的每个分量都具有n位,则Co和Cg均具有n +1位,以确保可逆变换;Y虽然只需要n位。Van Waveren和Castano使用有损YCoCg变换在CPU或GPU上实现对DXT5 / BC3的快速压缩。它们将Y存储在alpha通道中(因为它具有最高的精度),而Co和Cg存储在RGB的前两个分量中。由于Y是分别存储和压缩的,因此压缩变得很快。对于Co-和CG-组件,他们找到一个二维包围框,并选择产生最佳结果的框对角线。请注意,对于在CPU上动态创建的纹理,最好也在GPU上进行压缩。 通过在GPU上渲染来创建纹理时,通常最好也在GPU上进行压缩。 YCoCg变换和其他亮度色度变换通常用于图像压缩,其中色度分量在2×2像素上平均。 这减少了50%的储存,而且通常效果很好,因为色度变化很慢。Lee-Steere和Harmon进一步采取这一步骤,将其转换为色相饱和度值(HSV),在x和y中,色相和饱和度的下采样系数为4,并将值存储为单通道DXT1纹理。Van Waveren和Castano也描述了法线图压缩的快速方法。
程序纹理Procedural Texturing
求一个函数的值,从而生成一个程序纹理,考虑到体积图像纹理的高存储成本,体积纹理对于程序纹理是特别有吸引力的应用,这种纹理可以通过多种技术合成,最常见的一种是使用一个或多个噪声函数来生成值 ;噪声函数通常以连续的2的幂频率进行采样,称为倍频程。每个倍频程都有一个权重,通常随着频率的增加而下降,这些加权样本的总和称为湍流函数
由于评估噪声函数的成本,三维阵列中的格点通常被预先计算并用于内插纹理值,有多种方法使用颜色缓冲混合来快速生成这些阵列,其他程序方法也是可能的。例如,通过测量从每个位置到散布在空间中的一组“特征点”的距离来形成细胞纹理。以各种方式映射产生的最近距离,例如,改变颜色或阴影法线,创建看起来像细胞、石板、蜥蜴皮和其他自然纹理的图案;另一种类型的程序纹理是物理模拟或其他交互过程的结果,如水波纹或蔓延的裂缝。在这种情况下,程序纹理可以在对动态条件的反应中有效地产生无限的可变性当生成程序性二维纹理时,参数化问题可能会比创作的纹理带来更多的困难,在创作的纹理中,可以手动修改或处理拉伸或接缝伪影。一种解决方案是通过直接在表面上合成纹理来完全避免参数化。在复杂的表面上进行这种操作在技术上具有挑战性,并且是一个活跃的研究领域;抗锯齿程序纹理比抗锯齿图像纹理更难也更容易。一方面,预计算方法(如mipmapping)不可用,给程序员增加了负担。另一方面,程序纹理作者拥有关于纹理内容的“内部信息”,因此可以对其进行裁剪以避免走样。对于通过对多个噪波函数求和而创建的程序纹理来说尤其如此。每个噪声函数的频率都是已知的,因此可以丢弃任何会导致混叠的频率,从而降低计算成本。有多种技术可以反走样其他类型的程序纹理
纹理动画Texture Animation
纹理坐标也不必是静态的,可以在网格数据本身中或通过顶点或像素着色器中应用的函数来显式地更改帧与帧之间的纹理坐标;通过将矩阵应用于纹理坐标,可以创建更精细的效果。除了平移,这允许线性变换,例如缩放、旋转和剪切,图像扭曲和变形变换,以及广义投影。通过在CPU或着色器中应用函数,可以创建更多精细的效果;通过使用纹理混合技术,可以实现其他动画效果
材料映射Material Mapping
纹理的一个常见用途是修改影响着色方程的材质属性;像素着色器可以从纹理中读取值,并在评估着色方程之前使用它们来修改材质参数。纹理最常修改的参数是表面颜色。这种纹理被称为反照率颜色贴图或漫反射颜色贴图。然而,任何参数都可以被纹理修改:替换它,乘以它,或者以其他方式改变材质表现
纹理可以用来控制像素着色器本身的流程和功能,而不是修改方程中的参数。通过让一个纹理指定表面的哪些区域具有哪种材料,可以将具有不同着色方程式和参数的两种或多种材料应用于表面,从而为每个区域执行不同的代码
着色模型输入(如表面颜色)与着色器的最终颜色输出具有线性关系。因此,包含这种输入的纹理可以用标准技术过滤,并且避免了锯齿。包含非线性阴影输入的纹理,如粗糙度或凹凸贴图,需要多加小心以避免走样。考虑到着色方程的过滤技术可以改善这种纹理的结果
透明度映射Alpha Mapping
使用alpha混合或alpha测试,alpha值可以用于许多效果
一种与纹理相关的效果是贴花,通过给一个texel指定一个0的alpha值,使它透明让其失效,即通过正确设置贴花纹理的alpha,可以用贴花替换或混合基础曲面;具体是首先用场景渲染帧缓冲区,然后渲染一个框,对于框内的所有点,贴图纹理被投影到帧缓冲区内容。最左边的texel是完全透明的,所以它不会影响帧缓冲区。黄色texel不可见,因为它会被投影到表面的隐藏部分
凹凸贴图Bump Mapping
本节描述了一大类我们统称为bump mapping的小规模细节表示技术。所有这些方法通常都是通过修改每个像素的着色例程来实现的。它们比单独的纹理映射提供了更三维的外观,但没有添加任何额外的几何形状
本节描述了一大类小型细节表示技术,我们将其统称为凹凸贴图(bump mapping)。所有这些方法通常都是通过修改每个像素的着色例程来实现的。与单独的纹理贴图相比,它们具有更三维的外观,但没有添加任何其他几何形状。
一个对象的细节可以分为三个等级:覆盖许多像素的宏观特征( macro-features ),横跨几个像素的细节特征(meso-features)和实质上小于像素的微观特征( micro-features)。这些类别可能会相互切换,因为在动画或交互式会话期间,观看者可能在许多距离处观察到同一对象。
宏观几何由顶点和三角形或其他几何图元表示。 创建三维角色时,通常以宏观尺度对肢体和头部进行建模。微几何封装在着色模型中,该模型通常在像素着色器中实现,并使用纹理贴图作为参数。所用的着色模型可模拟表面微观几何形状的相互作用,例如,发光物体在微观上是光滑的,而漫射表面在微观上是粗糙的。角色的皮肤和衣服具有不同的材质,因为它们使用不同的着色器,或者至少在这些着色器中的使用不同参数。
细观几何描述了这两个尺度之间的一切。它包含的细节过于复杂,无法使用单个三角形进行有效渲染,但是对于观察者来说,它足够大所以可以分辨出几个像素上的表面曲率变化。角色脸上的皱纹,肌肉的细节以及衣服上的褶皱和接缝都是中等大小的。中尺度建模通常使用一类统称为凹凸贴图技术的方法。这些以像素级别调整着色参数的方式,使观看者的感觉远离基本几何形状的微小干扰,而基本几何形状实际上保持平坦。不同种类的凹凸贴图之间的主要区别是它们如何表示细节特征。变量包括真实水平和细节特征的复杂性。例如,数字艺术家通常将细节雕刻到模型中,然后使用软件将这些几何元素转换为一种或多种纹理,例如凹凸纹理和缝隙变暗的纹理。
Blinn于1978年提出在纹理中编码中尺度细节的想法。 他观察到,如果在着色过程中用稍微扰动的表面法线代替真实的表面,则表面似乎具有小范围的细节。 他将描述微扰表面法线的数据存储在数组中。
关键思想是,我们不使用纹理来更改照明方程式中的颜色分量,而是访问纹理来修改表面法线。表面的几何法线保持不变。 我们只修改照明方程式中使用的法线。此操作没有物理等效项。 我们在曲面法线上执行更改,但曲面本身在几何意义上保持平滑。 正如每个顶点具有法线会给人一种错觉,即三角形之间的表面是光滑的一样,修改每个像素的法线会更改三角形表面本身的感知,而无需更改其几何形状。
对于凹凸贴图,法线必须相对于某个参考系改变方向。为此,将切线框架(也称为切线空间基础)存储在每个顶点上。该参考系用于将灯光转换到曲面位置的空间(反之亦然),以计算干扰法线的效果。 除了顶点法线外,在多边形表面上应用了法线贴图的情况下,我们还存储了切线(tangent)和副切线向量(bitangent vectors)。 双切线向量也被错误地称为双法线向量(binormal vector)。
切线和双切线向量表示法线贴图本身在对象空间中的轴,因为目标是将光线变换为相对于贴图
法线n,切线t和双切线b这三个向量形成基本矩阵:
这个矩阵有时缩写为TBN,它将光的方向(对于给定的顶点)从世界空间转换为切线空间。这些向量不必彼此真正垂直,因为法线贴图本身可能会变形以适合曲面。但是,非正交基础会导致纹理倾斜,这可能意味着需要更多存储空间,并且可能会对性能产生影响,即矩阵随后无法通过简单的转置来反转。一种节省内存的方法是只在顶点存储切线和切线,并取它们的叉积来计算法线。但是,只有在矩阵的手性始终(左右手坐标系)相同的情况下,此技术才有效。通常,模型是对称的:飞机,人,文件柜和许多其他对象。由于纹理消耗大量内存,因此它们通常被镜像到对称模型上。 因此,仅存储对象纹理的一侧,然后纹理映射将其放置在模型的两侧。在这种情况下,切线空间的手性在两侧会有所不同,因此无法假定。如果在每个顶点上存储了额外的信息位来指示手性,则在这种情况下仍然可以避免存储法线。如果已设置,则此位用于求反切线和双切线的叉积,以生成正确的法线。如果切线框架是正交的,则还可以将基础存储为四元数,这两者都更节省空间,并且每个像素可以节省一些计算。尽管在实践中很少见,但质量可能会略有下降。
切线空间的概念对于其他算法很重要。如下一章所述,许多着色方程式仅依赖于曲面的法线方向。 但是,诸如铝或天鹅绒之类的材料也需要知道观看者和照明相对于表面的相对方向。切线框架可用于定义材质在表面上的方向。 Lengyel和Mittring的文章广泛报道了这一领域。Schuler 提出了一种在像素着色器中动态计算切线空间的方法,而无需为每个顶点存储预先计算的切线架构。Mikkelsen对这项技术进行了改进,并得出了一种不需要任何参数化的方法,而是使用表面位置的导数和高度场的导数来计算扰动法线的方法。但是,与使用标准切线空间映射相比,此类技术所导致的显示细节要少得多,并且可能会产生美术工作流问题。
Blinn’s Methods
Blinn最初的凹凸贴图方法在纹理的每个纹理像素上存储两个有符号的值 bu 和 bv。这两个值对应于沿u和v图像轴改变法线的量。也就是说,这些通常是双线性插值的纹理值用于缩放垂直于法线的两个向量。将这两个向量添加到法线以更改其方向。两个值bu和bv描述了曲面在该点处面向的方向。具体看下图。这种类型的凹凸贴图纹理称为偏移矢量凹凸贴图(offffset vector bump map)或偏移贴图(offffset map)
表示凹凸的另一种方法是使用高度场(heightfifield)来修改表面法线的方向。每个单色纹理值代表一个高度,因此在纹理中,白色是高区域,黑色是低区域(反之亦然)。具体看下图。这是首次创建或扫描凹凸贴图时使用的通用格式,也是Blinn在1978年引入的格式。与第一种方法中使用的思想类似,该高度域用于导出u和v带符号的值。这是通过获取相邻列之间的差异来获得u的斜率,以及获取相邻行之间的差异来实现v的。一种变体是使用Sobel滤波器,该滤波器赋予直接相邻的邻居更大的权重。
法线贴图 Normal Mapping
凹凸贴图的常见方法是直接存储法线贴图(normal map)。 算法和结果在数学上与Blinn的方法相同; 只有存储格式和像素着色器计算会更改。
法线贴图对映射到[-1,1]的(x,y,z)进行编码,例如,对于8位纹理,x轴值0表示-1.0,255表示1.0。 下图给出了一个示例。 颜色[128,128,255](浅蓝色)将代表所示颜色映射的平坦表面,即法线为[0,0、1]
法线图表示最初是作为世界空间法线图引入的,在实践中很少使用。对于这种类型的贴图,扰动非常简单:在每个像素处,从贴图中检索法线,然后将其与光的方向一起直接使用,以计算表面上该位置的着色。还可以在对象空间中定义法线贴图,以便可以旋转模型,然后法线仍然有效。然而,world and object-space表示都将纹理绑定到特定方向的特定几何图形上,这限制了纹理重用.
取而代之的是,通常在切线空间(即相对于曲面本身)中检索扰动的法线。这允许表面变形以及正常纹理的最大重复使用。切线空间法线贴图也可以很好地压缩,因为通常可以假定z分量的符号(与未扰动表面法线对齐的)为正。
与过滤颜色纹理相比,过滤法线贴图是一个难题。通常,法线和着色颜色之间的关系不是线性的,因此标准过滤方法可能会导致令人反感的混叠。想象一下看楼梯由闪亮的白色大理石块制成。在某些角度下,楼梯的顶部或侧面可以捕捉光线并反射出明亮的镜面高光。但是,楼梯的平均法线假设是45度角。它会从与原始楼梯完全不同的方向捕获亮点。如果在没有正确过滤的情况下渲染具有尖锐镜面高光的凹凸贴图,则由于运气原因的高光闪烁,会产生分散注意力的闪光效果。
Lambertian曲面是一种特殊情况,其中法线贴图对着色几乎具有线性影响。Lambertian着色几乎完全是一个点积,它是线性运算。平均一组法线并对其结果执行点积等于将单个点积与法线平均:
请注意,平均向量在使用前未标准化。上面公式表明,对于Lambertian曲面,标准过滤和mipmap几乎可以产生正确的结果。由于Lambertian着色方程不是点积,因此结果不太正确。它是一个clamp点乘积——max(l·n,0)。clamp操作使其变为非线性。 这会使表面变得过于黑暗,使其能看清光线的方向,但是在实践中,这通常并不令人讨厌。一个警告是,通常用于法线贴图的某些纹理压缩方法(例如从其他两个法线重构z分量)不支持非单位长度的法线,因此使用非标准化的法线贴图可能会带来压缩困难。
在非Lambertian曲面的情况下,可以通过将着色方程的输入作为一组过滤,而不是单独过滤法线贴图来产生更好的结果。 在9.13节中讨论了这样做的技术。
最后,从高度图h(x,y)导出法线图可能很有用。 如下进行。 首先,使用中心差计算x和y方向上的导数的近似值:
然后,在texel(x,y)处的未归一化法线。
必须注意纹理的边界。通过使凹凸能够将阴影投射到其自身的表面上,可以使用“水平贴图”进一步增强法线贴图。这是通过预先计算其他纹理(每个纹理与沿着表面平面的方向相关联)并为每个纹理像素存储该方向上的地平线角度来完成的。
视差映射 Parallax Mapping
凹凸和法线贴图的一个问题是凹凸不会随着视角移动位置,也不会相互遮挡。例如,如果你沿着真实的砖墙看,从某个角度你不会看到砖与砖之间的灰泥。墙壁的凹凸贴图永远不会显示这种类型的遮挡,因为它只是改变了法线情况。更好的方法是让凹凸实际影响表面上的哪个位置在每个像素处呈现。
视差映射的概念由Kaneko[851]于2001年提出,Welsh[1866]对其进行了改进和推广。视差是指当观察者移动时,物体的位置相对移动。当观众移动时,这些凸起应该看起来有高度。视差映射的关键思想是通过检查发现的可见点的高度,对像素中应该看到的内容进行有根据的猜测
对于视差贴图,凹凸点存储在一个高度场纹理中。当查看给定像素处的表面时,会在该位置检索高度场值,并用于移动纹理坐标以检索表面的不同部分。移动的量是基于获取的高度和眼睛到表面的角度。参见图6.37。高度场值要么存储在单独的纹理中,要么打包在其他纹理的未使用颜色或alpha通道中(打包不相关的纹理时必须小心,因为这会对压缩质量产生负面影响)。在使用高度场值移动坐标之前,会对其进行缩放和偏移。这个比例尺决定了高度场在地表之上或之下延伸的高度,而偏差给出了“海平面”的高度,在这个高度上不会发生位移。给定纹理坐标位置p,调整高度h,高度值vz和水平分量vxy的归一化视图向量v,新的平行调整纹理坐标
纹理灯光 Textured Lights
纹理也可以用来增加光源的视觉丰富性,并允许复杂的强度分布或聚光灯功能。对于所有的光照都限制在一个锥体或锥体上的光,投射纹理可以用来调节光的强度[1192,1597,1904]。这允许使用形状的聚光灯、有图案的灯,甚至是“幻灯机”效果。这些灯通常被称为gobo或cookie灯,以专业剧院和电影照明中使用的剪纸的术语命名
|
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有账号?立即注册
×
|