Catlike Coding CustomSRP部分的練習(xí)筆記紧唱,記錄了工程思路、知識點和一些注意事項隶校。跟隨的中文翻譯版本漏益。英文原教程頁面這里
4. 方向光陰影
陰影的實現(xiàn):1.渲染陰影貼圖 2.采樣陰影貼圖
- 渲染陰影:使用Shadow Map實現(xiàn)場景陰影。在渲染場景前深胳,先使用LightMode為ShadowCaster的Pass遭庶,通過光源對場景取景,將光源可見場景中最近的片元深度信息保存到ShadowMap中稠屠,真實渲染過程中,將渲染的片元轉(zhuǎn)換到光源相機空間計算深度值翎苫,判斷在不在陰影范圍內(nèi)权埠。
- 渲染陰影前,進(jìn)行一些屬性配置煎谍,比如渲染陰影的最大距離和陰影貼圖的大小攘蔽。如果把攝像機看到的物體全部進(jìn)行陰影繪制,那么性能開銷大呐粘,陰影貼圖也需要很大尺寸满俗。所以設(shè)置最大距離100转捕,陰影貼圖的大小設(shè)置一組枚舉。把陰影設(shè)置看作是管線的設(shè)置之一唆垃,在PipelineAsset中設(shè)置五芝。作為渲染參數(shù)傳入CameraRenderer,在相機裁剪中配置陰影距離辕万,將相機遠(yuǎn)平面和最大陰影距離中的小值作為最大陰影距離枢步,在燈光中設(shè)置陰影距離。
-
使用陰影類配置陰影渐尿。在遍歷可見光時醉途,保存可見光的陰影數(shù)據(jù)。當(dāng)可投影光源數(shù)量小于上限&&光源開啟陰影&&陰影強度大于0&&場景中有一個陰影投射物受到了光源影響砖茸,滿足以上條件時隘擎,將可見光記錄進(jìn)投影光源列表中。
這里使用了bool cullingResults.GetShadowCasterBounds(int index, out Bounds b)
方法判斷光源是否影響了陰影投射對象凉夯。如果光源影響了場景中至少一個陰影投射對象货葬,則為true,out bounds返回封裝了可見陰影投射物的包圍盒恍涂。注意宝惰,此處index是光線在場景中的索引,不應(yīng)該僅僅是方向光的索引再沧,在未來還會增加非方向光的陰影尼夺,非方向光源也會應(yīng)用到這里的索引。 - 渲染陰影炒瘸。創(chuàng)建渲染紋理淤堵,并指定紋理類型是ShadowMap,記得要在相機渲染完后釋放臨時渲染紋理顷扩。指定渲染紋理存儲在RenderTarget中拐邪,否則會存儲到幀緩沖中。調(diào)整陰影渲染時機隘截,在渲染場景前渲染陰影扎阶。
渲染陰影時,循環(huán)遍歷所有光線婶芭,為所有保存在投影光源列表中的光線渲染陰影东臀。要為單個光線渲染陰影,首先要創(chuàng)建一個ShadowDrawingSettings(cullingResults, lightIndex)
實例犀农,用來創(chuàng)建陰影對象惰赋,它需要剔除結(jié)果和可見光索引作為構(gòu)造參數(shù)。陰影圖本質(zhì)也是一張深度圖呵哨,它記錄了從光源出發(fā)赁濒,能看到的場景中距離它最近的表面位置(深度信息)轨奄。但是方向光沒有真實位置,我們要找出與光源方向相匹配的視圖和投影矩陣拒炎,并給我們一個裁剪空間立方體挪拟,這個立方體與包含光源陰影的攝影機的可見區(qū)域重疊,這些數(shù)據(jù)的獲取可以直接調(diào)用cullingResults中的方法cullingResults.ComputeDirectionalShadowMatricesAndCullingPrimitives(..., out Matrix4x4 viewMatrix, out Matrix4x4 projMatrix, out ShadowSplitData splitData
獲取光源空間的視圖變換和投影變換矩陣枝冀,以及陰影剔除信息舞丛。為渲染過程設(shè)置視圖和投影變換矩陣,然后執(zhí)行context.DrawShadows(ref shadowSettings)
方法渲染投射陰影果漾。 -
編寫ShadowCasterPass球切。因為
context.DrawShadows
方法渲染陰影只會渲染Shader中帶有ShadowCasterPass的物體,我們需要為Shader編寫ShadowCasterPass绒障。因為這個Pass只寫入深度數(shù)據(jù)吨凑,所以為Pass添加ColorMask 0表示不寫入任何顏色數(shù)據(jù)。我們只需要得到頂點在裁剪空間的位置和裁剪的基礎(chǔ)顏色户辱,片元函數(shù)的作用只是裁剪不滿足閾值的片元鸵钝。 -
多光源陰影投射。為每個平行光都在陰影圖集中分配一個獨立的圖塊庐镐,當(dāng)燈光數(shù)小于1恩商,圖塊不拆分,大于一就拆分成4塊必逆。為每個平行光計算各自的圖塊在陰影圖集上的坐標(biāo)偏移量怠堪。為每個光線渲染ShadowMap時,我們通過
buffer.SetViewport(Rect(x,y,width,height))
調(diào)整視口的位置來將渲染位置放在圖集的相應(yīng)圖塊上名眉。
- 采樣陰影
- 保存陰影轉(zhuǎn)換矩陣粟矿。我們要將片元轉(zhuǎn)換到光照空間,找到對應(yīng)的紋理坐標(biāo)损拢,所以要將每個光源的陰影轉(zhuǎn)換視圖和投影矩陣保存下來陌粹,將其發(fā)送到GPU。
- 將陰影貼圖圖塊相關(guān)信息也要保存并發(fā)送給GPU福压,如陰影圖塊偏移掏秩、圖塊拆分?jǐn)?shù)量、視圖投影矩陣等荆姆。將陰影變換矩陣的視口映射到陰影紋理圖集空間蒙幻,并找到正確tile上。首先判斷當(dāng)前圖形API胞枕,如果使用了反向z-buffer將矩陣的z反轉(zhuǎn),由于深度緩沖精度有限魏宽,能表示的深度數(shù)量有限腐泻,反轉(zhuǎn)能更好地利用這些位(儲存為unsigned形式)OpenGL中0是0深度决乎,1是最大深度,Dx中0是0深度派桩,-1是最大深度构诚。
- 獲取陰影數(shù)據(jù)。在保存方向光陰影時記錄陰影強度和陰影圖塊的偏移(光源id)铆惑。將所有的方向光陰影數(shù)據(jù)發(fā)送到GPU范嘱。在shader中接收所有的方向光陰影數(shù)據(jù)。
- 正式采樣陰影圖集员魏。創(chuàng)建shadows.hlsl專門對陰影圖集采樣丑蛤。陰影圖集不是常規(guī)紋理,所以使用TEXTURE2D_SHADOW宏定義陰影圖集撕阎。我們將使用一個特殊的SAMPLER_CMP宏來定義采樣器狀態(tài)受裹,這個宏定義了一種不同的方式來采樣陰影貼圖,因為常規(guī)的雙線性過濾對深度數(shù)據(jù)沒有意義虏束。我們對陰影貼圖進(jìn)行采樣的方式就是比較深度值棉饶,因此我們可以定義一個顯式的采樣器狀態(tài),將單個分量與指定的比較值進(jìn)行比較镇匀,而不是依賴Unity推導(dǎo)的渲染紋理狀態(tài)照藻。通過唯一的名字,我們可以內(nèi)聯(lián)定義一個采樣器狀態(tài)汗侵,這里我們使用名字是sampler_linear_clamp_compare幸缕,同時為其定義了一個簡寫的SHADOW_SAMPLER宏。
- 通過SAMPLE_TEXTURE2D_SHADOW宏對陰影圖及采樣晃择,它和其他采樣器一樣冀值,需要陰影圖集、采樣器和片元在紋理空間的位置宫屠。
- 注意:采樣陰影圖集的結(jié)果是根據(jù)有多少光到達(dá)表面決定的列疗,它是[0,1]區(qū)間的值,通常叫做陰影衰減因子浪蹂,如果片元完全被陰影覆蓋就是0抵栈,如果沒有任何陰影覆蓋就是1,之間表示被部分陰影遮擋坤次。還要考慮古劲,當(dāng)燈光的陰影強度為0時,陰影衰減值永遠(yuǎn)是1缰猴,也就是沒有陰影覆蓋产艾,此時就與陰影采樣無關(guān)了。所以最終結(jié)果是陰影采樣被燈光陰影強度插值的結(jié)果。
- 獲取燈光陰影衰減闷堡。為燈光新增陰影衰減屬性隘膘。獲取CPU傳來的陰影強度和圖塊位置。將shadow.hlsl計算得到的陰影衰減傳遞給燈光杠览。
- 計算燈光時弯菊,將燈光的陰影衰減添加到入射光強度中。
注意:此時的陰影會有很強的自陰影踱阿,需要后續(xù)優(yōu)化管钳。
- 級聯(lián)陰影
這時的陰影貼圖通常會有透視走樣的問題。透視走樣指越靠近相機软舌,邊緣的鋸齒化就會越嚴(yán)重才漆,因為陰影貼圖的分辨率是固定的,同樣大小的陰影所對應(yīng)的陰影貼圖中的紋素大小也是固定的(陰影貼圖使用正交投影葫隙,因此陰影貼圖中的每個紋素都有固定的世界空間大心┕骸)阎曹。如果使用透視相機书聚,其效果是近大遠(yuǎn)小抹锄,在渲染時,陰影越靠近相機糟描,越容易出現(xiàn)多個片元從陰影貼圖中的同一紋素進(jìn)行采樣的情況怀喉。這幾個片元是同一陰影值,從而產(chǎn)生鋸齒邊船响。提高分辨率可以緩解問題躬拢,但無法解決。(本質(zhì)原因:光源照射方向與法線方向不一致见间,導(dǎo)致光源到表面的深度會有鋸齒狀波動聊闯。)
(和MIPMAP的原因一樣)
- 設(shè)置級聯(lián)。在陰影設(shè)置中設(shè)置級聯(lián)數(shù)量和每層級聯(lián)對應(yīng)的級聯(lián)比例米诉。
- 渲染級聯(lián)菱蔬。每個級聯(lián)都需要自己的陰影轉(zhuǎn)換矩陣,所以需要調(diào)整陰影轉(zhuǎn)換矩陣的大小史侣。為每個方向光保存多個連續(xù)的陰影圖塊作為級聯(lián)陰影拴泌。在渲染每個光源陰影時,循環(huán)遍歷級聯(lián)索引惊橱,獲得每個級聯(lián)陰影的視圖投影矩陣和陰影剔除數(shù)據(jù)蚪腐。
- 級聯(lián)包圍球。為子視截體構(gòu)建投影矩陣時税朴,要在生成的陰影貼圖中(陰影貼圖空間)回季,并盡可能減少當(dāng)前不在視野內(nèi)的無關(guān)區(qū)域家制。也就是說,要盡可能計算出與子視截體緊密貼合的投影矩陣泡一,投影矩陣用正交投影(相當(dāng)于一個長方體AABB包圍盒)慰丛,投影空間是一個能包住子視截體的包圍盒。但因為在渲染時瘾杭,攝像機的位置和朝向等屬性會及時改變,每個層級的子視截體也在不停變化哪亿,正交投影包圍盒也在變化粥烁,這樣就可能導(dǎo)致前后兩幀包圍盒發(fā)生突變,進(jìn)而使生成的陰影貼圖分辨率突變蝇棉,產(chǎn)生陰影抖動問題讨阻。解決方法是把包圍盒改成包圍球。包圍球的缺點是視截體外的空間也被包含篡殷,會在剔除區(qū)域以外也看到陰影钝吮。因為每個方向光的方向和球無關(guān),所以每個方向光都可以使用同一個包圍球板辽,所以我們需要保存級聯(lián)等級的數(shù)量個包圍球數(shù)據(jù)即可奇瘦。因此:我們需要知道這些球體應(yīng)該從哪個級聯(lián)中采樣,因此在shader中定義級聯(lián)數(shù)量劲弦、包圍球數(shù)組(vec4耳标,xyz位置,w半徑)
- 采樣級聯(lián)邑跪。將級聯(lián)數(shù)量次坡、級聯(lián)包圍球數(shù)據(jù)、級聯(lián)陰影轉(zhuǎn)換矩陣在shader中接收画畅。新建ShadowData數(shù)據(jù)砸琅,保存級聯(lián)索引。計算級聯(lián)等級轴踱,根據(jù)表面位置和級聯(lián)包圍球球心距離和級聯(lián)包圍球半徑的大小關(guān)系症脂,找到相應(yīng)的級聯(lián)等級。
- 級聯(lián)剔除寇僧。在計算級聯(lián)等級時摊腋,當(dāng)級聯(lián)等級超過最大的級聯(lián)等級時,說明超過陰影范圍了嘁傀,要剔除兴蒸。在ShadowData新增陰影強度數(shù)據(jù),當(dāng)超過陰影范圍時细办,陰影強度設(shè)置為0橙凳。同樣蕾殴,當(dāng)片元深度超過最大陰影深度時,也要進(jìn)行陰影剔除岛啸。所以在shader中接收CPU傳來的最大陰影深度钓觉,在片元函數(shù)中保存表面在視圖空間中的深度,當(dāng)表面深度大于最大陰影深度時坚踩,將陰影強度同樣設(shè)置為0.
-
陰影過渡荡灾。使用公式
獲得過渡階段的陰影強度,以淡化陰影瞬铸。
為表面的深度批幌,
是最大陰影距離,
為陰影過渡范圍嗓节。在shadowsetting中引入陰影過渡范圍荧缘,并將陰影過渡范圍和最大陰影距離一起發(fā)送給GPU。
-
級聯(lián)過渡拦宣。與陰影過度類似截粗,在最后一個級聯(lián)邊緣對陰影進(jìn)行平滑過渡。使用公式:
鸵隧,
表面深度绸罗,
包圍球半徑,
過渡參數(shù)豆瘫。
- 陰影質(zhì)量
目前實現(xiàn)的陰影有嚴(yán)重的自陰影現(xiàn)象从诲,陰影滲漏(shadow acne)。產(chǎn)生陰影滲漏的主因是陰影貼圖分辨率問題靡羡,如果分辨率比較小系洛,導(dǎo)致場景中多個片元在計算陰影的時候?qū)?yīng)上了同一個紋素,導(dǎo)致判斷該點有沒有陰影時出現(xiàn)了問題略步。假設(shè)光線方向與表面法線方向不一致描扯,那么就會導(dǎo)致對表面上四個片元來說對應(yīng)同一陰影紋素,都要與此陰影紋素對比表面深度趟薄,由于光線方向與表面法線方向不一致绽诚,而自然會有兩個片元深度比該紋素淺,不被陰影遮擋杭煎,而兩個片元深度比紋素深恩够,從而被遮擋。提高陰影分辨率可以減小陰影滲漏現(xiàn)象羡铲,但不能解決蜂桶。
- 調(diào)整陰影偏差Bias。計算片元深度時也切,減去一個偏差值扑媚,使原來比紋素深度大的兩個片元也能深度小于陰影紋素腰湾,去掉了自陰影。問題:難以定量地針對當(dāng)前被照明物體的表面凹凸程度設(shè)置準(zhǔn)確的偏差值疆股。Unity使用的是基于物體斜度比例的深度偏差值费坊。大部分改善對陰影深度貼圖采樣誤差的算法,核心思想是分析待繪制場景中各部分內(nèi)容對采樣誤差的影響程度旬痹。unity默認(rèn)設(shè)置(1.0 1.0)附井,自測(1.0,2.0)好用。深度偏差會帶來影物漂移两残,我們不用
- 級聯(lián)數(shù)據(jù)羡忘。自陰影的大小與世界空間的陰影紋素有關(guān),不同級聯(lián)的紋素大小不一樣磕昼,所以需要向GPU發(fā)送更多數(shù)據(jù)。
- 法線偏差:在采樣陰影時节猿,使表面法線方向偏移一些票从,然后對表面的一點進(jìn)行采樣,如果距離足夠遠(yuǎn)就可以避免陰影滲漏滨嘱。這會讓陰影位置發(fā)生稍微改變峰鄙,比如邊緣不齊或假陰影,但不會導(dǎo)致影物漂移太雨。做法:沿表面法線稍微移動表面位置吟榴。如果只考慮一個維度,那么移動距離等于世界空間中一個紋素大小即可囊扳。紋素大小計算:包圍球直徑/陰影貼圖尺寸吩翻。法線偏移計算:法線*紋素大小,然后加上表面位置即為偏移后的表面位置锥咸。
- 可調(diào)節(jié)的偏差:光源組件上有bias和normalBias兩個屬性狭瞎,將bias當(dāng)作斜度比例偏差值也就是
buffer.SetGlobalDepthBias
第二個參數(shù),然后把normalBias也參與到計算好的法線偏差中搏予,使法線偏差可以自由調(diào)節(jié)熊锭。需要注意,創(chuàng)建新方向光都要根據(jù)實際情況調(diào)節(jié)雪侥,否則會出現(xiàn)嚴(yán)重的影物漂移碗殷。
總結(jié)最終的自陰影的解決方法:
- 斜度比例偏差。通過設(shè)置
buffer.SetGlobalDepthBias(0f, light.slopeScaleBias);
來獲取光源的斜度偏差數(shù)據(jù)速缨,設(shè)置斜度偏差锌妻。渲染完畢陰影后要將深度偏差復(fù)原。 - 法線偏差旬牲。計算紋素大小从祝,并使頂點位置沿法線偏移一個紋素大小襟己,燈光屬性里還有一個法線偏差參數(shù),作為法線偏差距離的縮放參數(shù)牍陌。
float3 normalBias = surface.normal * (dirShadowData.normalBias * _CascadeData[shadowData.cascadeIndex].y);
法線偏差距離 = 表面法線光線法線偏差縮放參數(shù)紋素大小擎浴。
計算表面在紋理空間的位置時,把表面世界空間位置加上法線偏差距離毒涧,得到偏移后的表面位置贮预,用偏移后的表面位置參與紋理空間的位置的計算。
- 陰影平墜
陰影平墜:導(dǎo)致陰影滲漏的另一個潛在問題是Unity應(yīng)用了陰影平墜(shadow pancaking)技術(shù)契讲,那么可以剔除不希望看到的陰影仿吞。Idea是:渲染方向光陰影時,通過剪裁光照空間捡偏,給該空間設(shè)定陰影視錐體近裁剪平面唤冈,只有在近平面以內(nèi)的物體才能投射陰影,且陰影視錐體近裁剪平面會盡可能向前移動(排除了不可見的陰影投射物)银伟,意在減少光照空間范圍你虹,從而提高陰影貼圖精度,減少陰影滲漏彤避。但是問題在于:對于穿過陰影視錐體裁剪平面的物體傅物,會帶來瑕疵,產(chǎn)生不正確的陰影琉预。Unity通過調(diào)整QualitySettings中的ShadowNearPlaneOffset避免發(fā)生這個問題董饰。
- 在ShadowCasterPass中,通過在頂點函數(shù)中將頂點位置限制在陰影視錐體近平面內(nèi)以解決問題圆米。陰影視錐體內(nèi)的頂點卒暂,如果超出了視錐體近平面,那么就讓它深度貼在近平面上娄帖。以防止近平面漏光介却。
- 這對在近平面兩側(cè)的陰影投射非常有效,但是對于大型物體來說只影響了部分頂點块茁,而與近平面有穿插的投影會變形齿坷。我們通過把視錐體近平面往后拉一下來緩解這個問題,為方向光定義近裁剪平面偏移屬性数焊。
- 百分比切近濾波(PCF)
陰影鋸齒問題:鋸齒原因在于永淌,判斷片元是否在陰影內(nèi),進(jìn)行深度測試時佩耳,要把該片元從當(dāng)前攝像機的觀察空間轉(zhuǎn)移到光源空間遂蛀,轉(zhuǎn)換矩陣不同,且陰影貼圖分辨率不大干厚,導(dǎo)致觀察空間多個片元對應(yīng)一個紋素李滴。最直接的方法就是提高陰影貼圖分辨率螃宙,但內(nèi)存占用大,而且無法解決問題所坯。實際開發(fā)中通過適當(dāng)?shù)姆直媛?區(qū)域采樣的方法改善鋸齒現(xiàn)象谆扎。因為陰影貼圖的紋素存儲的不是顏色信息而是深度信息,對深度取均值會產(chǎn)生不正確的結(jié)果芹助,所以鋸齒不能通過局部均值方式消除堂湖。百分比切近濾波(Percentage close filtering,PCF)方法是對陰影比較測試后的值進(jìn)行濾波状土,可以使生成的陰影邊緣平滑柔和无蜂。
片元著色器中,將當(dāng)前片元先轉(zhuǎn)換到光源空間蒙谓,然后經(jīng)過通過投影和視口變換到陰影深度貼圖空間斥季,假設(shè)變換后深度為z,貼圖坐標(biāo)為(u累驮,v)酣倾,對應(yīng)紋素坐標(biāo)為z0.如果不使用PCF,那么就會根據(jù)z和z0的大小判斷片元在陰影中是全黑還是不黑慰照。PCF對(u,v)處周圍紋素也采樣獲取深度值琉朽,如果周圍紋素深度值小于當(dāng)前片元深度值毒租,表示更靠前,不在陰影中箱叁,在卷積核中置0墅垮,否則置1,然后求此卷積核的平均值耕漱,作為柔滑邊緣的系數(shù)算色。
PCF步驟:
- CustomLit Pass中添加濾波關(guān)鍵字
- 陰影設(shè)置中設(shè)置濾波設(shè)置
- 根據(jù)濾波設(shè)置,激活對應(yīng)的關(guān)鍵字
- 傳遞陰影圖集大小和紋素大小
- 引用ShadowSamplingTent.hlsl定義采樣方法和采樣點數(shù)量螟够。
- 調(diào)用采樣方法使用大尺寸的PCF會使陰影變平滑灾梦,但會出現(xiàn)陰影滲漏現(xiàn)象(原因是PCF超采樣又使一個紋素對應(yīng)多個片元了)。需要使法線偏差匹配濾波模式的尺寸妓笙。
烘焙光照
場景中的間接光照信息通過烘焙獲取若河,靜態(tài)物體的間接光照信息烘焙到光照貼圖、用于照明動態(tài)物體的信息是烘焙到光照探針寞宫。間接光照是全局光照的一部分萧福,光線通過環(huán)境或自發(fā)光物體表面照射而來。
- 烘焙靜態(tài)光照
- 設(shè)置場景的光照設(shè)置辈赋。將LightSetting中MixedLighting中選擇Baked Indirect光照模式鲫忍,勾選Baked Global Illumination膏燕。將需要貢獻(xiàn)間接光照的物體勾選Mesh Renderer組件上的Contribute Global Illumination,該對象就能夠作為光線反射的對象悟民,提供間接照明坝辫。
- 采樣烘焙光照。定義GI.hlsl逾雄,定義struct GI和內(nèi)部的漫反射顏色阀溶。間接光照的來源是不固定的,因此只能用于漫反射鸦泳。而鏡面反射通常通過反射探針實現(xiàn)银锻。
- 光照貼圖的UV坐標(biāo):需要首先由unity將其發(fā)送到著色器中,需要管線對每個烘焙了光照信息的物體都這樣做做鹰。drawingSetting中可以設(shè)置PerObjectdata. Lightmaps击纬。litshader中添加LIGHTMAP_ON的編譯指令。光照貼圖是頂點數(shù)據(jù)的一部分钾麸,在頂點和片元結(jié)構(gòu)體中使用宏定義它更振。光照貼圖的UV坐標(biāo)由Unity為每個Mesh生成,將Mesh平鋪展開饭尝,像紋理一樣且不重疊不拉伸不旋轉(zhuǎn)肯腕,以便將其映射到紋理坐標(biāo),然后使物體均勻且不重疊地按照縮放和偏移放置在這個貼圖中钥平,就像BaseUV中應(yīng)用縮放和偏移一樣实撒。在UnityPerDraw中定義光照貼圖的UV轉(zhuǎn)換屬性,動態(tài)st是為了避免兼容問題而導(dǎo)致SRP批處理中斷涉瘾。將光照貼圖UV應(yīng)用UV ST偏移知态。
-
采樣光照貼圖。使用
SampleSingleLightmap
方法對光照貼圖采樣立叛,SampleSingleLightmap的參數(shù)负敏,第一個是TEXTURE2D_ARGS宏,需要將光照貼圖和采樣器作為參數(shù)秘蛇,第二個是UV坐標(biāo)其做,第三個是UV縮放和偏移,第四個是bool值表示是否壓縮了光照貼圖赁还,通過UNITY_LIGHTMAP_FULL_HDR來判斷庶柿,最后參數(shù)包含了解碼指令的float4類型變量
SampleSingleLightmap(TEXTURE2D_ARGS(unity_Lightmap, samplerunity_Lightmap),lightMapUV, float4(1.0,1.0,0.0,0.0),
#if defined(UNITY_LIGHTMAP_FULL_HDR)
false,
#else
true,
#endif
float4(LIGHTMAP_HDR_MULTIPLIER, LIGHTMAP_HDR_EXPONENT,0.0,0.0));
- 光照探針
光照貼圖無法作用在非靜態(tài)物體上,運動的場景和物體就顯得不協(xié)調(diào)秽浇,所以我們使用光照探針模擬光照貼圖效果浮庐。
原理是:在某一光照探針的所在位置點上對光照信息進(jìn)行采樣,然后從該光照探針相鄰的其他光照探針的位置上對光照信息進(jìn)行采樣,把這些采樣得到的光照信息插值運算审残,可以得出光照探針之間某個位置的光照信息梭域。運行期間這些插值的速度很快,可以得到實時渲染的要求搅轿。
從實現(xiàn)的角度來說病涨,光照探針對照亮在3D空間中的某個指定點的光照信息在運行前的預(yù)計算階段進(jìn)行采樣,然后把這些信息通過球諧函數(shù)編碼打包儲存璧坟。游戲運行時既穆,著色器程序可以把這些光照信息編碼快速地重建出原始光照效果。光照探針同光照貼圖一樣儲存場景中的照明信息雀鹃,不同之處在于光照貼圖存儲的是光線照射到場景物體表面的照明信息幻工,而光照探針則存儲的是穿過場景中空白空間的光線信息。光照探針之間的連線表示在空間中光線的傳遞路徑黎茎。光照探針的限制是:如果要處理光照的高頻信息囊颅,球諧函數(shù)的階數(shù)就要增大,提升階數(shù)性能耗費增加傅瞻,因此Unity使用3階球諧函數(shù)踢代,忽略光的高頻信息。最簡單的光照探針布局方式是將光照探針排列成一個規(guī)則的3D網(wǎng)格央視嗅骄,這樣的設(shè)置方法簡單高效胳挎,但會消耗大量內(nèi)存,因為光照探針本質(zhì)上是個球形的且記錄了當(dāng)前采樣點周圍環(huán)境的紋理圖像溺森。如果一片區(qū)域的照明信息都差不多那也沒必要使用大量光照探針慕爬,光照探針通常用于照明效果突然改變的場合。
球諧函數(shù)部分請看GAMES202 IBL部分儿惫。
- 采樣光照探針澡罚。光照探針的插值數(shù)據(jù)需要逐對象地傳給GPU伸但,drawsettings中設(shè)置肾请。在UnityPerDraw緩沖區(qū)中定義7個float4類型的變量來接受光照探針數(shù)據(jù),它們是代表紅色更胖、綠色和藍(lán)色的多項式組件铛铁,命名為unity_SH([AB][rgb])|C。在GI文件中對光照探針進(jìn)行采樣光照探針却妨,如果對象正在使用光照貼圖則直接返回0饵逐,否則返回max(0,SampleSH9()),該方法用于采樣光照探針信息彪标,需要光照探針數(shù)據(jù)和表面法線數(shù)據(jù)作為傳參倍权。
- 光照探針代理體(LPPV)
光照探針代理體(LPPV):在3D空間的位置點上,因為有且只有一個球面表達(dá)式用于描述光照捞烟,所以光照探針照明還不適合用于描述光線穿過一個很大物體的情況薄声,因為這種情況光照會發(fā)生很多的變動当船,從而無法精準(zhǔn)地模擬。光照探針適合小物體默辨,它的照明是基于一個點因此不適用于大物體德频。另一個限制是,因為球諧函數(shù)是在一個球面上對光照信息進(jìn)行編碼缩幸,所以對于有大型的平坦的表面的物體或者有凹面的物體壹置,光照探針照明技術(shù)也不適用。如果想在大物體上用光照探針照明表谊,則需使用光照探針代理體(Light Probe Proxy Volume)組件钞护。LPPV是“解決無法直接用光探針技術(shù)處理大型動態(tài)游戲?qū)ο髥栴}”的組件。
- 給物體掛載LPPV铃肯,并配置
- 采樣LPPV患亿,用drawingsettings將數(shù)據(jù)發(fā)送給GPU,UnityPerDraw定義LPPV的四個屬性 押逼。
- 判斷是否使用了LPPV或光照探針步藕,如果使用了,則必須通過SampleProbeVolumeSH4方法對LPPV采樣挑格。
SampleProbeVolumeSH4(TEXTURE3D_ARGS(unity_ProbeVolumeSH, samplerunity_ProbeVolumeSH), s.position, s.normal,
unity_ProbeVolumeWorldToObject, unity_ProbeVolumeParams.y, unity_ProbeVolumeParams.z, unity_ProbeVolumeMin.xyz,
unity_ProbeVolumeSizeInv.xyz);
3.判斷并采樣對LPPV進(jìn)行采樣需要對代理體的空間進(jìn)行轉(zhuǎn)換咙冗,以及一些其他的計算,例如代理體紋理采樣和球諧函數(shù)應(yīng)用等漂彤,這種情況下使用L1球諧函數(shù)可能結(jié)果會不太準(zhǔn)確雾消,但可能因單個物體表面而異。
- Meta Pass處理漫反射
因為間接漫反射光照會從表面反射出來挫望,因此還應(yīng)該受到這些表面的漫反射率的影響立润。Unity使用一個特殊的Meta Pass來確定烘焙時從表面反射的光照,然后提供給烘焙系統(tǒng)媳板,從而間接計算光照桑腮。目前沒有定義該Pass,Unity默認(rèn)表面為白色蛉幸。
- 添加Meta Pass破讨,Pass的Light Mode設(shè)置為Meta,關(guān)閉剔除功能奕纫。
- 像采樣光照圖一樣提陶,使用光照貼圖的UV坐標(biāo)。不同的是匹层,我們將UV賦值給對象空間頂點位置的XY分量隙笆,Z分量限制到[0,FLT_MIN]區(qū)間,F(xiàn)LT_MIN是最小正浮點數(shù)。然后使用TransformWorldToHClip方法將頂點轉(zhuǎn)換到裁剪空間中撑柔。(就是需要這么做)
- Meta Pass通過定義一個bool4類型的標(biāo)記向量unity_MetaFragmentControl進(jìn)行通信煤率,如果標(biāo)記了x分量,則需要漫反射率乏冀。這已經(jīng)足夠給反射光照著色蝶糯,但是Unity的Meta Pass還通過加上粗糙度乘一半的鏡面反射來提升效果。這是因為考慮到高鏡面但是粗糙的材質(zhì)也可以傳遞一些間接光照辆沦。最后通過PositivePow擴大反射光照昼捍,將其限制到unity_MaxOutputValue.
- 在著色函數(shù)中計算全局光照的漫反射,應(yīng)用到表面漫反射項上肢扯,得到間接光照的漫反射妒茬。
- 自發(fā)光表面
自發(fā)光表面:有些表面可以發(fā)出光,盡管場景中沒有任何照明蔚晨,因為它不是真正的光源乍钻,所以不能影響其他表面,但是可以參與烘焙光照貼圖的計算中铭腕,從而照明周圍靜態(tài)物體银择。
- 給shader增加自發(fā)光紋理和自發(fā)光顏色兩個屬性。
- 烘焙自發(fā)光累舷。在Meta Pass中判斷浩考,如果unity_MetaFragmentControl標(biāo)記了y分量,則返回自發(fā)光顏色被盈。但是現(xiàn)在自發(fā)光物體還不能參與烘焙光照的計算中析孽,也不能照亮其他物體,自發(fā)光是通過單獨的Pass烘焙的只怎,需要對每個材質(zhì)進(jìn)行烘焙自發(fā)光的配置才行袜瞬。
陰影蒙版
- 烘焙陰影
使用光照貼圖的好處是可以不限于陰影的最大距離,烘焙的陰影在最大距離之外也不會被剔除身堡。通常情況下邓尤,我們可以設(shè)置在陰影最大距離之外使用實時陰影,超過范圍則使用烘焙陰影盾沫。
ShadowMask照明模式:陰影蒙版是一種紋理裁赠,它和與之搭配使用的光照貼圖紋理使用相同的UV采樣坐標(biāo)和紋理分辨率殿漠。ShadowMask的每一個紋素存儲著它對應(yīng)場景位置點上面最多4個光源(因為目前的GPU架構(gòu)赴精,一個紋素最多只支持RGBA 4個顏色通道)在該位置的遮擋信息,即記錄這一點有多少光源能照到绞幌。
將LightMode改為ShadowMask照明模式蕾哟,此模式下Unity會先計算從靜止的物體投射到其他靜止物體上的陰影。多出來的混合模式光源會轉(zhuǎn)用烘焙式光照計算,具體哪個光源由烘焙光源計算由引擎決定谭确,每個光照探針最多存儲4個光源的遮擋信息帘营,如果4個以上的光源發(fā)出的光線相交,則其余混合模式光源則會自動改為使用烘焙模式逐哈,并且這些光源信息是提前計算好的芬迄。因為混合模式光源的陰影蒙版在運行時有所保留,所以運動的游戲?qū)ο笏渡涞年幱翱梢耘c預(yù)先計算并存儲在陰影蒙版中的陰影正確合成昂秃,不會導(dǎo)致重復(fù)投影問題禀梳。
在ShadowMask照明模式里,間接照明效果和陰影衰減都儲存在光照貼圖中肠骆,陰影被儲存在額外的ShadowMask中算途,當(dāng)只有主光源時,所有被照亮的物體都會作為紅色出現(xiàn)在ShadowMask中蚀腿,紅色是因為陰影信息存儲在紋理的Red通道中嘴瓤,貼圖可以存儲四個光照的陰影,它有四個通道莉钙。
在ShadowMask模式下廓脆,靜止的物體向靜止的物體投射陰影是不受shadow distance限制的,只有運動的物體向靜止的物體投影才受限制磁玉,且要在最大陰影距離內(nèi)才生效狞贱,此部分陰影通過陰影貼圖實現(xiàn)。同時運動的游戲?qū)ο笠部梢詮撵o止的游戲?qū)ο筇幗邮荜幱巴渡涫裾牵@部分陰影是通過光照探針實現(xiàn)的瞎嬉。一般的ShadowMask紋理比實時陰影的陰影貼圖紋理分辨率低。unity會自動對靜態(tài)和動態(tài)游戲?qū)ο螽a(chǎn)生的重疊陰影進(jìn)行組合厚柳,因為控制靜態(tài)物體的光照與陰影蒙版和控制動態(tài)物體的光照和陰影貼圖將會被編碼為遮蔽信息氧枣。
- LightMode中設(shè)置Shadow Mask,在Project Settings->Quality->Shadows中Shadowmask Mode設(shè)置為Distance Shadowmask模型别垮。
- 使用陰影蒙版便监。設(shè)置shader中,Shadow Mask相關(guān)關(guān)鍵字碳想,使渲染管線知道Shadow Mask的存在烧董。
- 定義是否使用Shadow Mask的標(biāo)記,如果對于場景中的光線胧奔,符合Shadow Mask條件逊移,則打開Shadow Mask標(biāo)記并且將關(guān)鍵字設(shè)置為true。
- 讓著色器知道是否使用shadowmask龙填,如果使用則得到烘焙的陰影數(shù)據(jù)胳泉。shadow中需要ShadowMask數(shù)據(jù)拐叉,ShadowMask也是ShadowData的數(shù)據(jù)之一。同時扇商,GI中也需要知道ShadowMask數(shù)據(jù)凤瘦。
- GI中,如果開啟了Distance Shadowmask模式案铺,則采樣shadowmask蔬芥。
- 在GetLighting中,獲取GI中的ShadowMask控汉。
7.drawingsettings中發(fā)送逐對象shadowmask數(shù)據(jù)
- 遮擋探針
遮擋探針:陰影蒙版已經(jīng)被正確的應(yīng)用到了靜態(tài)的光照貼圖對象坝茎,但是動態(tài)對象還沒有陰影蒙版數(shù)據(jù),因為它們使用光照探針而不是光照貼圖暇番。unity將陰影蒙版數(shù)據(jù)烘焙到光照探針中嗤放,稱之為遮擋探針”诔辏可以通過向緩沖區(qū)中添加unity_ProbesOcclusion向量訪問次酌。
- UnityPerdraw緩沖區(qū)中定義數(shù)據(jù)
- 沒有光照貼圖的情況下,返回探針數(shù)據(jù)
- drawingsettings中發(fā)送探針數(shù)據(jù)舆乔。對于探針來說岳服,shadow mask沒有使用的通道被設(shè)置為白色,所以動態(tài)物體處于完全照明時為白色希俩,處在完全陰影中為青色吊宋。
- 此時還無法使用UnityInstancing,只有在定義SHADOWS_SHADOWMASK時颜武,遮擋數(shù)據(jù)才能自動獲得實例璃搜,所以定義SHADOWS_SHADOWMASK。
- LPPV也可以和shadowmask配合使用鳞上,drawingsettings加入LPPV與遮擋探針的配合这吻。在shadowmask采樣時需要判斷LPPV是否開啟,開啟則采樣LPPV遮擋
- 混合陰影
混合陰影:有了烘焙的陰影和實時陰影篙议,接下來將烘焙陰影與實時陰影混合唾糯。超過陰影最大距離使用烘焙陰影,在陰影距離內(nèi)使用實時陰影鬼贱。
- GetDirectionalShadowAttenuation方法獲得陰影衰減移怯,可以獲得烘焙與實時陰影,因此將烘焙陰影與實時陰影的代碼分開这难。
- 分別獲取烘焙陰影衰減和實時陰影衰減舟误。
- 判斷如果定義了shadowmask的distance屬性,則用烘焙陰影衰減替代實時陰影衰減雁佳。
- 陰影過渡:根據(jù)深度來從實時陰影過渡到烘焙陰影脐帝。且根據(jù)全局陰影強度在二者之間插值。使用單獨對cullingResults.GetShadowCasterBounds(index, out Bounds b)的判斷糖权,確定光源是否使用陰影遮罩堵腹,即使沒有陰影投射(也就是說超過了最大陰影距離),也要返回光源陰影強度的負(fù)值星澳,這樣可以在shader中通過是否為負(fù)來判斷是否超過最大陰影距離疚顷,從而在超過最大陰影距離的位置,使用烘焙陰影禁偎。
- 支持shadowmask模式腿堤。其工作原理與Distance shadowmask相同,但distance shadowmask在不超過陰影距離內(nèi)如暖,使用陰影貼圖的實時陰影笆檀,在超過陰影距離外,使用烘焙陰影(靜態(tài)的用mask盒至,動態(tài)的用探針)酗洒。shadow mask模式在所有的靜態(tài)物體只使用烘焙陰影,動態(tài)對象只使用陰影貼圖枷遂。
- 多光源烘焙陰影:shadowmask紋理共有4個通道樱衷,最多支持4個mixed光源,烘焙時酒唉,最重要的方向光的陰影信息存儲在R通道中矩桂,第二個光源陰影信息保存在G通道,目前的著色器只使用了R通道的陰影信息痪伦,需要將光源通道索引發(fā)送到GPU來調(diào)整侄榴,但我們不能以來燈光的順序,因為燈光隨時可變