用谷歌co深度学习_学习笔记 --- Real-Time Rending(第二篇)

0.前序

Randy:学习笔记 --- Real-Time Rending 4th​zhuanlan.zhihu.com

直接撞上字数限制了,不得不另开文章

本文是我个人的读书笔记,或者你可以认为是把4th以我的方式翻译了一遍,重在以中文转述本文的每一章每一节都讲到了什么,方便以后深入学习时的查阅,原文中一些文章的链接/或是无法理解的地方可能会省略

本文难免存在一些理解不到位或是转述谬误的地方

欢迎各路神仙留言指正或是指教一些我不知道的点

若能不吝赐教,鄙人先谢为敬


---5.5 透明混合/Alpha 混合

对于透明物体的渲染算法,大致分为基于光线基于视图两种模式

基于光线的算法是接近PBR的,更精细的,考虑光的弯曲(折射),光的衰减,透明物体的厚度,反射率和透射变化,观察角度等因素的,将在后面的章节讨论

本节中将介绍较为简单的,基于视图的透明模式,这种模式下会根据物体的深度排序,将前面的半透明对象视为它后面的对象的颜色的 调节/衰减 器

纱门透明(screen-door transparency) 是一种通过棋盘格点阵填充颜色缓冲,实现的透明渲染方案,它将物体的透明度转为间隔填充颜色缓冲的mask,通过间隔填充不透明的覆盖像素覆盖原有内容来表现,类似的想法也被应用于剪切纹理抗锯齿中(6.6节)

通常,屏幕上的像素非常接近,以至于棋盘图案本身是不可见的,因此不会产生过大的破绽

这种方案优点是简单,透明对象可以在任何时间以任何顺序渲染呈现,无需排序处理,是一种顺序无关的透明渲染模式(Order-Independent-Transparency)

但其主要缺点是,通常只能在屏幕的一个区域上渲染一个透明对象,另外只在50%透明度时效果最好,对于其它透明度,可能产生可检测的图案(破绽图案)

stochastic transparency 随机透明是由Enderton等人[423],采用亚像素级别的 screen-door transparency与随机采样相结合的方法,这种融合过程类似AA算法

通过使用随机点阵填充来合理表现透明度,如图5.31,为了使结果合理,每个像素需要使用大量的内存来保存亚像素样本

不过吸引人的是,使用这种技术,就不再需要混合,抗锯齿,透明及其它任何导致像素覆盖的现象,只需这一种机制即可

但使用这种技术往往会产生较大的噪声/噪点

69e779ccc5c6e491e26a930877f434f4.png
图5.31 随机的透明度。产生的噪声显示在放大的区域

除上面两种方法之外,其它大多数透明混合算法,都是混合透明物体和其身后物体的颜色,为此就需要引入Alpha混合的概念

颜色缓冲&深度缓冲,除了单纯的RGB+Z数值外,还可以附带一个Alpha值用于表现物体的透明度,1.0表示完全不透明,此时物体在深度缓冲/透明排序下就会完全覆盖其身后物体的颜色,而对于0.0完全透明的物体,则会在alpha测试时直接丢弃掉该片元

透明度,可以理解为对光线的拦截替换比率,1.0完全不透明的物体,将拦截来自身后100%的光线,并替换为它自己的颜色,因此就相当于直接覆盖身后的片元

物体对于一个像素的alpha透明度影响,需要综合物体对像素的覆盖率,以及物体本身的透明度(alpha值)考虑

对于一个肥皂泡的边缘,它覆盖了3/4的像素,并具有0.1的alpha值,那么它在该像素上的alpha可以看作0.75*0.1=0.075,即对于该像素,前景肥皂泡将拦截来自背景颜色中7.5%的光线并替换为自己的颜色

如果使用MSAA,那么3/4的采样点会受到来自肥皂泡 0.1alpha 的影响

------5.5.1 混合顺序(Blending Order)

如果物体表现透明,那么它一定渲染在场景的前方,且alphe小于1.0大于0.0,被物体覆盖的每个像素会从PS中得到透明物体的RGBA数据,并使用从后向前的覆盖混合操作(over operator),将物体的RGBA与被覆盖像素的RGB颜色混合

aec9ba2dd782cb80b831ced6d7b4869e.png

