The Lab Renderer學習筆記

The Lab Renderer

前言

Unity Vision VR/AR Summit來到中國了(http://www.bagevent.com/event/197605?bag_track=http://www.bagevent.com/event/197605   ),最近也關注了一下Unity的VR開發。
git

UnityVRSummit
大概是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/63141github

特性&實現

從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渲染多個燈光的解決方案。
數組

TheLab-PlayerSettings
爲了實現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循環,一次性計算全部燈光的光照:markdown

LightingTerms_t ComputeLighting( float3 vPositionWs ...)
{
    [ loop ] for ( int i = 0; i < g_nNumLights; i++ )
    {}
}

接下來就是在C#層來處理燈光信息了。app

  • 首先,須要爲每一個Unity中的燈光對象添加「ValveRealtimeLight.cs 」腳本,class ValveRealtimeLight管理一個靜態變量「List< ValveRealtimeLight > s_allLights」用來簿記全部的燈光數據。
  • 而後,須要在Main Camera對象上添加「ValveCamera.cs」腳本。在class ValveCamera.UpdateLightConstants()成員函數中,會計算全部的燈光相關的參數,並設置到Shader的常量中。函數

    以上就是The Lab Renderer的Single-Pass Forward Rendering這個特性的實現思路。oop

Shadows

The Lab Renderer還接管了陰影的渲染。須要在Unity的Quality->Shadows settings 中選擇「Disable Shadows」來關閉Unity默認的陰影。
ui

TheLab-ShadowMap
如上圖所示,The Lab Renderer使用Shadow Mapping的算法來生成實時陰影。這個算法粗略的過程是這樣的:

  1. 從燈光的角度渲染一個深度緩衝。這個深度緩衝的幾何意義,能夠粗略的理解爲每一個像素點到燈光最近的距離;這個深度緩衝又被成爲Shadow Buffer或者Shadow Map。
  2. 在渲染Back Buffer的時候,對於每一個須要着色的點,將其「投影」(Projection)到上述的Shadow Map空間,而後進行比較,來判斷這個點是否是離燈光最近—-也就是有沒有被其餘物體遮擋,即在陰影之中。
  3. 生成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)。spa

[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()這個函數裏了。

相關文章
相關標籤/搜索