Steam VR - The Lab Renderer學習筆記
前言
Unity Vision VR/AR Summit來到中國了(http://www.bagevent.com/event/197605?bag_track=http://www.bagevent.com/event/197605 ),最近也關注了一下Unity的VR開發。
大概是6月份看到新聞:Steam釋出了The Lab所使用的渲染器的所有原始碼。我一直挺好奇的,對於Unity3D這樣不開源的引擎,如果搞一個渲染器呢?今天有時間讀一下程式碼,一探究竟。
相關連結:
- 官方帖子:http://steamcommunity.com/games/250820/announcements/detail/604985915045842668
- GitHub下載:https://github.com/ValveSoftware/the_lab_renderer
- Unity Asset Store下載:https://www.assetstore.unity3d.com/en/#!/content/63141
特性&實現
從GitHub下載了The Lab Renderer之後,粗略的瀏覽一遍,內容不多,主要是幾個元件的C#程式碼和一些Shader。接下來就看看它的主要特性是怎麼實現的。
Single-Pass Forward Rendering
The Lab Renderer使用Forward Rendering的原因主要是為了MSAA(MultiSampling Anti-Aliasing)和效率。然而Unity預設的Forward Rendering使用了Multi-Pass來渲染所有燈光(每個物體的每個動態燈要多一個Pass來渲染它的光照),The Lab Renderer提供了一個單Pass渲染多個燈光的解決方案。
為了實現Single-Pass Forward Rendering,首先要在Player Settings做一些設定,如上圖所示。所謂的“Single-Pass”主要靠Shader來實現了。大體思路就是在“vr_lightng.cginc”這個shader檔案中定義了一系列燈光引數的陣列:
#define MAX_LIGHTS 18
...
float4 g_vLightColor[ MAX_LIGHTS ];
float4 g_vLightPosition_flInvRadius[ MAX_LIGHTS ];
float4 g_vLightDirection[ MAX_LIGHTS ];
然後使用一個for迴圈,一次性計算所有燈光的光照:
LightingTerms_t ComputeLighting( float3 vPositionWs ...)
{
[ loop ] for ( int i = 0; i < g_nNumLights; i++ )
{}
}
接下來就是在C#層來處理燈光資訊了。
- 首先,需要為每個Unity中的燈光物件新增“ValveRealtimeLight.cs ”指令碼,class ValveRealtimeLight管理一個靜態變數“List< ValveRealtimeLight > s_allLights”用來簿記所有的燈光資料。
然後,需要在Main Camera物件上新增“ValveCamera.cs”指令碼。在class ValveCamera.UpdateLightConstants()成員函式中,會計算所有的燈光相關的引數,並設定到Shader的常量中。
以上就是The Lab Renderer的Single-Pass Forward Rendering這個特性的實現思路。
Shadows
The Lab Renderer還接管了陰影的渲染。需要在Unity的Quality->Shadows settings 中選擇“Disable Shadows”來關閉Unity預設的陰影。
如上圖所示,The Lab Renderer使用Shadow Mapping的演算法來生成實時陰影。這個演算法粗略的過程是這樣的:
- 從燈光的角度渲染一個深度緩衝。這個深度緩衝的幾何意義,可以粗略的理解為每個畫素點到燈光最近的距離;這個深度緩衝又被成為Shadow Buffer或者Shadow Map。
- 在渲染Back Buffer的時候,對於每個需要著色的點,將其“投影”(Projection)到上述的Shadow Map空間,然後進行比較,來判斷這個點是不是離燈光最近—-也就是有沒有被其他物體遮擋,即在陰影之中。
- 生成Shadow Buffer的渲染,對於Spot Light非常直觀啦;對於方向光,The Lab採用了近似的方法:將方向光替換成一個“非常遠”的點光源;對於點光源,The Lab使用6個假的Spot Light來替代。0_0|||
上述演算法的流程控制,在ValveCamera.cs指令碼中實現。首先它需要一個從燈光角度渲染的Camera、一個RenderTexture用做Shadow Map,還需要一個Shader來進行Shadow Map渲染(Resources/vr_cast_shadows.shader)。
[ExecuteInEditMode]
[RequireComponent( typeof( Camera ) )]
public class ValveCamera : MonoBehaviour
{
...
[NonSerialized] private Camera m_shadowCamera = null;
[NonSerialized] public RenderTexture m_shadowDepthTexture = null;
[NonSerialized] public Shader m_shaderCastShadows = null;
...
}
在ValueCamera.OnPreCull()指令碼回撥函式中會呼叫ValueCamera.ValveShadowBufferRender()來渲染Shadow Buffer。如上圖的Shadow所示,The Lab把所有燈光渲染到了一個整體的Shadow Buffer之中,把每個燈光Shadow Buffer對應的區域,儲存到Shader引數“g_vShadowMinMaxUv”之中。這樣在前面講的Single-Pass Forward Rendering過程中,就可以在一個Pass實現所有燈光的光影計算了。
至於vr_cast_shadows.shader的內容,就很簡單了,它核心就是一個Vertex Shader,用來計算Projection之後的Position座標就好,UV啊什麼之類的都可以省略掉了。
在燈光渲染的Shader中(vr_lighting.cginc)通過ComputeShadow_PCF_3x3_Gaussian()函式來計算陰影。所謂的PCF就是Percentage Closer Filter,為的是產生陰影的邊緣柔滑效果。在這個函式中,它才算有了高斯過濾來對目標點周圍的3x3範圍進行計算。
Adaptive Quality
對於VR來說,幀速率是非常重要的,所以Valve的大牛就新增了這個特性:動態調節渲染質量,達到穩定的搞效率,這是他在GDC 2016上的一個演講:https://www.youtube.com/watch?v=eIlb688pUu4
這部分主要涉及到何時去調節質量,調節哪些地方(哪些是不能隨便調的),具體的邏輯都在ValveCamera.UpdateAdaptiveQuality()這個函式裡了。
相關文章
- Isaac Lab 學習筆記:概述筆記
- numpy的學習筆記\pandas學習筆記筆記
- 學習筆記筆記
- ucore作業系統學習筆記(二) ucore lab2實體記憶體管理分析作業系統筆記記憶體
- 【學習筆記】數學筆記
- 《JAVA學習指南》學習筆記Java筆記
- 機器學習學習筆記機器學習筆記
- 學習筆記-粉筆980筆記
- 學習筆記(3.29)筆記
- 學習筆記(4.1)筆記
- 學習筆記(3.25)筆記
- 學習筆記(3.26)筆記
- JavaWeb 學習筆記JavaWeb筆記
- golang 學習筆記Golang筆記
- Nginx 學習筆記Nginx筆記
- spring學習筆記Spring筆記
- gPRC學習筆記筆記
- GDB學習筆記筆記
- 學習筆記(4.2)筆記
- 學習筆記(4.3)筆記
- 學習筆記(4.4)筆記
- Servlet學習筆記Servlet筆記
- 學習筆記(3.27)筆記
- jest 學習筆記筆記
- NodeJS學習筆記NodeJS筆記
- WebSocket 學習筆記Web筆記
- mount 學習筆記筆記
- mapGetters學習筆記筆記
- jQuery學習筆記jQuery筆記
- 學習筆記:DDPG筆記
- flex學習筆記Flex筆記
- react 學習筆記React筆記
- Promise學習筆記Promise筆記
- vim學習筆記筆記
- Ansible 學習筆記筆記
- Taro 學習筆記筆記
- MongoDB學習筆記MongoDB筆記
- hbase學習筆記筆記
- git學習筆記Git筆記