日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

Unity SRP自定义渲染管线 -- 3.Lights

發(fā)布時間:2023/12/13 编程问答 52 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Unity SRP自定义渲染管线 -- 3.Lights 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

Lights

Single-Pass Forward Rendering

  • 實現(xiàn) diffuse shading.
  • 支持 directional(方向光), point(點光源), and spotlights(聚光燈).
  • 每幀可允許最多16個可見光參與渲染
  • 每個物體可以最多由4個像素光和4個頂點光參與計算光照。

這是本系列教程的第三篇,在這一篇中,我們將實現(xiàn)每個物體由8個光源進行shading且僅消耗一個draw call。

1. Shading With a Light

為了渲染光照,我們得為我們的渲染管線加入一個最基礎的lit shader。光照渲染可以非常簡單,比如只包括光的漫反射,也可以非常非常復雜,比如基于物理的渲染(PBS)。我們現(xiàn)在先從最基礎的開始,只計算方向光的漫反射,不考慮陰影。

1.1 Lit Shader

復制Unlit.hlsl并重命名為Lit.hlsl. 在文件中,用lit代替unlit,尤其是定義vertex和fragment 函數(shù)的名字。

?同樣復制Unlit.shader并重命名為Lit.shader. 在文件中,用lit代替unlit。

現(xiàn)在我們可以通過新建的lit shader創(chuàng)建material了,雖然目前渲染的效果和unlit一樣(沒寫呢還當然一樣)

1.2 Normal Vectors

為了計算方向光,我們需要知道表面的法線。我們?yōu)関ertext函數(shù)的輸入和輸出結(jié)構體添加法線信息。

我們假設物體使用統(tǒng)一的scale,因此用3X3 模型矩陣簡化法線的坐標變換,如果不是統(tǒng)一的scale,我們需要用world to object的轉(zhuǎn)置矩陣進行計算(具體原理可以搜索其他資料,法線的坐標變換)。坐標變換后在fragment函數(shù)中進行歸一化。

為了證明我們獲得了正確的法線信息,我們在fragment函數(shù)中輸出法線看看效果

1.3 Diffuse Light

漫反射由光照與表面法線的角度夾角決定,目前我們先硬編碼,將光照的方向設置為 (0 ,1,0)。

2. Visible Lights

為了使用場景中的光源,我們的渲染管線需要將光源數(shù)據(jù)傳輸?shù)紾PU中,場景中可能存在多光源,所以我們也需要支持多光源的渲染。Unity中默認的渲染管線會為每個物體每個光源分配一個pass進行渲染(M?X N 即M個物體N個光源需要M X N個Pass)。LWRP渲染管線則對每個物體只使用一個Pass渲染所有光源。HDRP則使用Deferred rendering,先渲染所有物體的表面信息,再對每個光源使用一個pass進行渲染。

在本文里,我們使用和LWRP相同的策略,對每個物體用一個pass渲染所有光源,所以要求我們將當前可見的所有光源信息傳輸?shù)紾PU,那些雖然在場景中,但是對物體沒有產(chǎn)生任何影響的光源將被忽略不參與計算。

2.1 Light Buffer

在一個Pass中渲染所有光源意味著所有光源的信息必須在同時都準備好,我們目前暫且將所有光源類型限制為方向光,這意味著我們需要知道每個光源的顏色和方向信息。為了支持多光源,我們采用數(shù)組來存儲。我們用一個單獨的buffer存儲光源的信息,給這個buffer命名為_LightBuffer.

然而我們并不能夠在定義數(shù)組時不指定數(shù)組大小,我們聲明一個宏來定義最大可見光源數(shù)量,用它來指定數(shù)組大小

加入一個DIffuseLight函數(shù),它用傳入進來的光源信息計算Diffuse光照

在LitPassFragment函數(shù)中加入for循環(huán)來支持多光源的渲染?

2.2 Filling the Buffer

現(xiàn)在我們渲染出來的東西還是一片漆黑,這是因為我們還沒把光源數(shù)據(jù)傳進GPU來,我們需要在我們的渲染管線MyPipeline中聲明同樣大小的數(shù)組,再使用Shader.PropertyToID方法獲取shader中相關屬性的引用,

?通過函數(shù)SetGlobalVectorArray操作command buffer,可以將數(shù)組數(shù)據(jù)傳入到GPU中。

?2.3?Configuring the Lights

我們現(xiàn)在是可以將光源數(shù)據(jù)每幀傳輸?shù)紾PU中了,但是現(xiàn)在確依然顯示漆黑,這是因為我們還得先設置數(shù)據(jù),我們聲明個ConfigureLights函數(shù)來完成這項工作。

在culling剪裁中,Unity同時指出了哪些光源是可見的。這一信息可以從cull結(jié)果中獲得,這一信息以visibleLights名字的list變量存儲在cull結(jié)果中。

