Real-Time Rendering 4th 译文《六 纹理(中)》

6.4 纹理动画

应用于表面的图像不必是静态的。例如,视频源就可以用作逐帧变化的纹理。

纹理坐标也不必是静态的。应用程序设计人员可以在网格的数据本身中,或通过应用在顶点或像素着色器中的函数,在帧与帧之间显式地更改纹理坐标。想象一下,假设我们已经对瀑布进行了建模,并用看起来像落水的图像对其进行了纹理处理。我们假定 v 坐标是流向。为了使水运动,必须在每个连续帧的 v 坐标中减去一个量。从纹理坐标中减去的效果就是使纹理本身看起来向前移动。

通过将矩阵变换应用于纹理坐标可以创建更精细的效果。除了平移之外,还允许进行线性变换,例如缩放,旋转和剪切 [1192,1904],图像扭曲和变形变换[1729] 以及广义投影[638]。通过在CPU或着色器中应用功能,可以创建更多精致的效果。

通过使用纹理混合技术,可以实现其他动画效果。例如,通过从大理石质地开始并以肉色进行褪色,可以使雕像变为活物[1215]。


6.5 材质映射

纹理的常见用途是修改影响着色方程的材质属性。现实世界中的对象通常具有在其表面上变化的材质属性。为了模拟此类对象,像素着色器可以从纹理读取值,并在计算着色方程之前使用它们修改材质参数。最常被纹理修改的参数是表面颜色。这种纹理称为反照率颜色图或漫反射颜色图。但是,可以通过纹理修改任何参数:替换,相乘或以其他方式更改它。例如,在图 6.25 中,将三个不同的纹理应用于表面,以替换常量值。

图 6.25. 金属砖和砂浆。右侧是表面颜色,粗糙度(较浅)的凹凸贴图和凹凸贴图高度(较浅)的纹理。(图片来自 three.js 示例 webgl 色调映射 [218]。) 

在材质中纹理的使用可以更进一步。代替修改方程式中的参数,可以使用纹理来控制像素着色器本身的流和功能。通过使一种纹理指定表面的哪些区域具有哪种材质,可以将具有不同着色方程式和参数的两种或多种材质应用于表面,从而为每种材质执行不同的代码。例如,具有一些生锈区域的金属表面可以使用纹理来指示生锈的位置,根据纹理查找有条件地执行着色器的生锈部分,否则执行闪亮的金属着色器(第 9.5.2 节)。

着色模型输入(例如表面颜色)与从着色器输出的最终颜色具有线性关系。因此,可以使用标准技术对包含此类输入的纹理进行滤波,并避免走样。包含非线性着色输入(例如粗糙度或凹凸贴图)的纹理(第 6.7 节)需要多加注意,以避免出现走样。着色方程的滤波技术可以改善此类纹理的结果。这些技术在 9.13 节中讨论。


6.6 透明映射

一种与纹理相关的效果是贴花(decaling)。例如,假设你想在茶壶上放一朵花的图片。你不想要整个图片,而只想要花所在的部分。通过将 0 的 alpha 分配给纹理元素,可以使其透明,从而使其无效。因此,通过正确设置贴花纹理的 Alpha,您可以将贴图替换或混合基础表面。通常,将夹取匹配函数与透明边框一起使用,以将贴花的单个复制(相对于重复纹理)应用于表面。图 6.26 中显示了如何实现贴花的示例。有关贴花的更多信息,请参见第 20.2 节。

 图 6.26. 一种实现贴花的方法。首先使用场景渲染帧缓冲区,然后渲染一个框,对于框内的所有点,贴花纹理都投影到帧缓冲区的内容上。最左边的纹理元素是完全透明的,因此不会影响帧缓冲区。黄色纹理像素是不可见的,因为它将被投影到表面的隐藏部分上。

一个与 Alpha 的相似的应用是制作剪切图(cutouts)。假设您制作了灌木的贴花图像,并将其应用于场景中的矩形。其原理与贴花的原理相同,除了不与下层表面齐平外,灌木丛会绘制在其背后的任何几何形状的顶部。(?)这样,您可以使用单个矩形来渲染具有复杂轮廓的对象。

在灌木丛的案例中,如果围绕它旋转相机观察,则会露馅,因为这个灌木丛没有厚度。一种解决方法是复制此灌木丛矩形并将其沿树干旋转 90 度。这两个矩形构成了廉价的三维灌木丛,有时也称为“交叉树” (cross tree)[1204],从地面上看时,这种错觉相当有效。参见图 6.27。Pelzer [1367] 讨论了使用三个剪切图代表草的类似配置。在第 13.6 节中,我们讨论了一种称为广告牌(billboarding)的方法,该方法用于将这种渲染减少为单个矩形。如果观看者移至地面上方,就会露馅,因为从上方看到灌木丛是两个剪切图。参见图 6.28。

 图6.28. 从离地面有点远的地方看“交叉树”灌木丛,然后再向上看,就露馅了。

