本篇文章我會介紹一下我本身在Unity中實現的SSR效果
出發點是理解SSR效果的原理,所以最終效果不是很是完美的(代碼都是夠用就行),可是從學習的角度來講足以學習到SSR中的核心算法。
若是對核心算法沒有興趣,能夠直接使用Unity官方的PostProcessing庫,其中包含了一個SSR效果。(其實現來自於casual effects)html
參考資料:
https://github.com/Unity-Technologies/PostProcessing
http://www.kode80.com/blog/2015/03/11/screen-space-reflections-in-unity-5/
http://casual-effects.blogspot.com/2014/08/screen-space-ray-tracing.htmlgit
完成的工程:
https://github.com/yangrc1234/ScreenSpaceReflection
目前只在2017.一、DirectX下實現,沒有進行其餘測試。除非之後有需求,不然可能不會更新這個repo,畢竟官方已經有解決方案了,不必重複造輪子。這個repo用於學習目的就好了。
一些shader的宏、變量多是2017.1纔有的,若是老版本編譯不過歡迎提issue。
github
第一部分包含屏幕空間反射的定義、以及一個最初步的實現。算法
屏幕空間反射是一個後處理效果。經過對屏幕空間的畫面,按必定方式投射光線,採樣光線路徑上的像素,獲得一個點上的反射顏色。
好比說,對於一個像素A,咱們去計算它的反射。要計算反射,咱們必需要知道視線方向和該點的空間位置以及的法線方向,從而計算出光線的方向。
視線方向好說,空間位置,咱們能夠從深度貼圖中還原出來。法線方向意味着屏幕空間反射只能在Deferred Rendering下進行。在Deferred Rendering下咱們能夠輕鬆的從GBuffer中獲得一個點的法線方向。c#
獲取這些信息後,咱們就能夠開始投射光線了。每次光線步進,咱們都將當前位置的點再投影到屏幕空間上,去採樣屏幕上的像素。若是咱們計算獲得(如何計算等下再說)該像素是光線路徑上的一點,咱們就能夠將該點返回做爲結果了。app
如下是實際代碼:函數
[ImageEffectOpaque] private void OnRenderImage(RenderTexture source, RenderTexture destination) { mat.SetTexture("_BackfaceTex", GetBackfaceTexture()); mat.SetMatrix("_WorldToView", GetComponent<Camera>().worldToCameraMatrix); //emmmmm不知道爲何UNITY_MATRIX_V在這裏變成了一個單位矩陣。須要手動設置world to view的矩陣。 Graphics.Blit(source, destination, mat,0); }
v2f vert (appdata v) { v2f o; o.vertex = UnityObjectToClipPos(v.vertex); o.uv = v.uv; float4 cameraRay = float4(v.uv * 2.0 - 1.0, 1.0, 1.0); //做爲一個後期特效,咱們能夠經過uv座標,來得到相機光線方向。注意座標z爲1.0,這裏的cameraRay是從原點到far clip plane的光線 cameraRay = mul(unity_CameraInvProjection, cameraRay); //將相機光線從clip space轉移到view space o.csRay = cameraRay / cameraRay.w; return o; } fixed4 frag (v2f i) : SV_Target { float decodedDepth = Linear01Depth(tex2D(_CameraDepthTexture, i.uv).r); float3 csRayOrigin = decodedDepth * i.csRay; //由於i.csRay是指着far clip plane的光線,此時csRayOrigin是view space的光線起點 float3 wsNormal = tex2D(_CameraGBufferTexture2, i.uv).rgb * 2.0 - 1.0; //世界座標系下的法線 float3 csNormal = normalize(mul((float3x3)_WorldToView, wsNormal)); //將轉換到view space float2 hitPixel; float3 debugCol; if (traceRay( //檢測相交 csRayOrigin, normalize(reflect(csRayOrigin, csNormal)), hitPixel, //out debugCol)) //out { reflection = (1 - rayPercent) * tex2D(_MainTex, hitPixel); } //return float4(debugCol, 1); return tex2D(_MainTex, i.uv) + tex2D(_CameraGBufferTexture1,i.uv) * half4(reflection,1); }
在traceRay方法中,咱們進行實際的光線投射、相交檢測。
traceRay的簽名中我設置了一個debugCol的參數,當我須要debug這個函數時,我將須要debug的內容放到debugCol中,在main裏輸出debugCol的顏色。這只是我我的的習慣。
對於反射顏色的計算,我只是簡單的獲取了那個像素的顏色,而後加到輸出裏去而已。事實上,由於咱們是在Deferred rendering模式下,咱們能夠獲取到該點的全部的用於着色的信息,用這些信息咱們能夠進行一次完整的基於物理着色。在PostProcessing中的SSR就是這麼作的,能夠參考一下。學習