打開鏡像世界
我們在Unity裏打開鏡像的世界呢,原理很簡單,我們再加一個Mirror Camera就可以了,其位置是我們當前Camera相對於鏡面的一個鏡像的映射(Transform)。
- 鏡像矩陣
這個概念比較複雜,記公式就可以了。
有了公式,代碼就好寫了
public static void CalculateReflectionMatrix(ref Matrix4x4 reflectionMat, Vector4 plane)
{
reflectionMat.m00 = (1F - 2F * plane[0] * plane[0]);
reflectionMat.m01 = (-2F * plane[0] * plane[1]);
reflectionMat.m02 = (-2F * plane[0] * plane[2]);
reflectionMat.m03 = (-2F * plane[0] * plane[3]);
reflectionMat.m10 = (-2F * plane[1] * plane[0]);
reflectionMat.m11 = (1F - 2F * plane[1] * plane[1]);
reflectionMat.m12 = (-2F * plane[1] * plane[2]);
reflectionMat.m13 = (-2F * plane[1] * plane[3]);
reflectionMat.m20 = (-2F * plane[2] * plane[0]);
reflectionMat.m21 = (-2F * plane[2] * plane[1]);
reflectionMat.m22 = (1F - 2F * plane[2] * plane[2]);
reflectionMat.m23 = (-2F * plane[2] * plane[3]);
reflectionMat.m30 = 0F;
reflectionMat.m31 = 0F;
reflectionMat.m32 = 0F;
reflectionMat.m33 = 1F;
}
-
然後是把MirrorCamera放到鏡像的點,同時修改以下屬性,設置其變換矩陣:
reflectionCamera.worldToCameraMatrix = cam.worldToCameraMatrix * reflection;
-
剪切矩陣我們要把MirrorCamera與鏡面間的內容剪切掉。
Unity的Camera直接有方法,但這裏用的還是公式,如下鏈接說的很詳細,不再多說了。
Unity的Camera直接有方法,但這裏用的還是公式,如下鏈接說的很詳細,不再多說了。https://www.jianshu.com/p/15dea7f9e50f
代碼如下:
public static void CalculateObliqueMatrix(ref Matrix4x4 projection, Vector4 clipPlane)
{
Vector4 q = projection.inverse * new Vector4(sgn(clipPlane.x), sgn(clipPlane.y), 1.0f, 1.0f);
Vector4 c = clipPlane * (2.0F / (Vector4.Dot(clipPlane, q)));
projection[2] = c.x - projection[3];
projection[6] = c.y - projection[7];
projection[10] = c.z - projection[11];
projection[14] = c.w - projection[15];
}
Mirror Shader
獲取MirrorCamera的RenderTarget,即MirrorCamera的截圖效果。
inline float4 ComputeNonStereoScreenPos1(float4 pos)
{
float4 o = pos * 0.5f;
o.xy = float2(o.x, o.y * _ProjectionParams.x) + o.w;
o.zw = pos.zw;
return o;
}
以上Unity Shader 庫中定義的ComputeScreenPos的內部實現。
之後使用tex2Dproj方法進行紋理採樣。
fixed4 refl = tex2Dproj(_ReflectionTex, UNITY_PROJ_COORD(i.refl));
其中uniform float4 _ProjectionParams;
投影參數各值分別爲如下含義:
- x: = 1,如果投影翻轉則x = -1
- y:是camera近裁剪平面
- z:是camera遠裁剪平面
- w:是1/遠裁剪平面
inline float4 ComputeScreenPos1(float4 pos, float d)
{
float4 o = pos *0.5f;
o.xy = float2(o.x / d, o.y / d * _ProjectionParams.x) + 0.5f;
//o.xy = float2(o.x / o.w, o.y / o.w * 1) + 0.5f;
o.zw = pos.zw;
return o;
}
使用如下進行紋理採樣:
fixed4 refl = tex2D(_ReflectionTex, i.refl.xy);
效果如下:
可以看到採樣的有抖動的問題。
打開Wireframe
如下圖:我們就可以看到其中的規律了。
如果使用tex2D函數進行採樣,如果紋理貼圖與面有拉抻的情況。那樣就會出現不規則、不確定的貼圖採樣扭曲的現象,這就是爲什麼使用 tex2Dproj
的原因了。
最後看一些UNITY_PROJ_COORD
的定義,大多數平臺上其值是與輸入值一樣的。
#if defined(SHADER_API_PSP2)
#define UNITY_BUGGY_TEX2DPROJ4
#define UNITY_PROJ_COORD(a) (a).xyw
#else
#define UNITY_PROJ_COORD(a) a
#endif
後續處理
uv擾動參數和噪聲貼圖
這裏還要說明一種深度模糊的效果
附錄:
uniform float4 _ScreenParams
屏幕參數:- x = 屏幕寬度
- y = 屏幕高度
- z = 1 + 1.0/屏幕寬度
- w = 1 + 1.0/height屏幕高度(指像素數)
- Unity內置變換矩陣
- Githup 源碼位置
https://github.com/bennychao/UnityShaders