其中cs是位于前方透明(源)物体的颜色,αs是透明对象的透明度,cd是混合位于后方原有像素的颜色(目标颜色),co是将透明对象放置在原场景前混合得到的颜色(从前方看到的颜色)

渲染管线中由cs和αs发起混合,得到颜色缓冲区中已存在的背景颜色cd,执行公式混合并将结果取代cd

考虑一下之前提到的光线拦截替换原理,不难理解over operator给予了我们透过半透明体看到背景的颜色混合结果,但某种意义上这并不能表现真实世界中全部的透明效果

如果一个透明物体单纯使用over operator进行表现,那么它的效果就像是一块透明的玻璃/塑料板 这种致密的材料

而对于薄纱织物一类的效果,它表现透明的原理是由于材料的空隙,因而需要使用一张alpha纹理来控制alpha在完全不透明和完透明(或接近完全透明)间变换,或是通过格点较大的 screen-door transparency 实现

17e728158bf924f8b31ca2338e1ab685.png
图5.32 薄纱织物和塑料,两种效果

另一方面over operator的效果与真实物理也有一定的出入,思考一下物体的反射和透射原理,如果一个蓝色的物体前放有红色的不透明体,透过红色不透明体的光线大多为红光,这就导致后方的蓝色物体会显得较暗

这类有关真实物理、透射率反射率应用到渲染中,将在14.5.1和14.5.2节中讨论

此外为了正确的实现over operator,如果出现多个不透明&透明物体的情况,理论上我们需要先排列覆盖所有不透明物体,定位摄像机最前方,位于所有透明物体前不透明体的颜色,再依次向前叠加所有透明体的颜色进行混合,才能得到最终的效果

对于不透明物体的叠加覆盖我们也可以使用公式5.24,因为不透明体alpha值为1.0将直接排除其后的颜色,但并不实际,一般还是用深度缓冲实现覆盖效果

主要问题在于,深度缓冲中,只能保存当前筛留下的一个物体的颜色,如果出现多个透明物体爹在同一个像素上,就无法通过深度缓冲解决问题

此时我们需要一定的机制排列出上述的顺序,一种方法是根据单个物体在视图空间的深度,一般情况下这种粗略的排序可以很好的工作,但某些情况下仍会出现问题

对于复杂场景,复杂视角下的排列错误问题,使用多边形对齐BSP树,能够得到比较精确的物体排序

一般我们都会在渲染透明物体的pass中关闭深度缓冲写入,测试仍正常,使参与混合的透明片元不会修改已存储的Z深度值

对于单个简单凸面透明体的渲染,这种策略有助于提高效率,对于多个物体渲染,我们应采用一定的排序方法,但即便我们不进行排序且出现了多透明体重叠的情况,不论先后渲染(是否正确按顺序),在关闭写入的情况下,至少能保证所有位于前方的透明体稳定通过测试,并保证能以某种混合呈现出来,不至于在摄像机Transform变换时(渲染顺序改变)突显/消失

在解决了排序问题后,对于多透明的物体相互渗透/嵌套,透明物体是凹面体(一个经典的例子是透明厚壁圆通内的透明物体),这种情况下不能单纯的按照物体先后顺序进行渲染,而是要细化到每一个表面的先后顺序,这就需要使用深度剥离算法或是其它技术,例如每个透明体正/背面两次绘制[1192,1255]

尽管over operator这种透明混合有各种各样的问题,但由于它的简单性,对于常见的单一透明凸面体能表现出正确的透明效果,且不需要额外的内存或特殊GPU支持(只需要一个额外的pass),依然被广泛的使用

它的另一个更常用的称谓是alpha混合,提到透明渲染,我们总是会先想起它来

84e711a9f68b6caa8117199720c29a07.png
图5.33,左图单纯使用深度缓冲进行透明混合,对于透明物体相互渗透的情况,未能混合出正确的结果,右图使用了深度剥离算法混合出了正确的效果,但需要更大的成本

cbede222f1a97eb8d240e3720275147c.png
图5.35 深度剥离算法,每个透明层绘制一次,再按序混合起来,左侧是最接近摄像机的最表面的一层,中间是次层,右侧是最远离的一层,最终结果见624页的图14.33