为了解决这个问题,可以以不同的方式(切片,分支,图层)添加更多的剪切图,以提供更具说服力的模型。第 13.6.5 节讨论了一种生成此类模型的方法; 第 857 页的图 19.31 显示了另一个。有关最终结果的示例,请参见第 2 和 1049 页上的图像。(!)

将 alpha 贴图和纹理动画结合使用,可以产生令人信服的特殊效果,例如闪烁的火炬,植物生长,爆炸和大气效果。

有几种使用 alpha 贴图渲染对象的选项。Alpha 混合(第 5.5 节)允许使用带小数的透明度值,该值可以对对象边缘以及部分透明的对象进行反走样。但是,alpha 混合需要在不透明的三角形之后以从后到前的顺序渲染混合的三角形。一个简单的交叉树是两个剪切纹理(cutout textures)的示例,其中没有正确的渲染顺序,因为每个四边形位于另一个的前面。即使在理论上可以排序并获得正确的顺序,通常这样做效率也不高。例如,一块田地可能有成千上万个使用剪切图的草叶。每个网格物体可以由许多单独的叶片合成。因此,明确地分出每个叶片是不切实际的。

渲染时,可以通过几种不同的方法来改善此问题。一种是使用 Alpha 测试,它是有条件地丢弃像素着色器中具有低于给定阈值的Alpha 值的片元的过程。具体做法如下

 其中 texture.a 是纹理查找中的 alpha 值,参数 alphaThreshold 是用户提供的阈值,该阈值确定哪些片元将被丢弃。因为透明片元被丢弃了,所以该二进制的可见性测试使三角形可以以任何顺序呈现。我们通常希望对 alpha 值为 0.0 的任何片元执行此操作。丢弃完全透明的片元还有一个额外的好处,就是可以节省进一步的着色器处理和合并的成本,还可以避免将 z 缓冲区中的像素错误地标记为可见 [394]。对于剪切图,我们通常将阈值设置为高于 0.0,例如 0.5 或更高,然后采取进一步的步骤,然后完全忽略 alpha 值,而不是将其用于混合。这样做可以避免乱码。但是,由于只有两个级别的透明度(完全不透明和完全透明)可用,因此图像质量较低。另一种解决方案是对每个模型执行两次遍历,第一次遍历针对实体剪切图,写入 z 缓冲区,另一次遍历针对半透明样本,并且不写入 z 缓冲区。


alpha 测试还有两个其他问题,即过度的放大(magnification) [1374] 和过度的缩小(minification) [234,557]。当将 alpha 测试与 mipmapping 一起使用时,如果处理方式不同,效果可能令人难以信服。图 6.29 的顶部显示了一个示例,其中树的叶子变得比预期的更加透明。这可以用一个例子来解释。假设我们有一个具有四个 alpha 值的一维纹理,即(0.0,1.0,1.0,0.0)。通过平均,下一个 mipmap 级别变为(0.5,0.5),然后最高级别为(0.5)。现在,假设我们使用  = 0.75。访问 mipmap 级别 0 时, 4 个纹理像素中的 1.5 个将通过丢弃测试。但是,访问下两个级别时,由于 0.5 <0.75,所有内容将被丢弃。请参见图 6.30。

