本篇文章我会介绍一下我本身在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就是这么作的,能够参考一下。学习