另一种混合公式为

999fc8296fca338cb833dce5b23a1570.png

对于RGB色而言,这种直接相加的混合模式有助于提亮,从而表现发光效果,例如闪电/火花

不过使用这种相加混合模式也需谨慎,一方面不经过滤可能出现超越范围的情况,另一方面对于一些多层的透明物体(烟/火)多层累加会使得颜色过饱和

此外可以通过修改方程,使得从前向后混合也得到相同的结果,这种混合模式称之为 under operator

f2a015ffd54788ffeab71645d8846913.png

公式中source源是背景色,而destination目标是前景色(与over相反),混合后co是从前方看到的效果

under operator与over operator的混合方向是相反的,即over operator是从最后不透明体不断向前混合透明体(Back RGB-->Front RGBA),而under operator是从最前方的不透体提不断向后混合后方的透明体直到最前方的不透明体(Front RGBA-->Back RGBA)

这种模式需要cd目标颜色/cs源色都保持有一个alpha值(因为是从前方透明体向后混合其它透明体),同时混合后的颜色也保持一个α0的透明度可用于下一次混合

不过对于α0的计算,即便我们直接交换了前景色/后景色,值也是一样的

对于α0的顺序独立性,可以通过将alpha看作覆盖率得到几何解释

7ab481241018c92e2593a00651c041fc.png
图5.34 对于整个像素的覆盖率(alpha)值而言,无论我们如何调整覆盖顺序,结果都是 as+ad-asad

最后遇到位于最前方的不透明体,αs=1.0时,混合得到的α0=1.0

此时再向后混合时,αd=1.0,将完全覆盖后面的颜色

------5.5.2 无序透明(Order-Independent Transparency,OIT)

无序透明意味着不需要应用程序提前执行排序,在渲染透明对象时可按照渲染管线默认的,无规则顺序进行渲染,并得到正确的结果

单纯使用over operator 结合深度缓冲,在多透明对象的情况下是无法实现OIT的这在上一节中已经讨论过了

一种方法是先在一个pass中渲染所有不透明体,记录RGB+Z深度信息,之后关闭深度写入,但保持测试在下一个pass中无序渲染所有透明体,并使用under混合透明体与透明体之间的颜色,保存到一个RGBA缓冲中,最后再用over混合不透明体和所有透明体颜色

但这种方法在多透明体无序渲染中,由于不清楚透明体之间的排位顺序,即便我们都进行了混合,或是加入了一个Z缓冲尝试保证每单次混合前后关系正确,也不能保证最终效果一定正确

under operator另一个用处就是应用在深度剥离算法,这种高阶的OIT算法

OIT算法思路如下:

第一个pass中正常渲染所有对象(包括所有透明体和不透明)记录它们的RGBA+Z深度

(此后所有pass只针对透明对象)

第二个pass中,在第一次Z深度测试下,如果对象Z深度与第一次Z深度匹配,那么就记录到另一个RGBA+Z缓冲中,但这个Z缓冲初始化为最远A初始化完全透明,此时我们就剥离出了摄像机最前方的一层透明物体

第三次pass中,在第二次Z深度测试下,我们再记录一层RGBA+Z(Z初始化最远A初始化完全透明),要求找出比第二次深度更远,但距摄像机最近的片元,我们就剥离出了距摄像机第二层的透明对象

第四次pass中,在第三次Z深度测试下,再剥出第三层

......依次类推

从而我们就将场景前方的所有透明对象,在片元级别,一层层的剥了出来,每次RGBA+Z缓冲相当于渲染了一张纹理(Render Texture,RT)

在经过一定的层数后停止,最后我们在第一次RGBA层上,使用under operator从前向后混合每一层的RGBA透明物体颜色

但深度剥离算法依然存在诸多问题

首先是要剥几层?我们需要预设一个固定的剥离层数,或是通过一个像素绘制计数器,直到某一次pass中无法填入任何RGBA+Z,但事实上在复杂的多透明体相互渗透穿插的情况下,摄像机稍有一点移动,都会导致需要剥离的层数发生改变,这导致了算法执行效率的不稳定,不过由于我们使用从前向后剥离,因此最前面的最先被观潮到的表面能够先被渲染出来并向后覆盖,即使层数不足,先前弃后也能保证结果能够接受