图6.29. 顶部:带有mipmapping的alpha测试,无任何更正。底部:alpha测试,其alpha值根据覆盖范围重新调整。(图片来自“见证者,The Witness”,由伊格纳西奥·卡斯塔(Ignacio Casta)提供。 

 图 6.30. 上方图片是具有混合功能的叶子图案的不同 mipmap 级别,较高的级别则进行缩放以提高可见性。在下方图片,显示mipmap,将使用 0.5 的 alpha 测试对其进行处理,以显示对象后退时如何减少像素。(图片由 Ben Golus 提供[557]。)

Casta˜no [234]提出了一种在 mipmap 创建期间完成的简单解决方案,效果很好。对于 mipmap 级别 k,覆盖率 \large c_{k} 被定义为

其中   是在 mipmap 级别 k 中的纹理像素数量,α(k,i)是在像素 i 处来自 mipmap 级别 k 的 alpha 值,而  \large \alpha _{t}是用户在公式 6.9 中提供的 alpha 阈值。在这里,我们假设 α(k,i)>的结果为1,否则为0。注意,k = 0 表示最低的 mipmap 级别,即原始图像。然后,对于每个 mipmap 级别,我们找到一个新的 mipmap 阈值\large \alpha _{k},而不是使用 \large \alpha _{t},以使  \large c_{k}等于 \large c_{0}(或尽可能接近)。这可以使用二进制搜索来完成。最后,在 mipmap 级别 k 中所有纹理像素的 alpha 值按 \large \alpha _{t}/\alpha_{k} 缩放。图 6.29 的底部使用了这种方法,NVIDIA 的纹理工具也对此提供了支持。Golus [557] 给出了一个变体,其中未修改 mipmap,但是随着 mipmap 级别的增加,alpha 会在着色器中按比例放大。

 Wyman 和 McGuire [1933] 提出了另一种解决方案,其中理论上将公式 6.9 中的代码行替换为

 

 随机函数在 [0,1] 中返回一个统一值,这意味着平均而言,它将得出正确的结果。例如,假设纹理查找的 alpha 值为 0.3,则片元将以 30% 的机会被丢弃。这是一种随机透明的形式,每个像素只有一个样本 [423]。在实践中,将随机函数替换为哈希函数,以避免时间和空间上的高频噪声:

 通过对上述函数的嵌套调用来形成三维哈希,即 float hash3D(x,y,z){return hash2D(hash2D(x,y),z); },返回[0,1)中的数字。哈希的输入是对象空间坐标除以对象空间坐标的最大屏幕空间导数(x 和 y),然后进行夹取(clamping)操作。需要进一步注意以获得 z 方向运动的稳定性,并且该方法最好与时间性反走样技术结合使用。该技术会随着距离的增加而逐渐淡入,因此在关闭时我们根本不会获得任何随机效果。这种方法的优点是每个片元平均而言都是正确的,而 Casta〜no 的方法 [234] 为每个 mipmap 级别创建单个 。但是,此值可能会在每个 mipmap 级别上有所不同,这可能会降低质量并需要美术人员干预。
 

Alpha 测试会在放大下显示涟漪人造错觉,可以通过将 Alpha 贴图预先计算为距离范围[580] 来避免(请参见第 677 页的讨论)。

Alpha 覆盖和类似的功能半透明自适应反走样会获取片元的透明度值,并将其转换为覆盖像素内的多少样本 [1250]。这个想法就像 5.5 节中描述的屏幕门透明,但这是在子像素级别。想象每个像素有四个样本位置,并且一个片元覆盖了一个像素,但是由于剪切图纹理的缘故,它的透明度为 25%(不透明度为 75%)。Alpha 覆盖模式使片元变得完全不透明,但仅覆盖了四个样本中的三个。例如,此模式可用于重叠草叶的剪切图纹理,例如[887,1876]。由于每个绘制的样本都是完全不透明的,因此最接近的叶状体将沿其边缘以一致的方式将对象隐藏在其后方。由于 Alpha 混合功能已关闭,因此无需排序即可正确地混合半透明边缘像素。

Alpha to Coverage 是反走样 Alpha 测试的好方法,但是在 Alpha 混合时会显示伪像。例如,两个具有相同 alpha 覆盖率的 alpha 混合片元将使用相同的子像素图案,这意味着一个片元将完全覆盖另一个片元,而不是与其混合。Golus [557] 讨论了使用 fwidth()着色器指令为内容提供更清晰的边缘。见图 6.31。

对于 alpha 贴图的各种使用方法,重要的是要了解双线性插值如何影响颜色值。想象一下彼此相邻的两个纹理像素:rgbα=(255,0,0,255)是纯红色,而它的近邻 rgbα=(0,0,0,2)是黑色,几乎完全透明。正好位于两个纹理像素中间的位置的rgbα 是多少? 如果简单进行插值会得出(127,0,0,128),所得的 rgb 值呈“暗红色”。但是,真正的结果实际上并不是暗的,它是一个纯红色,并且它已预乘其 Alpha。如果要插值 Alpha 值,则为了确保正确的插值,需要保证在插值之前,已被插值的颜色已经由 Alpha 进行了预乘。例如,假设我们将几乎透明的近邻设置为 rgbα=(0,255,0,2),即给出一些绿色的微小色调。并且该颜色不会与 Alpha 预乘,那么在插值时会得到结果(127,127,0,128)—— 绿色的微小色调突然将结果变为(预乘)黄色样本,这是不对的。现在先进行预乘,此近邻纹理像素的预乘版本为(0,2,0,2),在插值后,这就给出了合适的预乘结果(127,1,0,128)。这个结果更为正确,最终的预乘颜色主要是红色,而绿色则难以察觉。

双线性插值后得出的预乘结果可能导致贴花和剪切对象周围出现黑色边缘。“较暗”的红色结果被其余管线视为未相乘的颜色,并且条纹变为黑色。即使使用 Alpha 测试,此效果仍然可见。最好的办法是在完成双线性插值之前进行预乘 [490,648,1166,1813]。WebGL API支持此功能,因为颜色合成对于网页很重要。但是,双线性插值通常由 GPU 执行,并且在执行此操作之前,着色器无法完成对纹理像素值的操作。图像不会以 PNG 等文件格式进行预乘,因为这样做会失去色彩精度。在使用 Alpha 贴图时,默认情况下,这两个因素会共同导致黑边现象。一种常见的解决方法是对剪切图像进行预处理,使用从附近的不透明纹理像素衍生的颜色来绘制透明的“黑色”纹理像素 [490,685]。通常,所有半透明区域都需要以这种方式手动或自动重新上色,以避免 mipmap 级别也出现边缘问题 [295]。另外值得注意的是,在形成带有 Alpha 值的 mipmap 时,应该使用预乘值 [1933]。
 

 

 

 

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值