又踩了一坑,好在谷歌到了之前的一個人遇到相同的問題,順利解決。
先說說問題背景,我目前的畢設是體數據渲染,實現的辦法是raycast。最基本的一點就是在fragment program里,獲取raycast的方向。
問題出現在加入陰影之后。在這之前,獲取方向是沒問題的。
在透視相機中,可以簡單地用float3 viewDir = normalize(worldPos - _WorldSpaceCameraPos);
來獲取方向。
在正交相機中,我們需要換個方法,來獲取相機的朝向(對正交相機來說,朝向就是raycast的方向)。Unity提供了一個參數來表示當前相機的類型。
因此我們可以用這樣的式子替換,來適應兩種相機
float3 viewDir =
unity_OrthoParams.w * -UNITY_MATRIX_V[2].xyz //這個-UNITY_MATRIX_V[2].xyz可以用來表示相機的朝向,即C#代碼里的transform.forward
+
(1- unity_OrthoParams.w) * (worldPos - _WorldSpaceCameraPos);
這個時候我用常規的辦法加入了陰影(引入ShadowCaster的pass)。並且用同樣的辦法計算深度然后輸出。然后……就出問題了,主要表現是在Shadow Map里渲染錯位。
一番排查之后發現了問題。目前我只考慮單個Directional Light,下面也都按照Directional Light來說。按照Shadow Map的流程,我們會需要Directional Light為相機渲染一次Shadow Map。此時該Light相當於一個正交相機。盡管之前我們對兩種相機都做了適應,但是……在渲染ShadowMap的時候,這個unity_OrthoParams,是不管用的,因此viewDir的計算是按照透視相機來的,於是就錯位了。
所以我們得自己判斷正交還是透視了。好在有大神給出了解答:使用UNITY_MATRIX_P[3][3]
進行判斷。為0表示透視,1表示正交。具體原理我也不清楚,不過測試之后是完全可行的。
修改后的viewDir如下:
float3 viewDir = normalize(
UNITY_MATRIX_P[3][3] * -UNITY_MATRIX_V[2].xyz
+
(1 - UNITY_MATRIX_P[3][3]) * (worldPos - _WorldSpaceCameraPos));
另外推薦一個系列教程:http://catlikecoding.com/unity/tutorials/ 其中包括了shader編程,由淺入深地把unity5的standard surface shader復現了一遍,包括編輯器界面。同時包括了各種技術的原理、實現。看一遍絕對是受益頗多。除開shader的教程,其他的幾個教程也是非常有意思。總之強烈推薦。