此外深度剥离需要消耗大量显存在保存每一层的RGBA+Z信息,一种优化是每pass剥离结束后,立即向后混合,这样就只用三张RT,但增加了工作量,另一种优化是从后向前剥离,每层剥离后立即使用over operator向前混合,从而且背景层无需记录Alpha,但从后向前的剥离顺序就很需要层数的正确性

从前向后的深度剥离算法,由于之后所有pass只针对透明物体,因而它的思路是基于场景中只有透明物体,或者透明物体层数足够多导致不透明(例如头发的多层叠加),因为最后我们是混合不到最末的不透明体的

另一个问题是复杂的透明体和不透明体遮挡关系下,在简单的所有透明体都出现在场景中央都不被遮挡下是正确的,如果有部分透明体被遮挡,但在视图上位于未被遮挡的第一层透明体之外,由于我们使用了最近初始化向远过测试,这些透明体仍能被正确排除

但如果出现ABA情况,B是一面墙体,A是墙两侧的透明体,我们以墙为背景观察一侧的透明体,此时如果墙后的透明体与墙前的透明体,在视图上重叠,在剥离层数足够多时,就会错误的混合到墙后的透明体(每层剥离只参照上一层更远,但没有考虑后面的层可能已经挡在了墙后面)

为了解决混合不到最末不透明体,以及复杂遮挡的情况,从后向前剥离混合,结合透明体单独渲染,似乎是一种比较完美的思路

首先渲染所有不透明体,记录RGB+Z

之后所有pass只针对透明体,但要参照不透明体渲染的Z深度,向前过测试(筛出位于场景前的不被遮挡的透明体)

第一pass找出符合要求的,但最靠后方的一层(需要另一个初始化为最近的Z缓冲解决靠后),并立即向背景的RGB混合颜色,之后将本次使用到的Z缓冲中,不是最近位置的数值,替换到之前不透明体的Z缓冲上(从而下一pass就找本次剥离更前方的透明层)

之后的pass继续按照向前过测试,但找最靠后方一层的思路