finalColor字段存儲了光源的顏色,該顏色數(shù)據(jù)是由光源的color屬性和intensity屬性相乘后的結(jié)果,并經(jīng)過了顏色空間的校正,所以我們可以直接使用該信息將其賦值給visibleLightColors數(shù)組。

然后,unity默認的渲染管線中,intensity定義在gamma空間,我們工作中線性空間,所以通過GraphicsSettings.lightsUseLinearIntensity 屬性我們將其設置為線性空間。

?方向光的光源方向信息可以通過光源的旋轉(zhuǎn)信息獲得,光源的方向是它的z軸方向。我們可以通過VisibleLight.localtoWorld矩陣獲取在世界坐標系中的該信息。這個矩陣的第三列定義了光源的本地Z軸方向。

在shader中我們使用從物體朝向光源的向量方向進行計算,所以將獲得的光源方向進行取反操作。

我們的shader目前將會計算四個光源,即使場景中沒有四個光源,也將會計算四次。在場景中加入四個光源后,渲染的效果如下。

在frame debugger中可以查看到傳入GPU的light data。

2.4?Varying the Number of Lights?

當可見光的數(shù)量大于我們設定的maxVisibleLights時,會產(chǎn)生越界的錯誤,所以我們要對邊界條件進行處理,當可見光數(shù)量大于maxVisibleLights時,忽略掉多出的那些光源(Unity光源排序的規(guī)則可以參考其他資料,簡單來講是通過光源的重要程度排序)

我們還要處理的一種情形是當光源數(shù)目由多變少,這時候需要清理重置光源的信息,確保下一幀的正確渲染。

3.?Point Lights

這一節(jié)我們將實現(xiàn)渲染管線中的點光源。

3.1?Light Position

和方向光不同,點光源不關心光的方向而關心光源的位置。我們不另外開辟新數(shù)組存儲位置信息,而是使用之前聲明用于存儲方向光方向信息的數(shù)組來存儲點光源的位置數(shù)據(jù)。在Mypipeline中重新命名該數(shù)組

使用VisibleLight.lightType來判斷當前光源的類型,當是方向光時存入方向信息,當是點光源時存入位置信息。

在shader函數(shù)中,使用該數(shù)據(jù)信息獲取光源位置信息,并傳入worldPos,兩者相減即可獲得光線的方向。

當是方向光時,w是0,當是點光源時w是1,我們利用該性質(zhì)將worldPos 與 w分量相乘,這樣就可以用同一個公式計算點光源和方向光的信息。

為了獲取片段的位置信息,我們需要在shader中進行處理,由vertex函數(shù)輸出到fragement函數(shù)。

至此,我們就可以看到點光源的效果了。

3.2?Distance Attenuation

和方向光不同,點光源要考慮光源強度隨著距離而衰減。這里的衰減關系是距離平方的倒數(shù)。為了避免除數(shù)是0出現(xiàn)錯誤,因此加入一個極小的值0.00001

3.3 Light Range

?點光源還有個屬性是光照范圍。 在范圍外的物體將不會受該光源的影響,雖然在事實上它們可能會被物體照亮,但是用范圍這個屬性,我們可以更好的規(guī)定哪些物體受到該光源到影響,沒有這個范圍屬性限制,所有的光源都會被認為是可見的。

范圍屬性不是突變的而是平滑漸變的,其公式為:

范圍屬性是場景中的數(shù)據(jù),所以我們也需要將其傳入GPU,這回我們將使用一個新的數(shù)組來存儲它。

像之前做的一樣,把數(shù)據(jù)用command buffer輸入到GPU中

填充數(shù)據(jù)時,我們計算好,將結(jié)果存入數(shù)組后傳入GPU,這樣可以減少GPU的工作。

在shader中計算范圍的影響,進行著色

?

Light fades out based on range

?

4.?Spotlights

接下來我們添加聚光燈光源.聚光燈和點光源很像,但是有方向的限制

4.1?pot Direction

像方向光源,聚光燈也是沿著它的z方向發(fā)射光,但是是一個圓錐形范圍,它也有個位置屬性,所以我們得新添加個數(shù)組來支持聚光燈。

判斷光源類型,如果是聚光燈,將方向信息填入新的數(shù)組中。

?在shader中添加方向數(shù)據(jù)。

4.2?Angle Falloff

聚光燈類型光源也是漸變的衰減,這個范圍可以被定義為一個內(nèi)層的角度和一個外層的角度,從內(nèi)層的角度開始衰減,直到外層衰減到0.

Unity LWRP中,spot light類型光源只允許我們控制其外層角度,其衰減的方法被假定為與外層的角度有一個固定算法。

為了得到fallof,先把spot 光源的角度的一半由角度轉(zhuǎn)換成弧度,并計算其cosine值。

根據(jù)外層的角度計算內(nèi)層的角度的公式以及衰減函數(shù)的公式和計算如下所示:?

?其中衰減函數(shù)可以進行簡化:

?最后在shader中用計算出來的光照進行著色

?為了保證不同類型的光照計算的一致性(用同樣的shader代碼),將w分量設置為1

