Unity Shader 案例之 鏡面材質製作

打開鏡像世界

我們在Unity裏打開鏡像的世界呢,原理很簡單,我們再加一個Mirror Camera就可以了,其位置是我們當前Camera相對於鏡面的一個鏡像的映射(Transform)。

  1. 鏡像矩陣
    公式

這個概念比較複雜,記公式就可以了。

有了公式,代碼就好寫了

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;   
}
  1. 然後是把MirrorCamera放到鏡像的點,同時修改以下屬性,設置其變換矩陣:reflectionCamera.worldToCameraMatrix = cam.worldToCameraMatrix * reflection;

  2. 剪切矩陣我們要把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擾動參數和噪聲貼圖
在這裏插入圖片描述
這裏還要說明一種深度模糊的效果

附錄:

  1. uniform float4 _ScreenParams 屏幕參數:
    • x = 屏幕寬度
    • y = 屏幕高度
    • z = 1 + 1.0/屏幕寬度
    • w = 1 + 1.0/height屏幕高度(指像素數)
  2. Unity內置變換矩陣
    在這裏插入圖片描述
  3. Githup 源碼位置
    https://github.com/bennychao/UnityShaders
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章