当然也有一种思路是所有pass渲染所有的物体,逐渐剥离所有物体从前到后的深度层次,对于不透明体向后under混合自然会覆盖掉后面的颜色,这在场景物体数量比较有限时也能接受(或者说这才是一般的深度剥离思路

Bavoil和Myers[118]提出了双深度剥离,即单次中同时对比远近剥离出最近层和最远层,从而将渲染的pass减半

Liu等人[1056]研究了一种bucket排序方法,该方法一次可以捕获32个层,但需要消耗大量内存来保存所有排序层,并且对于抗锯齿算法需要消耗极大的成本

透明渲染的另一个问题是如何针对GPU的环境来设计高效的算法,1984年 Carpenter 提出了一种A-buffer的思路,每个三角形会根据它们全覆盖或半覆盖的像素,在A-buffer创建掩码,全覆盖的不透明片元可以直接替换它们后面的掩码,类似于Z缓冲

每个像素会存储一个所有相关片元链表,当所有链表形成,通过遍历所有片段信息就能产生最终结构

A-buffer的优点是对于每个像素只分配固定容量,但从某种意义上说,这也可能是一个缺点,因为在帧开始呈现之前并不知道所需的存储容量,对于复杂的游戏场景可能产生大量的透明片元以及重叠

基于A-buffer的透明渲染思路,现代GPU实现了片元链表的功能,并通过DX11暴露,这一功能的实现需要无需视图(UAVs)原子操作

该算法的原理是对每个透明表面进行光栅化,并将生成的片元插入到每个像素的长链表中,并根据深度进行排序

之后执行一个单独的pass用于遍历每像素的片元链表,根据排序进行透明混合从而得到最终的颜色,并渲染出最终的画面

无论是Abuffer还是像素链表,都是通过GPU的预分配内存资源实现的,但这需要用户预先确定需要内存的容量,如果内存不足就会影响算法的执行

Salvi和Vaidyanathan[1532]提出了一种解决这个问题的方法——多层alpha混合(multi-layer alpha blending),使用英特尔引入的一种称为像素同步的GPU特性以及DX11引入的光栅化序视图实现,此外,粗排序有利于这种方法更好的工作

这种方法提供了可编程的实现,来重新定义存储和混合,在内存不足时可以优雅的降级

移动设备也有类似的技术,称为tile local storage,允许它们实现多层alpha混合[153]

然而这种思路徐工高昂的性能代价

6f31819b0c83409e2786b252ca7f3999.png
图5.36 左上角传统的从后向前alpha1混合,在无序渲染下导致错误的结果,右上角A-buffer给出了一个比较完美的效果,左下角使用多层alpha混合渲染,右下角展示了ABuffer与多层alpha混合的差异

Bavoil等人[115]则提出了一个kbuffer的思路,保存前几个可见的透明层并尽可能的排序,将更深的层进行合并或是丢弃

Maule等人[1142]基于Kbuffer提出了通过加权平均来解析那些较深的透明层

加权平均算法是另一种顺序无关的(或者说它本身就不考虑物体的顺序),单通道(pass),泛用性广的透明渲染思路,这种算法对于高透明的表面或是粒子的透明混合很有效

8a999d80ba9aef6d985950ad49af8b85.png
图5.37 随着透明度的增高,物体的顺序变得越来越不重要,此时就可以使用加权平均一类,不考虑顺序性的透明算法

加权和透明公式如下:

a881c58579cdf4f110b98d789ea8bdaa.png

n是透明片元的数量,ai和ci是前方不透明片元的颜色和透明度,cd是后方透明部分/已混合部分的颜色

当透明物体被渲染时,亦会通过list的形式存储ai和ci,并在透明物体渲染结束时,每像素出计算公式得到最终结果

加权和公式的问题是可能生成过饱和颜色即大于(1.0,1.0,1.0),并且背景颜色可能在alpha总和超过1.0时产生负值影响

为解决这两个问题,通常采用加权平均算法

8e7e720095430b96a135eb718916392b.png

第一行的两个sum值会在透明渲染的pass,针对每像素在缓冲区形成,之后每像素根据公式计算得到最终结果

每个对像素颜色有贡献的片元都根据其alpha值都被赋予了一个权重,最后计算co的过程其实是一个over操作

加权平均的一个局限/特性便是不分先后的将所有颜色混合,除了alpha权重,我们还可以引入其它权重例如按距离加权有助于更好的表现靠近观察者的透明表面

McGuire和Bavoil[1176,1180]便引入了距离权重,从而实现了更有说服力的结果

9ddb0cef126519cf9adc5779eb396375.png
图5.38 两个不同的相机位置,查看相同的引擎模型,都渲染加权混合顺序独立的透明度并且引入了距离权重

不过在大场景,远距离的情况下,受深度变化的非线性影响,距离摄像机较远的一组物体它们的深度权重几乎相等

对此McGuire和Mara[1181,1185]扩展了这种方法,通过增加一种近似的透射效果

本节中讨论的所有透明度算法都是混合各种颜色而不是过滤它们,以模拟像素覆盖率,此类方法之所以有效是由于覆盖产生的透明度不同与顺序是无关的

鉴于透明内容、渲染方法和GPU功能的种类繁多,对于渲染透明对象没有完美的解决方案

其它有关透明效果表现的技术,例如体积照明,透射折射等将在本书后更深入的讨论

------5.5.3 Alpha预乘(Premultiplied Alphas and Compositing)

over operator除了用于实时渲染,也被用于照片/效果图的合成渲染(Compositing),这种情况下每像素的alpha与RGB颜色一起储存

我们通过将RGB(1,1,1)与每像素的alpha相乘,从而形成从纯白(α=1.0完全不透明)到纯黑(α=0.0完透明)的灰度图,来显示出物体的alpha通道,这种图像又是称之为matte,它表现出了物体的轮廓

同样的我们也可以通过这样的灰度图最为一张alpha map/alpha 纹理,通过纹理贴图技术来各向异性的改变物体表面的alpha值

alpha预乘(Premultiplied Alphas)是指RGB数据在保存时,就已经与alpha值进行了相乘

从而alpha混合的公式就由

变为了

应用到渲染中,显然我们可以在运行时少做一次乘法,从而提高效率

然而alpha预乘最大的作用其实并不是那一点运算量的提升,只有进行过alpha预乘的图片,才能在缩放/创建mipmap/滤波时,表现出正确的效果

考虑缩放的情况,对于横向两个像素 C1 α1 C2 α2 而言,采样点落在两像素中间,此时我们需要混合两个带alpha的像素颜色

假设混合使用最简单的线性插值,如果未进行预乘,那么采样结果便是颜色为C1和C2的线插中点,应用α1和α2的平均值作为新的α值

但无论是数感,还是考虑通光率/覆盖率,更符合实际情况应该是融合后像素,对于1/2的通光会按照α1为比率拦截替换C1颜色,另外1/2通光按α2为比率拦截替换C2颜色

也就是说alpha混合时应该是

混合后采样点对应的片元透明度固然是α1和α2的平均,但CS的混合影响应等于C1和C2对α的加权平均(分母为2,因为alpha极值是1,两个alpha就是2)

就实际的效果表现来看,预乘后的图片在缩放,滤波时更符合主观直觉

支持alpha通道的图像格式分为预乘alpha格式(关联alpha)不预乘alpha格式(未关联alpha)

PNG格式就是一种未关联alpha的格式,相对的OpenEXR是关联alpha的,TIFF格式则同时支持两种alpha关联

与alpha通道相关的概念是chroma-keying,这是一个来自于影视行业中的术语,也是我们所熟知的绿幕/蓝幕技术

演员在绿幕/蓝幕的背景中拍摄,图像/视频通过约定的key色值来提供一个轮廓蒙版,后期合成时,检测到特定的key色值就替换为需要的背景

这种方案允许只使用RGB色来表现透明效果,但只能指定唯一的一个特定key颜色,实现完全透明/完全不透明的蒙版效果

GIF格式就允许指定一个颜色为透明,类似的思路应用到实时渲染中被称之为透明剔除/透明蒙版,例如将纯黑色剔除为透明,一些FX Shader便会使用这种做法实现来关联一个蒙版图,从而形成特定形状的粒子(雪花)

---5.6 显示编码(Display Encoding)

这里之前有开一篇有关计算机色彩的文章,为了活跃一下联系Gamma矫正相关就更在这篇文章里了

Randy:计算机色彩​zhuanlan.zhihu.com

第六章 纹理贴图技术(Texturing)

“All it takes is for the rendered image to look right.”这一切都是为了让渲染出的图像看起来正确

纹理贴图技术是利用某些图像(image)、函数(function)或数据源(datasource),在片段元着色阶段,基于着色方程,更改片段元的某些属性来影响片元的着色结果,从而改变物体外观,使物体表面的某种属性产生各向异性

对纹理贴图技术狭义的理解是“把一张图片按一定方式贴在物体表面进行渲染”,这里是通过纹理改变了物体表面的漫反射颜色,而广义上纹理贴图技术可以不止作用于漫反射颜色

原书中在本章的开头例举了一个砖墙的例子:

首先相比于通过细致的拓扑,我们只需要将一张砖墙的纹理“贴”在一个矩形(两个三角形)上,就能获得不错的效果,使用纹理贴图技术最大的意义也便是在顶点有限的情况下,让物体的表面表现出无限的可能

之后为了补足砖墙在近距离观察时的细节,原书中提到了通过粗糙度贴图来影响砖块和水泥砂浆的光照效果,通过各类凹凸贴图(bump mapping),包括法线贴图(bump mapping)视差贴图(Parallax mapping)位移贴图(Displacement mapping)来产生阴影,自遮挡

a125d9a528dce3105724568cf84bd557.png
图6.1 金鱼的身体应用了漫反射贴图和凹凸贴图的效果

---6.1 纹理管线(The Texturing Pipeline)

纹理管线是描述纹理贴图技术如何影响物体表面外观的一整套流程

4fc477ffafd49f6438c609ba35897aec.png

纹理管线的大致流程是,

先使用投影方程(projector function)对物体的顶点坐标进行重映射/重解释,转入参数空间(parameter space)

之后使用一个或多个映射方程(corresponder function(s)),将参数空间坐标转换到纹理空间坐标(texture space)

从而为每个顶点分配纹理空间坐标,我们常说的UV坐标就是对应图片(image)形式的纹理空间坐标

之后通过纹理空间坐标索引纹理数据,并通过值变换函数将其应用到片元的某一属性的改变,进而影响片元的着色表现,使物体表面的某一属性呈现各向异性

整个纹理管线的流程其实可以被分割为三部分,首先为物体的顶点分配一个索引值/纹理坐标(UV),之后通过插值得到片元对应的索引值,并索引到数据源(纹理)中的数据,再将数据应用到片元某一属性的改变上

分配索引值的过程由美术在DCC软件中通过图形化可视化的操作完成,分配方法可以是传统的投影/映射,也可以通过其它方法/参数来生成纹理坐标,例如观察方向,物体表面温度等等,都不失为创造特殊效果的方法

在进行实时渲染时,顶点中的UV坐标作为顶点信息的一部分,在三角形遍历时被内插值到片元上,片段元着色时会根据片元的UV坐标完成数据的索引,以及应用到片元属性的改变

更加详细的解释,以及之后有关Texturing Pipeline各阶段的大致概述不妨参见这篇写给美术看的文章:

Randy:纹理贴图原理与3dMax贴图技法​zhuanlan.zhihu.com

关于投影方程

投影方程会将物体的顶点位置(xyz)转入参数空间,这一过程是我们对三维坐标的一种重解释,进而再利用映射函数将参数空间展开为二维平面,即转换到二维的纹理空间

物体的顶点位置通常使用物体坐标系下的位置,从而纹理就像是“被贴在物体表面一样”,在物体运动时保持不变,也可以使用世界坐标,从而纹理就像是被“投影到物体表面一样”,在物理运动时发生变化

投影的方程的输入除了顶点位置xyz外,还可以通过其它输入参数对投影效果进行控制,例如根据物体的表面法线方向决定要被投影到立方体的那个面上

投影以及之后的映射,一般是在建模阶段,由美术在DCC软件中完成,并将纹理坐标(UV)作为顶点信息的一部分,保存在模型资源中

也可以在实时渲染的 顶点/片段元 着色阶段,实时进行投影的计算,动态生成纹理坐标,这么做有利于精度的提高,以及实现一些特殊效果,例如纹理动画

一些渲染方法,例如环境映射,有自己特殊的按像素进行 投影/映射 的纹理坐标计算方法

关于纹理坐标

经过投影映射后生成的纹理坐标其实不止于二维UV,这与数据源对应的纹理空间有关,

一般的图像(image)纹理只需UV两个坐标即可索引,但我们还是会引入第三个坐标,从而构成UVW坐标(3dMax中的UVW展开修改器),W是垂直于二维纹理空间的坐标轴,这一坐标的引入有利于借助三维计算UV坐标的旋转/翻转

一些三维纹理,例如立方体贴图(Cube Map),其本身就需要UVW三个坐标才能完成索引,这就需要纹理坐标具有向量性,从而具体指向立方体的某一个面

其它系统中可能使用(s,t,r,q)这样的齐次坐标,并借用齐次坐标与三维的对应性质,(s,t,r,q)--->(s/q,t/q,r,q),从而q值就像是一种投影距离的效果,越近投影在物体表面上的纹理大小就越大

为了应对不同分辨率的图像,顶点最终分配到的UV值应在[0,1]之间

纹理数据/信息

纹理可以是传统的图片格式,保存RGB/RGBA数据,进而改变片元的漫反射,或是作为三维向量,替换片元的法线方向(法线贴图)

也可以是一维的数据,这样的图片被称之为“灰度图”,例如高度图(Heigh Map),通过灰度值的白--->黑,对应沿法线凸出--->凹陷的距离,进而生成地形网格,或是作为视差贴图(Parallax Mapping)。灰度图也可以作为透明贴图(Alpha Map)进而使透明度产生各向异性

烘焙高度图时可以输出为一张灰度图,也可以和漫反射一起烘焙,将高度信息保存在像素Alpha中

对于纹理贴图技术来说,纹理只是数据源的一种,也可以由技术美术建立特定的方程/曲线/函数,同样可以作为一种数据源,通过UV坐标索引

线条/方程/函数可以在实时渲染时被计算,也可以被纹理化(渲染到纹理),根据其曲线,以UV作为输入值,灰度作为输出值,进而遍历生成纹理,作为一个查找表,通过索引就可以快速完成值的变换

多道UV

一个物体可以应用多张不同的纹理,进而改变漫反射/法线/金属度等属性,事实上这正是次世代的做法,

因此一个物体也可以拥有不止一套UV,进而适配不同的贴图,称之为不同的UV通道,在DCC中展开UV时会确定这一套展开生成的UV坐标通道编号,而实时渲染索引纹理数据时,也需要使用对应的UV

通常漫反射UV1,法线UV2,也可以只展开一套UV1去对应所有贴图

70b170f08023170060e9a1164dc23d0b.png

映射函数

映射函数会将参数空间转换到纹理空间,可以理解为是将投影参照的正方体/圆柱体/球体,展开为二维的平面的过程,从而与图像纹理表面的某一子部分对应,使之能够“贴在物体表面”,本质上这就是依照参数空间坐标,来分配UV坐标的一个过程

这种对应关系可以在DCC软件,进行UV展开时被直观的看到

c5247f4de81362f355ec6ded02857026.png
Max中的UVW编辑器,能直接看到物体表面被投影/映射后,转换到纹理空间下的对应位置,进而调整展开后的面片与纹理的某一子部分对应,本质上是对映射函数进行了调整,改变了顶点从参数空间转入纹理空间后获得的UV值

也可以在顶点/片段元着色中,动态的使用映射函数计算UV坐标,要注意的是3D数学坐标转换里“世界与物体在做相反运动”的关系,动态计算是是将参数空间下的坐标(物体),转换到纹理空间,那么对应的转换矩阵应是将纹理空间转换重合至参数空间的矩阵(纹理空间--->参数空间),是参数空间--->纹理空间的逆转换矩阵

单纯的进行坐标空间的转换后,获得的UV值可能并不在[0,1]范围内,为此就需要用到另一类映射函数,DX中称之为寻址模式(addressing mode),GL中称之为包装模式(warp mode),将范围外的值映射回,主要有四种方式:

*瓷砖/重复/平铺/warp/repeat/tile---类似360度限制角的过程,即不断±1(mod),直到数值回归[0,1]。针对UV坐标快速的做法是,如果为正可直接截取小数部分,若为负截取小数部分再加1

*翻转/镜像/Mirror---周期翻转重复的形式,根据整数部分的奇偶,决定截取的小数部分是否需要被1减翻转

*夹取/截断/clamp/clamp to edge --- 对超出范围的值,Clamp到边界

*边框填充/border/clamp to border --- 将超出范围的值,在索引时对应到一个指定的特定值(边境颜色),和其它三种的思路不同,border并不是将UV映射回[0,1],而是指定一个特定值,就像是在纹理外都填充好了这一颜色,从而使最终的索引能正常进行

70caf54489fbf99ecd0bb8611658d921.png
从左到右依次是四种寻址模式的效果

DX中还有一种一次镜像的寻址模式,仅在0和1左右进行1次镜像,之后超出 [-1,2]的均被夹取到边界

重复/平铺的方式,可以很廉价的为场景添加细节,然而超过三次以上的重复就很容易被人识破模式,因此要确信这种重复规整的模式是否符合预期,有时我们可能想要生成模式不重复的大纹理

一种方式是Andersson[40]所描述的针对地形图的纹理表现,可以将纹理值与某些非重复属性相结合,例如依据地形类型、海拔高度、坡度等因素对多种纹理进行组合

当然更一般的做法是使用,王皓瓷砖(Wang Tiles)角接瓷砖(Corner tiles),这类考虑接邻匹配的纹理形式,从而利用随机来对抗重复,并被正确的拼接在一起

也可以考虑一下彭罗斯拼图(Penrose tiling),这种优美的重复纹理


/*

好的,随着隔壁人物建模那边初步学习完成

这里翻篇开始后半年的学习计划

RTR继续从第六章开始阅读,本文也将进入长期的日更状态

计划用接下来的一年时间将RTR剩下的章节全部读完并做好笔记

本文难免存在一些理解不到位或是转述谬误的地方

欢迎各路神仙留言指正或是指教一些我不知道的点

若能不吝赐教,鄙人先谢为敬

*/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值