本章內(nèi)容概覽
- 光照模型
- 光源
- 實(shí)現(xiàn)光照模型
- 鋸齒和抗鋸齒
- 透明箕母,透明度和合成
- 顯示編碼
當(dāng)渲染三維物體時(shí),模型不應(yīng)該只有合適的幾何外形诽表,還要有所期望的顏色外表猎塞。取決于應(yīng)用程序的使用,我們可以得到真實(shí)感渲染結(jié)果蔓罚,也可以得到風(fēng)格化渲染樣式罗珍。下面是一些例子:
這一章會(huì)介紹真實(shí)感渲染和風(fēng)格化渲染的所需要的著色概念洽腺。
光照模型
決定一個(gè)渲染物體的外表顏色的第一個(gè)步驟是選擇一個(gè)光照模型來(lái)描述物體的顏色應(yīng)該如何更根據(jù)表面朝向、觀察方向和光源方向等參數(shù)來(lái)變化覆旱。
例如蘸朋,我們使用Gooch光照模型,這是一個(gè)非真實(shí)感渲染的一個(gè)部分扣唱,Gooch光照模型被設(shè)計(jì)用來(lái)理論上提升光照細(xì)節(jié)的可分辨性藕坯。
Gooch光照模型的基礎(chǔ)理念是將表面法線方向和光源的位置相比較,如果法線朝向光源噪沙,那么表面會(huì)使用暖色調(diào)炼彪,如果反向,就會(huì)使用冷色調(diào)正歼,在兩者之間的角度會(huì)在這些色調(diào)間插值辐马,這基于用戶提供的表面顏色。在這個(gè)例子中局义,我們給模型添加一個(gè)非真實(shí)感高光效果來(lái)給表面一種反光的效果喜爷,下圖顯示了使用這一光照模型的效果:
光照模型通常使用一些屬性來(lái)控制外表的變化,設(shè)置這些屬性的值就是決定物體外表的下一個(gè)步驟萄唇,我們的模型例子只有一個(gè)屬性——表面顏色檩帐,在上圖的下方材質(zhì)球顯示。
和大多數(shù)光照模型類似另萤,這個(gè)光照模型例子由表面朝向湃密、觀察和光源方向影響。對(duì)于著色四敞,這些方向經(jīng)常表示為標(biāo)準(zhǔn)化矢量泛源,這在下圖顯示:
現(xiàn)在我們定義所有的輸入到我們的光照模型,下面是數(shù)學(xué)公式:
上述等式中的中間計(jì)算如下:
在這一定義中的一些數(shù)學(xué)表達(dá)式也經(jīng)撤尬#可以在一些其它的光照模型中發(fā)現(xiàn)俩由。截取操作,通常截取到0以上或在0到11之間截取癌蚁,這在光照模型中很常見(jiàn)幻梯,這里我們使用來(lái)表示在0到1之間截取。點(diǎn)積操作出現(xiàn)了三次努释,每次出現(xiàn)在兩個(gè)單位矢量間碘梢,這也是一個(gè)極其常見(jiàn)的操作。點(diǎn)積的值是兩個(gè)矢量的長(zhǎng)度積然后乘以?shī)A角的余弦伐蒂,所以煞躬,兩個(gè)單位矢量的點(diǎn)積就是兩個(gè)矢量間的夾角的余弦,這對(duì)于衡量?jī)蓚€(gè)矢量的夾角來(lái)說(shuō)很有用。包含余弦的函數(shù)經(jīng)常用在光照模型中恩沛,這對(duì)于衡量?jī)蓚€(gè)方向的關(guān)系來(lái)說(shuō)很準(zhǔn)確也很簡(jiǎn)潔在扰,例如,光源方向和表面法線雷客。
另一個(gè)通用的著色操作是基于一個(gè)0到1之間的標(biāo)量值來(lái)在兩個(gè)顏色值之間進(jìn)行線性插值芒珠,這一操作的形式如,它使用位于0到1之間的
值來(lái)對(duì)
和
進(jìn)行線性插值搅裙。這一操作在這一光照模型中出現(xiàn)了兩次皱卓,首先在
和
之間插值,然后是在高光
和上面的結(jié)果顏色之間進(jìn)行插值部逮。線性插值操作在著色器中經(jīng)常出現(xiàn)娜汁,是一個(gè)內(nèi)置的方法,稱為
或
兄朋,在許多著色器語(yǔ)言中都有出現(xiàn)掐禁。
這一行計(jì)算了反射光矢量,將
通過(guò)
反射颅和,這一操作不像之前兩個(gè)操作那樣普遍使用傅事,但在某些情況下使用挺普遍的,作為內(nèi)置函數(shù)融虽,一般稱為
享完。
通過(guò)使用不同的數(shù)學(xué)表達(dá)式和著色參數(shù)和不同的方式來(lái)結(jié)合這些操作灼芭,光照模型的定義的種類極其豐富有额。
光源
在我們的光照模型例子中,光照的影響很簡(jiǎn)單彼绷,它對(duì)著色提供了一個(gè)單一的方向巍佑,當(dāng)然,在真實(shí)世界中寄悯,光照可以非常復(fù)雜萤衰。在場(chǎng)景中可以有多個(gè)光源,每一個(gè)光源有自己的大小猜旬、形狀脆栋、顏色和強(qiáng)度,非直射光還有更多的變化洒擦。我們之后介紹的PBR的真實(shí)感光照模型就不要考慮更多的參數(shù)椿争。
相反的,非真實(shí)感光照模型就會(huì)用許多不同的方式使用光照秦踪,取決于應(yīng)用和視覺(jué)風(fēng)格的選擇。一些高度非真實(shí)感模型可能完全沒(méi)有光照的概念,或者只提供一些簡(jiǎn)單的方向光源椅邓。
光照復(fù)雜性的下一步驟是光照模型的二元性柠逞,可能接受光照或不接受光照。一個(gè)表面使用這一模型進(jìn)行光照的話景馁,接受光照的部分有一種顏色板壮,不接受光照的部分會(huì)有不同的顏色。這暗示了一些對(duì)于區(qū)分這兩種情況的標(biāo)準(zhǔn):離光源的距離裁僧,陰影个束,表面是否朝向光源,或者這些因數(shù)的結(jié)合聊疲。
從有無(wú)光照的二元性到光照強(qiáng)度的連續(xù)縮放是一個(gè)小的步驟茬底,這可以通過(guò)無(wú)光照和完全光照進(jìn)行簡(jiǎn)單的插值得到,這暗示了光照強(qiáng)度的一個(gè)邊界范圍获洲,如0-1阱表,或者其它的非邊界量方式,后者使用的而一種通用的方式是將光照模型分為光照和非光照部分贡珊,使用一個(gè)因數(shù)線性縮放光照部分:
這一公式可以很簡(jiǎn)單的擴(kuò)展為RGB單光源:
以及多光源:
非光照部分與二元性光照模型的非光照外表部分綁定最爬,可以有多種形式,取決于所希望的視覺(jué)效果和應(yīng)用程序的需要门岔,例如爱致,會(huì)讓不被光照影響的部分顯示為純白,相反地寒随,非光照部分可以將不被光照影響的地方表示為一些非真實(shí)感結(jié)果糠悯,和Gooch光照模型類似。通常妻往,光照模型的這部分并不是直接受某一確切光源的直射光的影響互艾,如天空或散射過(guò)的光。
我們之前提到過(guò)讯泣,如果光源方向和法線夾角超過(guò)90°角纫普,那么這個(gè)光源不會(huì)影響表面的頂點(diǎn),實(shí)際上產(chǎn)生影響的是來(lái)自表面以下的光好渠。這一點(diǎn)可以被考慮為一種光源方向和表面關(guān)系的特殊情況昨稼,即使是基于物理的渲染,這個(gè)關(guān)系也可以由簡(jiǎn)單的幾何規(guī)則得到拳锚,對(duì)于許多不同種的非PBR和非真實(shí)感渲染都適用假栓。
一束光源在表面的效果可以被可視化為一系列光線,碰撞到表面的光線密度與計(jì)算光照模型的光強(qiáng)掛鉤晌畅。下圖顯示了這一過(guò)程的橫截面:
沿橫截面要碰撞到表面的光線間的間距與
更準(zhǔn)確地來(lái)說(shuō)剩岳,光線密度和正的點(diǎn)積成正比贞滨,點(diǎn)積為負(fù)相當(dāng)于光線從表面背面射入,不產(chǎn)生影響拍棕。所以晓铆,在使用點(diǎn)積進(jìn)行多光源著色前,需要先截?cái)帱c(diǎn)積值大于0绰播,這里使用標(biāo)記骄噪,即讓小于0的點(diǎn)積值賦為0:
這一光照模型適用于PBR,對(duì)于非真實(shí)感渲染也有利蠢箩,因?yàn)樗軒椭_定光照的完整連續(xù)性链蕊,特別是不在光照范圍和陰影內(nèi)的表面。然而谬泌,對(duì)于這一規(guī)則滔韵,有部分光照模型并不適用,這樣的光照模型會(huì)適用未截?cái)嘀?的版本的等式掌实。
對(duì)于陪蜻,最簡(jiǎn)單的方式是使用一個(gè)常量顏色:
這樣會(huì)得到光照模型:
這一模型的光照部分與蘭伯特光照模型有關(guān),由Johann Heinrich Lambert在1760年提出贱鼻,這一模型在理想漫反射光照表面的背景下適用宴卖,即表面是完全粗糙的。我們這里使用的是簡(jiǎn)化版本忱嘹,之后講PBR的時(shí)候會(huì)詳細(xì)講述嘱腥。蘭伯特模型自己本身可以用來(lái)簡(jiǎn)單著色耕渴,也是許多光照模型的重要構(gòu)成部分拘悦。
從上面我們推導(dǎo)的一些光照模型等式可以看處,一個(gè)光源通過(guò)兩個(gè)參數(shù)影響光照模型:一個(gè)由片段指向光源的方向矢量橱脸,和一個(gè)光源顏色
础米。存在許多不同類型的光源,雖然會(huì)由許多不同添诉,但這兩個(gè)參數(shù)是最主要的屁桑。
接下來(lái)我們會(huì)討論一些比較流行的光源,它們的共同點(diǎn)在于:在一個(gè)給定的表面位置上栏赴,每個(gè)光源從一個(gè)特定方向照亮表面蘑斧,換句話說(shuō),光源從被照亮區(qū)域的角度觀察,就是一個(gè)無(wú)線小的點(diǎn)竖瘾。這對(duì)于真實(shí)世界的光源來(lái)說(shuō)并不嚴(yán)謹(jǐn)沟突,這只是一個(gè)令人信服的近似結(jié)果。
平行光
平行光是光源中最簡(jiǎn)單的模型捕传,場(chǎng)景中的和
都是常量惠拭,其中
可能會(huì)因?yàn)殛幱岸p。平行光沒(méi)有位置庸论,當(dāng)然职辅,特定的光源在空間中是由位置的。平行光是一種抽象光聂示,當(dāng)距離光源的距離相對(duì)于場(chǎng)景的尺寸很大時(shí)域携,效果不錯(cuò)。例如鱼喉,一個(gè)小的桌面立體模型被20英尺外的照明燈照亮涵亏,這相當(dāng)于一個(gè)平行光源的效果。另一個(gè)例子是太陽(yáng)光照亮場(chǎng)景蒲凶,除非是考慮太陽(yáng)系中天體之間的光照气筋。
平行光的概念有時(shí)可以擴(kuò)展為方向保持不變,同時(shí)顏色
不停變化旋圆,這往往在特定的場(chǎng)景限制使用宠默,例如,可以使用兩個(gè)嵌套的立方體定義一個(gè)區(qū)域灵巧,在外圍的立方體外的
搀矫,內(nèi)部立方體等于某個(gè)常量,在這兩個(gè)立方體間的區(qū)域使用線性插值得到刻肄。
精確光源
精確光源擁有一個(gè)位置瓤球,這不同于平行光。這樣的光源同樣沒(méi)有維度敏弃,沒(méi)有形狀和大小卦羡,這不同于現(xiàn)實(shí)生活中的光源。我們使用術(shù)語(yǔ)“精確”(punctual)麦到,這一單詞來(lái)自拉丁語(yǔ)puntus绿饵,意思是點(diǎn),這用來(lái)表示來(lái)自一個(gè)單一位置的光照瓶颠。我們使用術(shù)語(yǔ)點(diǎn)光源來(lái)代表一類特定的發(fā)光物拟赊,其向所有方向發(fā)射光線,因此點(diǎn)光源和聚光燈是兩種不同類型的精確光源粹淋。光源方向取決于光源位置
和要照亮平面點(diǎn)的位置
:
上面的等式是矢量標(biāo)準(zhǔn)化的一個(gè)例子:將矢量除以它的模來(lái)得到同方向單位矢量吸祟,這個(gè)等式在許多著色器語(yǔ)言中都有內(nèi)建的方法瑟慈。然而,有時(shí)需要來(lái)自等式的中間結(jié)果屋匕,需要使用多個(gè)步驟來(lái)精確的得到標(biāo)準(zhǔn)結(jié)果封豪。將這一觀點(diǎn)用于精確光源方向等式得到:
是指向光源的光照方向,
通過(guò)數(shù)學(xué)定義得到矢量模炒瘟。
點(diǎn)光源/泛光燈
統(tǒng)一向不同方向發(fā)光的精確發(fā)光源被稱為點(diǎn)光源或泛光燈吹埠。對(duì)于點(diǎn)光源,與距離
呈函數(shù)關(guān)系疮装,這與衰減有關(guān)缘琅。下圖顯示了衰減產(chǎn)生的原因:
在給定的平面上,來(lái)自點(diǎn)光源的光線間的間距與光源到平面的距離呈正比廓推,且光線間的間距會(huì)不停增長(zhǎng)刷袍,這樣的話光線的密度相當(dāng)于降低了,并與
上面的等式常指代平方反比燈光衰減。雖然理論上這一正確的光源衰減并不等于實(shí)際专缠,往往存在一些問(wèn)題雷酪。
首先的問(wèn)題出現(xiàn)在小距離處,如果距離的值接近0涝婉,那么
的值會(huì)接近無(wú)窮大哥力,而且如果
的值為0,那么就會(huì)遇到除以0的錯(cuò)誤墩弯。為解決這一問(wèn)題吩跋,我們可以在分母加上一個(gè)小的值來(lái)進(jìn)行保護(hù):
的值取決于實(shí)際應(yīng)用程序赞警。比如桐早,虛幻引擎使用的是
霎箍。
另一種解決方式在尖叫引擎和寒霜引擎中使用佑钾,即使用一個(gè)截?cái)嗖僮鳎尫帜复笥?:
這種方法不同于第一種方法纬向,它有自己的物理解釋趋厉,即發(fā)光物自己的半徑首装,
的值小于
表明光照著色平面位于光源內(nèi)部脓魏,但這種情況是不可能的兰吟。
相反地通惫,第二個(gè)問(wèn)題出在大距離處茂翔,這一問(wèn)題與視覺(jué)效果無(wú)關(guān),主要是性能方面履腋。盡管光源的光會(huì)隨距離不斷衰減珊燎,但它不會(huì)等于0惭嚣,只會(huì)無(wú)限接近零,為了渲染效率悔政,我們可以在某一距離時(shí)讓光衰減為0晚吞,為實(shí)現(xiàn)這一目的有許多方法存在。理論上來(lái)說(shuō)谋国,這一瞬間的改變需要盡可能的不起眼槽地,為避免銳利的邊緣剪切,最好讓上述函數(shù)的導(dǎo)數(shù)和值在同一距離處均為0芦瘾。一種解決方法是將平方反比等式乘以一個(gè)窗口函數(shù)捌蚊,下面是虛幻和寒霜引擎使用的方法:
表明在平方運(yùn)算前先將值截?cái)嘀?。下面的曲線圖顯示了平方反比函數(shù)近弟、窗口函數(shù)和兩者相乘的結(jié)果:
應(yīng)用程序的需求會(huì)影響方法的選擇缅糟,例如,當(dāng)距離衰減方法的空間采樣頻率低時(shí)(例如光照貼圖或頂點(diǎn)光照)祷愉,讓在處的導(dǎo)數(shù)等于0是很重要的窗宦。尖叫引擎不使用光照貼圖會(huì)頂點(diǎn)光照,所以它使用一個(gè)更簡(jiǎn)單的調(diào)整二鳄,在
和
間線性衰減赴涵。
對(duì)于一些應(yīng)用程序,匹配平方反比曲線并不是必須的订讼,因此一些其它的方法會(huì)被使用句占,5.11-5.14等式可以被簡(jiǎn)寫(xiě)為:
其中是一些距離函數(shù),這些函數(shù)被稱為距離衰減函數(shù)躯嫉。在一些情況下纱烘,使用非平方反比衰減函數(shù)是出于性能的限制,例如祈餐,游戲《正當(dāng)防衛(wèi)2》需要的光源并不需要多么昂貴的計(jì)算擂啥。這表明了一個(gè)簡(jiǎn)單的衰減函數(shù),為避免逐頂點(diǎn)光照的粗糙性來(lái)說(shuō)足夠了:
在其它情況下帆阳,衰減函數(shù)的選擇會(huì)受可創(chuàng)造性考慮的影響哺壶,例如,虛幻引擎蜒谤,真實(shí)感和非真實(shí)感渲染有兩種衰減模式:平方反比模式和指數(shù)衰減模式山宾,前者已經(jīng)介紹過(guò)了,后者可以用來(lái)拉伸創(chuàng)建不同的衰減曲線鳍徽∽拭蹋《古墓麗影》的開(kāi)發(fā)者使用樣條線編輯器來(lái)控制衰減曲線,允許許多曲線控制操作阶祭。
聚光燈
不同于點(diǎn)光源绷杜,除了隨距離衰減光照外直秆,還可以沿某一特定方向進(jìn)行衰減,使用函數(shù)鞭盟,我們將兩者結(jié)合起來(lái):
的不同選擇可以得到不同的光照效果圾结,其中一種重要的效果是聚光燈,它將光照投影到一個(gè)圓錐里齿诉。一個(gè)聚光燈的方向性衰減函數(shù)繞方向
有旋轉(zhuǎn)對(duì)稱性筝野,這一點(diǎn)可以用方向
和反光源方向
的夾角
來(lái)表示。
大多數(shù)的聚光燈函數(shù)使用復(fù)合的余弦值的表達(dá)式粤剧。聚光燈通常有一個(gè)本影角
遗座,讓所有
的
。這個(gè)角度使用的最大距離衰減法則可以和點(diǎn)光源的最大距離衰減法則類似俊扳。對(duì)于聚光燈途蒋,通常還有一個(gè)半影角
,它定義一個(gè)內(nèi)圓錐馋记,在這之中的光強(qiáng)最大号坡。這在下圖顯示:
聚光燈可以使用許多不同的衰減函數(shù),但它們比較接近梯醒。例如宽堆,下面的
表示將值截?cái)嘣?-1之間茸习。平滑插值函數(shù)是一個(gè)三次多項(xiàng)畜隶,經(jīng)常用于進(jìn)行平滑插值。
下圖顯示了我們至今位置使用過(guò)的光源:
其它的精確光源
有許多不同的其它方式來(lái)讓精確光源的值變化号胚。
并不限制在上述的聚光燈討論中籽慢,它可以表示許多不同類型的方向變化,包括從真實(shí)世界光源測(cè)量到的列表模塊猫胁。美國(guó)照明工程學(xué)會(huì)(IES)對(duì)這些測(cè)量指定了一個(gè)標(biāo)準(zhǔn)的文件格式箱亿,IES文件可以從許多光源制造商那里得到,曾經(jīng)用于游戲《殺戮地帶:暗影墜落》弃秆,同時(shí)虛幻和寒霜引擎等也使用了届惋。
《古墓麗影》游戲有一種精確光源類型,它沿x菠赚、y脑豹、z軸使用不同的衰減函數(shù)。在《古墓麗影》中衡查,曲線可以用來(lái)隨時(shí)間改變光強(qiáng)瘩欺,例如,可以用來(lái)制作閃爍的火把峡捡。
其它光源類型
平行光和精確光源击碗,它們是通過(guò)光源方向計(jì)算方式主導(dǎo)的筑悴。不同的光源類型可以通過(guò)使用其它的計(jì)算光源方向的方法來(lái)定義们拙,例如稍途,《古墓麗影》有一種膠囊體光源,使用線段來(lái)代替點(diǎn)作為光源砚婆,對(duì)于每個(gè)著色像素械拍,其與自身到距離線段上最近點(diǎn)的方向用作光源方向。
只要著色器使用和
值來(lái)估計(jì)光照等式装盯,任何方法都可用來(lái)計(jì)算這些值坷虑。
至今為止討論的光源都是抽象的,顯示生活中埂奈,光源有大小和形狀迄损,并在不同的方向上照明表面上的點(diǎn)。在渲染中账磺,這樣的光源被稱為區(qū)域光芹敌,它們?cè)趯?shí)時(shí)渲染中的使用頻率正穩(wěn)定上升。區(qū)域光渲染的技術(shù)有兩類:模擬來(lái)自區(qū)域光部分遮擋結(jié)果的平滑陰影的邊垮抗,模擬在平面上進(jìn)行區(qū)域光光照氏捞。第二類光照常見(jiàn)于那些可以識(shí)別反射方向上的形狀和大小的平滑鏡面。平行光和精確光源并沒(méi)有不再使用冒版,只是沒(méi)有以前那么普遍了液茎。對(duì)于一個(gè)光源的區(qū)域的模擬計(jì)算得到了發(fā)展,實(shí)現(xiàn)時(shí)花費(fèi)的開(kāi)銷比較少辞嗡,所以現(xiàn)在正廣泛使用捆等。GPU性能的提高也允許使用更多在以前看來(lái)開(kāi)銷大的技術(shù)。
實(shí)現(xiàn)光照模型
為了能夠使用续室,這些光照和著色等式必須用代碼來(lái)實(shí)現(xiàn)楚里,這里我們討論一些重要的設(shè)計(jì)和編寫(xiě)實(shí)現(xiàn)的思路。
估值頻率
當(dāng)設(shè)計(jì)一個(gè)著色實(shí)現(xiàn)時(shí)猎贴,需要按照它們的估值的頻率進(jìn)行分類計(jì)算班缎。首先決定給定計(jì)算的結(jié)果是否在整個(gè)繪制命令中都是常量,在這種情況下她渴,計(jì)算需要通過(guò)應(yīng)用程序來(lái)執(zhí)行达址,通常在CPU上,盡管GPU的計(jì)算著色器也可以用來(lái)進(jìn)行某些特定的計(jì)算趁耗。結(jié)果通過(guò)統(tǒng)一著色器輸入傳遞至圖形API沉唠。
即使是在這一分類下,仍存在一個(gè)廣范圍的可能的估值頻率苛败。最簡(jiǎn)單的相關(guān)情況就是一個(gè)著色等式中的次級(jí)表達(dá)式满葛,可以賦予基于很少的變化參數(shù)的計(jì)算径簿,例如硬件配置和安裝選項(xiàng)。這樣的著色計(jì)算可能會(huì)在著色器編譯后進(jìn)行嘀韧,這種情況下并不需要設(shè)置統(tǒng)一著色器輸入篇亭。相反地,計(jì)算可能會(huì)在一個(gè)預(yù)計(jì)算的離線過(guò)程中進(jìn)行锄贷,在安裝期間或應(yīng)用程序加載期間译蒂。
另一種情況是當(dāng)著色計(jì)算的結(jié)果隨應(yīng)用程序改變時(shí),但改變很慢谊却,并不需要每幀都去更新柔昼。例如,基于真實(shí)時(shí)間的光照因數(shù)改變炎辨。如果計(jì)算開(kāi)銷很大捕透,最好多幀分?jǐn)傆?jì)算。
另一個(gè)包含每幀執(zhí)行計(jì)算的例子碴萧,例如結(jié)合視圖和透視矩陣乙嘀,或者每個(gè)模型執(zhí)行一次,例如更新取決于位置的光照因數(shù)勿决,或者每個(gè)繪制命令執(zhí)行一次乒躺,例如更新一個(gè)模型的每個(gè)材質(zhì)的因數(shù)。通過(guò)估值的頻率來(lái)組合統(tǒng)一著色器輸入可以讓?xiě)?yīng)用程序更有效率低缩,也可以通過(guò)減少常量更新來(lái)幫助CPU提升性能嘉冒。
如果一個(gè)著色等式的結(jié)果因?yàn)橐粋€(gè)繪制命令改變,它就不會(huì)通過(guò)一個(gè)統(tǒng)一著色器輸入傳遞給著色器咆繁,反而讳推,它必須通過(guò)一個(gè)可編程著色器階段來(lái)計(jì)算,如果允許玩般,通過(guò)變化著色器輸入傳遞到其它的階段银觅。在理論上,著色計(jì)算可以在任何一個(gè)可編程階段進(jìn)行坏为,每一個(gè)階段都與一個(gè)不同的估值頻率綁定:
- 頂點(diǎn)著色器——每個(gè)預(yù)處理細(xì)分曲面頂點(diǎn)估值一次究驴。
- 外殼著色器——每個(gè)平面片估值一次。
- 域著色器——每個(gè)后處理細(xì)分曲面頂點(diǎn)估值一次匀伏。
- 幾何著色器——每個(gè)基本體估值一次洒忧。
- 像素著色器——每個(gè)像素估值一次。
實(shí)際中够颠,大多數(shù)的著色計(jì)算每像素執(zhí)行一次熙侍。在這些著色計(jì)算都通常實(shí)現(xiàn)在像素著色器中時(shí),計(jì)算著色器的實(shí)現(xiàn)也普遍的增長(zhǎng)起來(lái)。其它的一些階段主要用于例如平移和變形的幾何操作蛉抓。為理解這一情況庆尘,我們可以比較逐頂點(diǎn)著色和逐像素著色估計(jì)。在較老的材料中巷送,這通常會(huì)牽涉到高洛德光照模型和Phong光照模型驶忌,這些光照模型使用的等式和之前講過(guò)的很像,但是修改了一些以便適應(yīng)多光源情況惩系。
下圖展示了逐頂點(diǎn)和逐像素著色的區(qū)別:
對(duì)于龍這種點(diǎn)密集的模型位岔,區(qū)別很小如筛,但對(duì)于茶壺模型堡牡,頂點(diǎn)著色估計(jì)造成了一種棱角感高光的視覺(jué)錯(cuò)誤,在兩個(gè)三角形構(gòu)成的平面杨刨,逐頂點(diǎn)著色也明顯錯(cuò)誤晤柄。造成問(wèn)題的原因是光照等式的一部分,尤其是高光部分妖胀,它的值的分布在平面網(wǎng)格上是非線性的芥颈。這讓它們非常不適合用在頂點(diǎn)著色器上,因?yàn)榻Y(jié)果會(huì)沿著三角形進(jìn)行線性插值赚抡,然后送進(jìn)像素著色器爬坑。
原則上,其實(shí)可以只在像素著色器中計(jì)算高光部分涂臣,在頂點(diǎn)著色器中計(jì)算剩余的部分盾计。這樣結(jié)果通常不會(huì)有手工視覺(jué)感,理論上也可以節(jié)省計(jì)算開(kāi)銷赁遗。實(shí)際中署辉,這樣的混雜實(shí)現(xiàn)通常不是最理想的。光照模型的線性變化的部分趨向于使用最少的計(jì)算開(kāi)銷岩四,趨向于增加將這一部分剝離出來(lái)趨向于增加充足的開(kāi)支哭尝,例如重復(fù)的計(jì)算,額外的變化輸入剖煌,這反而沒(méi)什么好處材鹦。
就像我們之前提到過(guò)的那樣,在大多數(shù)實(shí)現(xiàn)中耕姊,頂點(diǎn)著色器負(fù)責(zé)那些非著色計(jì)算桶唐,例如幾何變換和變形,作為結(jié)果的幾何表面屬性箩做,會(huì)被轉(zhuǎn)換到一個(gè)合適的坐標(biāo)系中莽红,由頂點(diǎn)著色器輸出,沿著三角形線性插值后傳入像素著色器作為變化著色器輸入。這些屬性通常包含表面位置安吁,表面法線醉蚁,有時(shí)還包括表面切線。
注意鬼店,即使頂點(diǎn)著色器總是生成單位長(zhǎng)度的表面法線网棍,插值也可以改變其長(zhǎng)度。見(jiàn)下圖左側(cè):
因此妇智,在像素著色器中也需要對(duì)法線進(jìn)行重新標(biāo)準(zhǔn)化滥玷。然而,頂點(diǎn)著色器生成的法線的長(zhǎng)度仍然有問(wèn)題巍棱,如果法線長(zhǎng)度在頂點(diǎn)間變化明顯惑畴,例如,頂點(diǎn)混合的副作用航徙,這會(huì)讓插值的結(jié)果方向歪斜如贷,這在上圖右側(cè)顯示了。由于這兩種效果到踏,實(shí)現(xiàn)通常會(huì)在插值前和后對(duì)矢量都進(jìn)行標(biāo)準(zhǔn)化杠袱,同時(shí)在頂點(diǎn)和像素著色器中。
不同于表面法線窝稿,指向特定位置的矢量楣富,如觀察矢量和精確光源的方向,通常不會(huì)進(jìn)行插值伴榔,反而纹蝴,表面位置的插值會(huì)用來(lái)在像素著色器中計(jì)算上面這兩個(gè)矢量。除了標(biāo)準(zhǔn)化潮梯,我們至今所看到的骗灶,需要在像素著色器中執(zhí)行的,每個(gè)矢量都通過(guò)矢量減法得到秉馏。如果對(duì)于某些理由耙旦,需要對(duì)這些矢量進(jìn)行插值的話,就不要先手動(dòng)進(jìn)行標(biāo)準(zhǔn)化的萝究,這會(huì)造成類似于下面的問(wèn)題:
之前我們提到過(guò)頂點(diǎn)著色器會(huì)將表面幾何體變換到一個(gè)合適的坐標(biāo)系免都,通過(guò)統(tǒng)一變換傳入到像素著色器的攝像機(jī)和光源位置,就是通過(guò)應(yīng)用程序變換到相同坐標(biāo)系的帆竹。但什么算是合適的坐標(biāo)系绕娘,所有的可能包括全局世界坐標(biāo)系,攝像機(jī)的局部坐標(biāo)系或當(dāng)前渲染模型的坐標(biāo)系栽连。這一選擇通常對(duì)于渲染系統(tǒng)來(lái)說(shuō)是完整的险领,基于對(duì)例如性能侨舆、變換性和簡(jiǎn)單性等系統(tǒng)層面的考量。例如绢陌,如果一個(gè)要渲染場(chǎng)景包含大量的光源挨下,世界空間可能會(huì)避免轉(zhuǎn)換到燈光空間,相反脐湾,攝像機(jī)空間反而更合適臭笆,可以基于觀察矢對(duì)像素著色器操作進(jìn)行優(yōu)化,也可能提高精度秤掌。
雖然大多數(shù)著色器實(shí)現(xiàn)愁铺,包括我們接下來(lái)要討論的實(shí)現(xiàn)例子,基本都遵循我們所講述過(guò)的框架闻鉴,但還是有一些例外的茵乱。例如,一些應(yīng)用程序選擇面方式的逐基本體著色估計(jì)椒拗,出于實(shí)現(xiàn)風(fēng)格化渲染的原因似将。這一風(fēng)格通常稱為扁平化渲染获黔,下圖顯示了兩個(gè)例子:
原則上蚀苛,扁平化渲染可以在幾何著色器中完成,但最近的相關(guān)實(shí)現(xiàn)通常使用頂點(diǎn)著色器玷氏,這通過(guò)將基本體屬性綁定在第一個(gè)頂點(diǎn)上堵未,并禁止頂點(diǎn)值插值來(lái)完成。進(jìn)行插值讓第一個(gè)頂點(diǎn)的值傳遞到基本體的所有像素上盏触。
實(shí)現(xiàn)例子
接下來(lái)我們展示一個(gè)光照模型實(shí)現(xiàn)的實(shí)例渗蟹。在之前提到過(guò)的,我們要實(shí)現(xiàn)的光照模型和擴(kuò)展Gooch光照模型很像赞辩,但修改了一些來(lái)適應(yīng)多光源情況:
使用的中間等式:
這一等式符合下面提到過(guò)的多光源架構(gòu):
光照和非光照部分:
在大多數(shù)渲染應(yīng)用程序中雌芽,例如的材質(zhì)變化變量可以存儲(chǔ)在頂點(diǎn)數(shù)據(jù)中,或者存儲(chǔ)在紋理中辨嗽。然而世落,為了讓這個(gè)實(shí)現(xiàn)例子變得比較簡(jiǎn)單,我們假設(shè)
永遠(yuǎn)是常量糟需。
這個(gè)實(shí)現(xiàn)會(huì)對(duì)所有的光源使用著色器的動(dòng)態(tài)分支循環(huán)屉佳,不過(guò)這個(gè)方法也只對(duì)這個(gè)簡(jiǎn)單案例有用,在復(fù)雜案例上洲押,如光源很多的場(chǎng)景武花,就不那么適用了。為了便捷杈帐,這里我們也只涉及點(diǎn)光源体箕。
著色器模型并不是獨(dú)立實(shí)現(xiàn)的,而是一個(gè)更大渲染框架的一部分。這個(gè)例子根據(jù)一個(gè)簡(jiǎn)單的WebGL2程序的Phong模型改編而來(lái)累铅,基本的規(guī)則是相同的驶沼。
我們會(huì)討論一些GLSL代碼的案例,并通過(guò)JavaScript WebGL調(diào)用争群。這里并不會(huì)教授WebGL的特定用法回怜,只是展示一般的實(shí)現(xiàn)規(guī)則。我們會(huì)從里到外進(jìn)行實(shí)現(xiàn)换薄,從一個(gè)像素著色器開(kāi)始玉雾,然后是頂點(diǎn)著色器,最后進(jìn)行應(yīng)用程序內(nèi)部的API調(diào)用轻要。
在著色器正式的代碼開(kāi)始前复旬,著色器的資源包括著色器輸入和輸出的定義。之前介紹過(guò)冲泥,GLSL術(shù)語(yǔ)中驹碍,著色器的輸入被分為兩類,第一類是統(tǒng)一輸入凡恍,它通過(guò)應(yīng)用程序設(shè)置志秃,并在整個(gè)繪制命令中保持為常量,第二類是變化輸入嚼酝,它的值會(huì)在著色器調(diào)用間變化浮还。這里我們首先定義像素著色器的輸入,GLSL使用in
進(jìn)行標(biāo)識(shí)闽巩,同時(shí)使用out
進(jìn)行輸出標(biāo)識(shí):
in vec3 vPos;
in vec3 vNormal;
out vec4 outColor;
這個(gè)像素著色器只有一個(gè)簡(jiǎn)單的輸出钧舌,即最終的顏色。像素著色器的輸入匹配頂點(diǎn)著色器的輸出涎跨,它通過(guò)沿整個(gè)三角形插值得到洼冻。這個(gè)像素著色器有兩個(gè)變化輸入:表面位置和表面法線,兩者都在應(yīng)用程序的世界坐標(biāo)系中隅很。統(tǒng)一輸入的數(shù)量要更多一些撞牢,所以為了簡(jiǎn)潔,我們只展示其中兩個(gè)與光源相關(guān)的定義:
struct Light{
vec4 position;
vec4 color;
};
uniform LightUBlock{
Light uLights[MAXLIGHTS];
};
uniform uint uLightCount;
由于光源是點(diǎn)光源外构,所以光源的定義包括位置和顏色普泡,它們被定義為vec4是為了符合GLSL的std140數(shù)據(jù)布局標(biāo)準(zhǔn)。盡管审编,在這個(gè)例子中撼班,std140布局會(huì)導(dǎo)致空間的浪費(fèi),但它簡(jiǎn)化了在CPU和GPU間確定連續(xù)數(shù)據(jù)布局的任務(wù)垒酬,這可以讓我們很簡(jiǎn)單地使用砰嘁。Light結(jié)構(gòu)體數(shù)組被定義在一個(gè)統(tǒng)一塊中件炉,這是GLSL的一個(gè)特性,用來(lái)綁定一組統(tǒng)一變量到一個(gè)緩沖對(duì)象上矮湘,這樣就可以更快地傳輸數(shù)據(jù)斟冕。數(shù)組長(zhǎng)度被定義為一個(gè)應(yīng)用程序允許的最大光源數(shù),我們之后會(huì)看到應(yīng)用程序會(huì)在著色器編譯前將這個(gè)字符串替換為數(shù)字缅阳,也就是預(yù)處理命令磕蛇。統(tǒng)一整型變量uLightCount是繪制命令中要使用的確定數(shù)量光源。
接下來(lái)十办,我們看一下像素著色器的代碼:
vec3 lit(vec3 l, vec3 n, vec3 v){
vec3 r_l = reflect(-l, n);
float s = clamp(100.0 * dot(r_l, v) - 97.0, 0.0, 1.0);
vec3 highlightColor = vec3(2, 2, 2);
return mix(uWarmColor, hightlightColor, s);
}
void main(){
vec3 n = normalize(vNormal);
vec3 v = normalize(uEyePosition.xyz - vPos);
outColor = vec4(uFUnlit, 1.0);
for(uint i = 0u; i < uLightCount; i++){
vec3 l = nomalize(uLights[i].position.xyz - vPos);
float NdL = clamp(dot(n, l), 0.0, 1.0);
outColor.rgb += NdL * uLights[i].color.rgb * lit(l, n ,v);
}
}
我們有一個(gè)針對(duì)光照部分的函數(shù)定義秀撇,通過(guò)main函數(shù)調(diào)用。注意我們使用的光照模型完全是上面提到過(guò)的向族。其中的和
作為統(tǒng)一值傳入呵燕,它們是整個(gè)繪制命令中的常量,應(yīng)用程序可以計(jì)算這些值件相,節(jié)省一些GPU周期再扭。
這個(gè)像素著色器使用一些內(nèi)置的GLSL函數(shù),refelct()函數(shù)可以用來(lái)反射一個(gè)矢量夜矗,clamp()函數(shù)有三個(gè)參數(shù)泛范,可以將第一個(gè)參數(shù)的值階段在后面兩個(gè)參數(shù)設(shè)置的范圍內(nèi),對(duì)應(yīng)HLSL方法是saturate()侯养。mix()函數(shù)是一個(gè)線性插值函數(shù)敦跌,擁有三個(gè)參數(shù),可以讓前兩個(gè)值根據(jù)第三個(gè)值設(shè)置的比例進(jìn)行線性插值逛揩,對(duì)應(yīng)HLSL方法是lerp()。最后麸俘,normalize()函數(shù)可以讓矢量的長(zhǎng)度變?yōu)?辩稽,方向不變。
接下來(lái)我們看頂點(diǎn)著色器从媚,這里我們不展示任何統(tǒng)一變量定義逞泄,主要看輸入和輸出變量定義:
layout(location = 0) in vec4 position;
layout(location = 1) in vec4 normal;
out vec3 vPos;
out vec3 vNormal;
注意,在之前提到過(guò)拜效,頂點(diǎn)著色器的輸出匹配像素著色器的變化輸入喷众,這些輸入包括指示數(shù)據(jù)是如何在頂點(diǎn)數(shù)組中排列的,頂點(diǎn)著色器代碼如下:
void main(){
vec4 worldPosition = uModel * position;
vPos = worldPosition.xyz;
vNormal = (uModel * normal).xyz;
gl_Position = viewProj * worldPosition;
}
這是一個(gè)頂點(diǎn)著色器的比較普遍的操作紧憾,著色器將表面位置和法線變換到世界空間到千,然后傳遞到像素著色器供光照著色使用。最后赴穗,表面位置被變換到裁剪空間憔四,接著傳遞到gl_Position中膀息,一個(gè)被光柵器使用的特殊系統(tǒng)定義變量。gl_Position變量是一個(gè)頂點(diǎn)著色器必須有的輸出了赵。
注意法線并沒(méi)有在頂點(diǎn)著色器中標(biāo)準(zhǔn)化潜支,這是因?yàn)樵谠嫉木W(wǎng)格數(shù)據(jù)中它們的長(zhǎng)度已經(jīng)被設(shè)定為1了,并且應(yīng)用程序并沒(méi)有執(zhí)行例如頂點(diǎn)混合或非統(tǒng)一縮放的額外的操作柿汛,這些操作會(huì)不均衡地改變它們的長(zhǎng)度冗酿。模型矩陣可以有一個(gè)統(tǒng)一縮放因數(shù),但這會(huì)成比例的改變所有法線的長(zhǎng)度络断。
應(yīng)用程序使用WebGL API來(lái)進(jìn)行不同的渲染和著色器設(shè)置已烤,每個(gè)可編程著色器階段被單獨(dú)設(shè)置,然后被綁定到一個(gè)程序?qū)ο笾屑搜颍@是像素著色器設(shè)置代碼:
var fSource = document.getElementById("fragment").text.trim();
var maxLights = 10;
fSource = fSource.replace(/MAXLIGHTS/g, maxLights.toString());
var fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
gl.shaderSource(fragmentShader, fSource);
gl.compileShader(fragmentShader);
注意fragmentShader
的引用胯究,這是WebGL的術(shù)語(yǔ),和pixelShader
一樣躁绸,不過(guò)前者在原理上更為正確裕循,但我們?cè)诒緯?shū)還是使用后者作為常用術(shù)語(yǔ)。這個(gè)代碼中也將MAXLIGHTS替換為常量值净刮,大多數(shù)的渲染框架執(zhí)行類似的預(yù)處理著色器操作剥哑。
這里介紹的代碼并不完整,還存在許多例如設(shè)置統(tǒng)一變量淹父、初始化頂點(diǎn)數(shù)組株婴、清除緩沖、繪制等操作暑认,這些可以在API文檔中看到困介。我們這里的目的只是給出著色器是如何被對(duì)待為一些獨(dú)立的處理器的這一概念。
材質(zhì)系統(tǒng)
渲染框架通常不會(huì)只實(shí)現(xiàn)一個(gè)著色器蘸际,通常座哩,目標(biāo)系統(tǒng)需要處理大量不同的材質(zhì),光照模型和著色器粮彤。
在之前的章節(jié)提到過(guò)根穷,一個(gè)著色器是一個(gè)GPU可編程階段的一個(gè)程序,就其本身而言导坟,它是一個(gè)低等的圖形API資源屿良,對(duì)藝術(shù)家來(lái)說(shuō)并不友好,不能直接操作惫周。相反尘惧,一個(gè)材質(zhì)是一個(gè)表面外表的面向藝術(shù)家的封裝結(jié)果,材質(zhì)有時(shí)也可以描述為非視覺(jué)慨念闯两,例如碰撞屬性褥伴,這不在本書(shū)的范圍內(nèi)谅将。
因?yàn)椴馁|(zhì)實(shí)現(xiàn)自著色器,它們并不是一個(gè)簡(jiǎn)單的一對(duì)一的關(guān)系重慢。在不同的渲染場(chǎng)景中饥臂,同樣的材質(zhì)可能使用相同的著色器,一個(gè)著色器可以被多個(gè)材質(zhì)共享似踱。最常見(jiàn)的例子是參數(shù)化材質(zhì)隅熙,其最簡(jiǎn)單的組成,材質(zhì)參數(shù)化需要兩種材質(zhì)實(shí)體:材質(zhì)模板和材質(zhì)實(shí)例核芽。每個(gè)材質(zhì)模板描述一類材質(zhì)囚戚,并且擁有一系列可用于賦予數(shù)字,顏色或紋理值的參數(shù)轧简,每個(gè)材質(zhì)實(shí)例綁定一個(gè)材質(zhì)模板加上一組它的參數(shù)驰坊。一些渲染框架,例如虛幻引擎哮独,允許一個(gè)更復(fù)雜的層階式架構(gòu)拳芙,材質(zhì)模板可以來(lái)自不同等級(jí)的其它模板。
參數(shù)可能在運(yùn)行時(shí)處理皮璧,通過(guò)將統(tǒng)一輸入傳遞給著色器程序舟扎,或在編譯階段,通過(guò)在著色器編譯前替換值悴务。一種編譯階段參數(shù)的通用類型時(shí)使用布爾值來(lái)切換給定材質(zhì)特性的激活狀態(tài)睹限,這可以讓藝術(shù)家通過(guò)復(fù)選框在材質(zhì)系統(tǒng)中實(shí)現(xiàn),例如關(guān)閉在遠(yuǎn)處物體的某些可以忽略的視覺(jué)效果讯檐。
因?yàn)椴馁|(zhì)的參數(shù)可能與光照模型的參數(shù)一對(duì)一綁定棺耍,這通常不是一個(gè)問(wèn)題锡足。一個(gè)材質(zhì)可能會(huì)固定一個(gè)給定的光照模型的參數(shù)造垛,例如將表面顏色變?yōu)橐粋€(gè)常量值斯入。相反,一個(gè)光照模型的參數(shù)可能是使用多種材質(zhì)參數(shù)蕉拢、插值點(diǎn)或紋理數(shù)據(jù)等經(jīng)過(guò)一系列復(fù)雜操作計(jì)算出來(lái)的結(jié)果。在一些例子中诚亚,例如表面位置晕换、表面朝向甚至是事件都可能作為因數(shù)加入計(jì)算≌咀冢基于表面位置和朝向的光照模型在地形材質(zhì)中非常普遍闸准,例如,高度和表面法線可以被用來(lái)控制雪效果梢灭,在高海拔水平處和接近水平表面處混合進(jìn)一個(gè)白色表面顏色夷家≌羝洌基于時(shí)間的材質(zhì)通常用于動(dòng)畫(huà)材質(zhì),例如閃爍的霓虹標(biāo)志库快。
材質(zhì)系統(tǒng)其中一個(gè)最重要的任務(wù)是將不同的著色器函數(shù)分類為獨(dú)立的元素摸袁,然后控制它們的結(jié)合方式。在許多情況下义屏,這種類型的合成非常有用:
- 使用幾何處理過(guò)程來(lái)合成表面著色靠汁,例如剛體變換、頂點(diǎn)混合闽铐、變形蝶怔、細(xì)分曲面、實(shí)例化和裁剪兄墅。這些功能獨(dú)立分布:表面著色器取決于材質(zhì)踢星,幾何處理過(guò)程取決于網(wǎng)格,所以分別控制它們并在材質(zhì)系統(tǒng)中合成會(huì)非常方便隙咸。
- 通過(guò)一些例如像素裁剪和混合操作來(lái)混合表面著色沐悦。這與移動(dòng)GPU的關(guān)聯(lián)性很大,其中的混合通常在像素著色器中執(zhí)行扎瓶。通常是自由選擇這些材質(zhì)操作用于表面著色器所踊。
- 合成操作過(guò)去常用于讓光照模型自己計(jì)算光照模型的參數(shù)。這允許只控制一次光照模型的實(shí)現(xiàn)概荷,就可以使用針對(duì)所計(jì)算的光照模型參數(shù)的不同方法來(lái)重復(fù)進(jìn)行結(jié)合秕岛。
- 將每個(gè)獨(dú)立可選擇的材質(zhì)特性合成在一起,這允許每種特性獨(dú)立的進(jìn)行實(shí)現(xiàn)误证。
- 將光照模型和所計(jì)算的光源估計(jì)的參數(shù)進(jìn)行合成:在著色點(diǎn)對(duì)每個(gè)光源計(jì)算
和
的值继薛。例如延后渲染的技術(shù)可以改變這種合成結(jié)構(gòu)。
如果圖形API提供這種類型的著色器代碼模塊性作為一種核心特性會(huì)很方便愈捅。然而遏考,不同于CPU代碼,GPU著色器不允許對(duì)代碼片段進(jìn)行后置編譯連接蓝谨,每個(gè)著色器階段的程序會(huì)被編譯為一個(gè)單元灌具。著色器階段間的分離提供一些模塊性限制,這會(huì)有些契合我們?cè)谏厦嫣岬竭^(guò)的第一個(gè)元素:使用幾何處理過(guò)程(在非像素著色器階段進(jìn)行)來(lái)合成表面著色(在像素著色器階段進(jìn)行)譬巫。但這并不是完美契合咖楣,因?yàn)槊總€(gè)著色器也會(huì)執(zhí)行其它的操作,其它類型的合成仍需要被處理芦昔。給定這些限制诱贿,能夠讓材質(zhì)系統(tǒng)實(shí)現(xiàn)所有類型合成的唯一方法在源代碼級(jí)別處,這個(gè)方法主要包括一些字符串操作,例如幾何和替換珠十,通常根據(jù)一些C風(fēng)格的指令料扰,#include,#if,#define
完成。
早期焙蹭,渲染系統(tǒng)有一個(gè)相當(dāng)少數(shù)量的著色器變體晒杈,通常每個(gè)著色器可以被手工重寫(xiě),這種方式有一些優(yōu)點(diǎn)壳嚎,例如桐智,每中變體可以通過(guò)最終著色器程序的完整知識(shí)來(lái)優(yōu)化。然而烟馅,這種方式會(huì)因?yàn)樽凅w數(shù)量的增長(zhǎng)而變得不切實(shí)際说庭,當(dāng)將不同的部分和選項(xiàng)考慮在內(nèi)的話,不同可能存在的變體的數(shù)量會(huì)非常大郑趁。這就是為什么模塊性和可結(jié)合性非常重要的原因刊驴。
當(dāng)設(shè)計(jì)一個(gè)處理著色器變體的系統(tǒng)時(shí)第一個(gè)需要解決的問(wèn)題是在不同選項(xiàng)間的選擇是通過(guò)動(dòng)態(tài)分支在運(yùn)行期間執(zhí)行還是通過(guò)條件預(yù)處理在編譯期間執(zhí)行。在較老的硬件中寡润,動(dòng)態(tài)分支不太可能實(shí)現(xiàn)捆憎,或者非常慢,所以運(yùn)行期間的選擇并不能作為一個(gè)選項(xiàng)梭纹。因此變體總是在編譯期間處理躲惰,包括不同數(shù)量和類型燈光的所有可能結(jié)合。
相反变抽,當(dāng)前的GPU可以很好地處理動(dòng)態(tài)分支础拨,尤其是在一個(gè)繪制命令時(shí)對(duì)所有的像素,分支的行為一致绍载。如今大多數(shù)的功能性變體诡宗,例如光源數(shù)量,通常在運(yùn)行期間處理击儡。然而塔沃,向一個(gè)著色器中添加大量的功能性變體會(huì)造成不同的開(kāi)銷:寄存器數(shù)量的增加和相應(yīng)占用量的減少,因此性能會(huì)降低阳谍。所以蛀柴,編譯期間的變體仍是有價(jià)值的,它避免包含了那些不會(huì)被執(zhí)行的復(fù)雜邏輯矫夯。
舉個(gè)例子名扛,想象一下一個(gè)應(yīng)用程序支持三種不同的光源,其中兩種光源類型很簡(jiǎn)單:點(diǎn)光源和平行光茧痒,第三個(gè)類型的光源是普遍的聚光燈,支持平面光照和其它復(fù)雜的特性融蹂,需要相當(dāng)數(shù)量的著色器代碼去實(shí)現(xiàn)旺订。然而弄企,普遍的聚光燈在應(yīng)用程序中的使用相對(duì)較少。在過(guò)去区拳,一個(gè)獨(dú)立的著色器變體會(huì)對(duì)這三種光源的不同排列組合進(jìn)行編譯拘领,其中一種情況是普遍聚光燈的數(shù)量大于或等于1,另一種情況是聚光燈的數(shù)量為0樱调。由于它的更簡(jiǎn)單的代碼约素,第二種變體更傾向于使用更少的寄存器,因此性能更好笆凌。
現(xiàn)代材質(zhì)系統(tǒng)同時(shí)使用運(yùn)行期間和編譯期間著色器變體圣猎。即使任務(wù)并不僅僅在編譯期間完成,整體的復(fù)雜性和變體的數(shù)量會(huì)保持增長(zhǎng)乞而,所以仍有大量的著色器變體需要編譯送悔。例如游戲《命運(yùn):被奪走的國(guó)王》中的部分領(lǐng)域,超過(guò)9000個(gè)編譯變體會(huì)在單幀使用爪模。變體可能的數(shù)量也會(huì)非常大欠啤,例如,Unity渲染系統(tǒng)的每個(gè)著色器由超過(guò)數(shù)千億的變體屋灌。只有實(shí)際使用的變體才會(huì)被編譯洁段,但著色器編譯系統(tǒng)需要進(jìn)行重新設(shè)計(jì)來(lái)處理大量可能的變體。
材質(zhì)系統(tǒng)設(shè)計(jì)者使用不同的策略來(lái)實(shí)現(xiàn)這些目的共郭。雖然這些策略常表現(xiàn)為相互排斥的系統(tǒng)架構(gòu)祠丝,但仍可以在一個(gè)系統(tǒng)中結(jié)合在一起,這些策略包括:
- 代碼重用——在分享文件中實(shí)現(xiàn)函數(shù)落塑,使用
#include
預(yù)處理命令來(lái)從任何需要的著色器中定位函數(shù)纽疟。 - 相減——一種著色器,經(jīng)常稱為超級(jí)著色器憾赁,集合了大量的功能污朽,使用了編輯期間預(yù)處理器條件語(yǔ)句和動(dòng)態(tài)分支的組合來(lái)移除不使用的部分,并在互相排斥的替換品間切換龙考。
- 相加——部分不同的功能被定義為擁有輸入和輸出連接的節(jié)點(diǎn)蟆肆,然后將這些結(jié)合起來(lái)。這和代碼重用結(jié)構(gòu)類似晦款,但更加的結(jié)構(gòu)化炎功。節(jié)點(diǎn)的結(jié)合可以通過(guò)文本或可視化圖標(biāo)編輯器完成,后者適合非工程師使用缓溅。通常只有著色器的一部分可以暴露在可視化圖表種蛇损,例如,在虛幻引擎的圖標(biāo)編輯器中,只可以影響光照模型的輸入淤齐。
- 基于模板——一個(gè)界面被定義股囊,只要符合界面的設(shè)計(jì)的話就可以插入不同的實(shí)現(xiàn)。這比相加策略更加的正式更啄,通常用于更大塊的功能稚疹。一個(gè)這種界面的通用的例子是將光照模型參數(shù)的計(jì)算和光照模型的計(jì)算本身分離。虛幻引擎擁有不同的材質(zhì)域祭务,包括計(jì)算光照模型參數(shù)的表面域和計(jì)算標(biāo)量值的光源函數(shù)域内狗。一種類似的表面著色器架構(gòu)同樣存在于Unity中。注意延遲渲染技術(shù)使用類似的架構(gòu)义锥,將G緩沖用作界面柳沙。
對(duì)于更多的特殊例子,《WebGL Insights》這本書(shū)的部分章節(jié)討論了不同的引擎如何控制它們的著色器管線缨该。除合成外偎行,也存在一些其它對(duì)于現(xiàn)在材質(zhì)系統(tǒng)的重要設(shè)計(jì),例如使用最少?gòu)?fù)用代碼來(lái)支持不同的平臺(tái)的需求贰拿。這包括使用不同方法變體來(lái)對(duì)不同平臺(tái)蛤袒、著色器語(yǔ)言和API來(lái)解釋性能。游戲《命運(yùn)》的著色器系統(tǒng)是解決這類問(wèn)題的代表方法膨更。它使用一種專門(mén)的預(yù)處理器層妙真,其可以使用自定義的著色器語(yǔ)言來(lái)編寫(xiě)著色器。這允許通過(guò)自動(dòng)轉(zhuǎn)換為不同的著色器語(yǔ)言和實(shí)現(xiàn)來(lái)適應(yīng)不同的平臺(tái)荚守。虛幻和Unity擁有類似的系統(tǒng)珍德。
材質(zhì)系統(tǒng)同樣需要確保性能,除了特殊化著色變體的編譯矗漾,也存在一些其它的公用優(yōu)化材質(zhì)系統(tǒng)的方法锈候。《命運(yùn)》材質(zhì)系統(tǒng)和虛幻引擎可以檢測(cè)繪制命令中常量的計(jì)算敞贡,然后將它放在著色器外泵琳。另一個(gè)例子是在《命運(yùn)》中使用的作用范圍系統(tǒng),用來(lái)區(qū)別不同更新頻率的常量誊役,并在合適的時(shí)間更新每個(gè)常量集來(lái)節(jié)省API的開(kāi)支获列。
正如我們所看到的,實(shí)現(xiàn)一個(gè)著色等式要決定以下的問(wèn)題:哪一部分應(yīng)該要簡(jiǎn)化蛔垢,計(jì)算不同等式的頻率如何击孩,用戶要如何修改和控制表現(xiàn)。最終渲染管線的輸出是顏色和混合值鹏漆,剩下的小節(jié)會(huì)講解抗鋸齒巩梢、透明和圖像顯示创泄。