5.??Lights Per Object

目前我們支持了對一個物體用四個光源進行光照,實際上,無論有幾個光源,目前每個物體都將計算4次,但其實很多時候是不必要的。不如如下的例子。9乘9的方格,共有81個球體,場景中有4個光源在四個角,當光源的范圍并不是很大時,大多數(shù)球體只受到一個光源的影響,甚至有的球體不受到任何影響。

目前81個球體在開啟GPU Instaing的時候?qū)⒅粫囊粋€draw call,但是球體的每個fragment將在fragment shader中計算4次光照,我們應該改進成只計算影響該fragment的光源。

5.1 Light Indices

在Culling期間,Unity也會計算出哪些光是可見的,每個物體受哪些光源的影響的信息可以以光照索引list的形式傳輸?shù)紾PU

Unity目前支持兩種形式的光源索引,第一種是對每個物體,將其受影響的光源存入兩個float4類型變量中。第二種是將所有物體受光源影響的信息以list形式一起存入單獨的buffer中。然而目前Unity 2018.3版本只支持第一種,因此我們采用第一種。

設置rendererConfiguration字段為RendererConfiguration.PerObjectLightIndices8來開啟光源的索引功能。

Unity現(xiàn)在需要為每個物體設置額外的數(shù)據(jù)以提供給GPU,這將會影響到GPU instancing。相較于根據(jù)受影響的光源分組,Unity更傾向于根據(jù)距離分組,另外光源的重要性也會影響到索引的排序,這些都會影響到合批。在我們的這個例子中,會由30個draw call,遠大于1,當然也遠小于81.

索引通過unity_4LightIndices0?and?unity_4LightIndices1引通變量可以獲得,它們應該存在UnityPerDraw Buffer中。另外

unity_LightIndicesOffsetAndCount變量中的Y分量存有當前物體受多少光源影響的數(shù)量。

現(xiàn)在我們可以限制調(diào)用DiffuseLight著色的次數(shù)為實際需要的了,但是我們還需要取出正確的索引來使用。我們目前限制燈光數(shù)量最多為4個,所以只需從unity_4LightIndices0變量中獲取。

限制GPU的開銷變小了,我們只需要計算真正影響到物體的光源,通過frame debugger我們可以查看傳入的光源的數(shù)量以及索引。

現(xiàn)在不在需要使用固定的數(shù)值來循環(huán)計算了,也不需要再去每次清除data。

5.2?More Visible Lights

現(xiàn)在可以支持更多可見的光源,讓我們把場景中最大的可見光源數(shù)量提升到16,但是大部分物體只會受少量光源的影響。修改變量值為16:

對于unity_4LightIndices0變量,最多只能存儲4個值,所以我們要注意不要越界:

但是我們可以不必限制單個物體最多受4個光源的影響,因為我們還可以用unity_4LightIndices1變量。但是我們不能超過8個,這已經(jīng)是對當個物體來講,目前能夠支持最多數(shù)量的光源了:

光源的索引是按照重要程度排序的,對于大多數(shù)物體,后四個光源的影響其實很小,關掉前四個光源的效果,可以查看后四個光源的效果:?

?

5.3?Vertex Lights

由于后四個光源其實并沒有那么重要,我們可以將其計算從fragment函數(shù)中移到vertex函數(shù)中,也就是從逐像素光照改為逐頂點光照,這樣雖然著色的精度會損失一些,但是可以減少GPU的消耗。現(xiàn)在,意味著我們支持4個逐像素光照,4個逐頂點光照,注逐頂點光照的結(jié)果要傳入到fragment函數(shù)中,作為初始值參與光照的計算,和逐像素光照相加后輸出:

5.4?Too Many Visible Lights

盡管目前我們已經(jīng)支持到場景中最多16個光源,但是依然無法避免有可能會存在更多光源的情況。當超出時,我們需要告訴Unity需要將一些光源舍棄以避免數(shù)組的越界。

我們可以通過GetLightIndexMap函數(shù)獲得光源索引的list,修改該list后再通過SetLightIndexMap函數(shù)存回去。Unity將對索引數(shù)組中為-1的值進行忽略,所以我們可以將超出的光源的索引改為-1:

進一步優(yōu)化,我們可以只需要當數(shù)量確實超出時進行該操作:?

?

5.5 Zero Visible Lights

另一個可能性是場景中沒有一個光源,這時為了避免錯誤崩潰,我們需要先判斷場景中的光源數(shù)量大于0再設置drawSettings.rendererConfiguration變量,同時只有在場景中光源數(shù)量大于0的情況下才設置光源數(shù)據(jù):

?不設置光源數(shù)據(jù)的一個副作用是這些數(shù)據(jù)將一直保持最后一個物體的數(shù)據(jù),為了避免這個問題,我們需要手動將unity_LightIndicesOffsetAndCount設置為0:

?

?

總結(jié)

以上是生活随笔為你收集整理的Unity SRP自定义渲染管线 -- 3.Lights的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。