Unity無需Shader實現鏡子效果
Unity鏡子效果製作教程
本文提供全流程,中文翻譯。 |
Chinar —— 心分享、心創新! |
Chinar 教程效果:
全文高清圖片,點選即可放大觀看 (很多人竟然不知道)
1
Create Mirror —— 建立鏡子
本教程,無需自己找鏡子Shader,只需2個指令碼即可在Unity中建立一個簡單的模擬鏡面反射效果
1. 在場景中建立一個 Plane —— 用來作為鏡子
2. 同時建立一個材質球 /Material —— 給到 Plane 上
3. 修改新建立的 Material 的 Shader 為 Unlit/Texture
2
Create Camera —— 建立一個新相機
1. 新建一個 Render Texture(我改名為 Plane 便於區分和理解)
2. 右鍵 層次列表/Hierarchy —— 建立一個新的 Camera
3. 將新建的 Render Texture(Plane)給新建的 Camera 元件中的 Target Texture
4. 給新建的 Camera相機,新增指令碼 ChinarMirrorPlane
並將 Main Camera與 Plane 拖到 Inspector 面板中對應的屬性裡
5. 給新建的 Camera相機,新增指令碼 ChinarMirror ,並將 Plane 拖至 Inspector 面板中
注意: 一定要修改 Plane 材質的屬性為:
具體流程其實很簡單,如下
兩個指令碼,都需要掛載到 Camera:
<span style="color:#000000"><code><span style="color:#000088">using</span> UnityEngine;
<span style="color:#880000"><span style="color:#880000">///</span> <span style="color:#880000"><summary></span></span>
<span style="color:#880000"><span style="color:#880000">///</span> 鏡子管理指令碼 —— 掛在新建的Camera上</span>
<span style="color:#880000"><span style="color:#880000">///</span> <span style="color:#880000"></summary></span></span>
[ExecuteInEditMode]
<span style="color:#000088">public</span> <span style="color:#000088">class</span> ChinarMirror : MonoBehaviour
{
<span style="color:#000088">public</span> GameObject mirrorPlane; <span style="color:#880000">//鏡子</span>
<span style="color:#000088">public</span> Camera mainCamera; <span style="color:#880000">//主攝像機</span>
<span style="color:#000088">private</span> Camera mirrorCamera; <span style="color:#880000">//映象攝像機</span>
<span style="color:#000088">private</span> <span style="color:#000088">void</span> <span style="color:#009900">Start</span>()
{
mirrorCamera = GetComponent<Camera>();
}
<span style="color:#000088">private</span> <span style="color:#000088">void</span> <span style="color:#009900">Update</span>()
{
<span style="color:#000088">if</span> (<span style="color:#000088">null</span> == mirrorPlane || <span style="color:#000088">null</span> == mirrorCamera || <span style="color:#000088">null</span> == mainCamera) <span style="color:#000088">return</span>;
Vector3 postionInMirrorSpace = mirrorPlane.transform.InverseTransformPoint(mainCamera.transform.position); <span style="color:#880000">//將主攝像機的世界座標位置轉換為鏡子的區域性座標位置</span>
postionInMirrorSpace.y = -postionInMirrorSpace.y; <span style="color:#880000">//一般y為鏡面的法線方向</span>
mirrorCamera.transform.position = mirrorPlane.transform.TransformPoint(postionInMirrorSpace); <span style="color:#880000">//轉回到世界座標系的位置</span>
}
}</code></span>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
<span style="color:#000000"><code><span style="color:#000088">using</span> UnityEngine;
<span style="color:#880000">/// <summary></span>
<span style="color:#880000">/// Plane管理指令碼 —— 掛載新建的Camera上</span>
<span style="color:#880000">/// </summary></span>
[ExecuteInEditMode] <span style="color:#880000">//編輯模式中執行</span>
<span style="color:#000088">public</span> <span style="color:#000088">class</span> ChinarMirrorPlane : MonoBehaviour
{
<span style="color:#000088">public</span> GameObject mirrorPlane; <span style="color:#880000">//鏡子Plane</span>
<span style="color:#000088">public</span> <span style="color:#000088">bool</span> estimateViewFrustum = <span style="color:#000088">true</span>;
<span style="color:#000088">public</span> <span style="color:#000088">bool</span> setNearClipPlane = <span style="color:#000088">true</span>; <span style="color:#880000">//是否設定近剪切平面</span>
<span style="color:#000088">public</span> <span style="color:#000088">float</span> nearClipDistanceOffset = -<span style="color:#006666">0.01f</span>; <span style="color:#880000">//近剪切平面的距離</span>
<span style="color:#000088">private</span> Camera mirrorCamera; <span style="color:#880000">//映象攝像機</span>
<span style="color:#000088">private</span> Vector3 vn; <span style="color:#880000">//螢幕的法線</span>
<span style="color:#000088">private</span> <span style="color:#000088">float</span> l; <span style="color:#880000">//到螢幕左邊緣的距離</span>
<span style="color:#000088">private</span> <span style="color:#000088">float</span> r; <span style="color:#880000">//到螢幕右邊緣的距離</span>
<span style="color:#000088">private</span> <span style="color:#000088">float</span> b; <span style="color:#880000">//到螢幕下邊緣的距離</span>
<span style="color:#000088">private</span> <span style="color:#000088">float</span> t; <span style="color:#880000">//到螢幕上邊緣的距離</span>
<span style="color:#000088">private</span> <span style="color:#000088">float</span> d; <span style="color:#880000">//從映象攝像機到螢幕的距離</span>
<span style="color:#000088">private</span> <span style="color:#000088">float</span> n; <span style="color:#880000">//映象攝像機的近剪切面的距離</span>
<span style="color:#000088">private</span> <span style="color:#000088">float</span> f; <span style="color:#880000">//映象攝像機的遠剪切面的距離</span>
<span style="color:#000088">private</span> Vector3 pa; <span style="color:#880000">//世界座標系的左下角</span>
<span style="color:#000088">private</span> Vector3 pb; <span style="color:#880000">//世界座標系的右下角</span>
<span style="color:#000088">private</span> Vector3 pc; <span style="color:#880000">//世界座標系的左上角</span>
<span style="color:#000088">private</span> Vector3 pe; <span style="color:#880000">//映象觀察角度的世界座標位置</span>
<span style="color:#000088">private</span> Vector3 va; <span style="color:#880000">//從映象攝像機到左下角</span>
<span style="color:#000088">private</span> Vector3 vb; <span style="color:#880000">//從映象攝像機到右下角</span>
<span style="color:#000088">private</span> Vector3 vc; <span style="color:#880000">//從映象攝像機到左上角</span>
<span style="color:#000088">private</span> Vector3 vr; <span style="color:#880000">//螢幕的右側旋轉軸</span>
<span style="color:#000088">private</span> Vector3 vu; <span style="color:#880000">//螢幕的上側旋轉軸</span>
<span style="color:#000088">private</span> Matrix4x4 p = <span style="color:#000088">new</span> Matrix4x4();
<span style="color:#000088">private</span> Matrix4x4 rm = <span style="color:#000088">new</span> Matrix4x4();
<span style="color:#000088">private</span> Matrix4x4 tm = <span style="color:#000088">new</span> Matrix4x4();
<span style="color:#000088">private</span> Quaternion q = <span style="color:#000088">new</span> Quaternion();
<span style="color:#000088">private</span> <span style="color:#000088">void</span> Start()
{
mirrorCamera = GetComponent<Camera>();
}
<span style="color:#000088">private</span> <span style="color:#000088">void</span> Update()
{
<span style="color:#000088">if</span> (null == mirrorPlane || null == mirrorCamera) <span style="color:#000088">return</span>;
pa = mirrorPlane.transform.TransformPoint(<span style="color:#000088">new</span> Vector3(-<span style="color:#006666">5.0f</span>, <span style="color:#006666">0.0f</span>, -<span style="color:#006666">5.0f</span>)); <span style="color:#880000">//世界座標系的左下角</span>
pb = mirrorPlane.transform.TransformPoint(<span style="color:#000088">new</span> Vector3(<span style="color:#006666">5.0f</span>, <span style="color:#006666">0.0f</span>, -<span style="color:#006666">5.0f</span>)); <span style="color:#880000">//世界座標系的右下角</span>
pc = mirrorPlane.transform.TransformPoint(<span style="color:#000088">new</span> Vector3(-<span style="color:#006666">5.0f</span>, <span style="color:#006666">0.0f</span>, <span style="color:#006666">5.0f</span>)); <span style="color:#880000">//世界座標系的左上角</span>
pe = transform.position; <span style="color:#880000">//映象觀察角度的世界座標位置</span>
n = mirrorCamera.nearClipPlane; <span style="color:#880000">//映象攝像機的近剪切面的距離</span>
f = mirrorCamera.farClipPlane; <span style="color:#880000">//映象攝像機的遠剪切面的距離</span>
va = pa - pe; <span style="color:#880000">//從映象攝像機到左下角</span>
vb = pb - pe; <span style="color:#880000">//從映象攝像機到右下角</span>
vc = pc - pe; <span style="color:#880000">//從映象攝像機到左上角</span>
vr = pb - pa; <span style="color:#880000">//螢幕的右側旋轉軸</span>
vu = pc - pa; <span style="color:#880000">//螢幕的上側旋轉軸</span>
<span style="color:#000088">if</span> (Vector3.Dot(-Vector3.Cross(va, vc), vb) < <span style="color:#006666">0.0f</span>) <span style="color:#880000">//如果看向鏡子的背面</span>
{
vu = -vu;
pa = pc;
pb = pa + vr;
pc = pa + vu;
va = pa - pe;
vb = pb - pe;
vc = pc - pe;
}
vr.Normalize();
vu.Normalize();
vn = -Vector3.Cross(vr, vu); <span style="color:#880000">//兩個向量的叉乘,最後在取負,因為Unity是使用左手座標系</span>
vn.Normalize();
d = -Vector3.Dot(va, vn);
<span style="color:#000088">if</span> (setNearClipPlane)
{
n = d + nearClipDistanceOffset;
mirrorCamera.nearClipPlane = n;
}
l = Vector3.Dot(vr, va) * n / d;
r = Vector3.Dot(vr, vb) * n / d;
b = Vector3.Dot(vu, va) * n / d;
t = Vector3.Dot(vu, vc) * n / d;
<span style="color:#880000">//投影矩陣</span>
p[<span style="color:#006666">0</span>, <span style="color:#006666">0</span>] = <span style="color:#006666">2.0f</span> * n / (r - l);
p[<span style="color:#006666">0</span>, <span style="color:#006666">1</span>] = <span style="color:#006666">0.0f</span>;
p[<span style="color:#006666">0</span>, <span style="color:#006666">2</span>] = (r + l) / (r - l);
p[<span style="color:#006666">0</span>, <span style="color:#006666">3</span>] = <span style="color:#006666">0.0f</span>;
p[<span style="color:#006666">1</span>, <span style="color:#006666">0</span>] = <span style="color:#006666">0.0f</span>;
p[<span style="color:#006666">1</span>, <span style="color:#006666">1</span>] = <span style="color:#006666">2.0f</span> * n / (t - b);
p[<span style="color:#006666">1</span>, <span style="color:#006666">2</span>] = (t + b) / (t - b);
p[<span style="color:#006666">1</span>, <span style="color:#006666">3</span>] = <span style="color:#006666">0.0f</span>;
p[<span style="color:#006666">2</span>, <span style="color:#006666">0</span>] = <span style="color:#006666">0.0f</span>;
p[<span style="color:#006666">2</span>, <span style="color:#006666">1</span>] = <span style="color:#006666">0.0f</span>;
p[<span style="color:#006666">2</span>, <span style="color:#006666">2</span>] = (f + n) / (n - f);
p[<span style="color:#006666">2</span>, <span style="color:#006666">3</span>] = <span style="color:#006666">2.0f</span> * f * n / (n - f);
p[<span style="color:#006666">3</span>, <span style="color:#006666">0</span>] = <span style="color:#006666">0.0f</span>;
p[<span style="color:#006666">3</span>, <span style="color:#006666">1</span>] = <span style="color:#006666">0.0f</span>;
p[<span style="color:#006666">3</span>, <span style="color:#006666">2</span>] = -<span style="color:#006666">1.0f</span>;
p[<span style="color:#006666">3</span>, <span style="color:#006666">3</span>] = <span style="color:#006666">0.0f</span>;
<span style="color:#880000">//旋轉矩陣</span>
rm[<span style="color:#006666">0</span>, <span style="color:#006666">0</span>] = vr.x;
rm[<span style="color:#006666">0</span>, <span style="color:#006666">1</span>] = vr.y;
rm[<span style="color:#006666">0</span>, <span style="color:#006666">2</span>] = vr.z;
rm[<span style="color:#006666">0</span>, <span style="color:#006666">3</span>] = <span style="color:#006666">0.0f</span>;
rm[<span style="color:#006666">1</span>, <span style="color:#006666">0</span>] = vu.x;
rm[<span style="color:#006666">1</span>, <span style="color:#006666">1</span>] = vu.y;
rm[<span style="color:#006666">1</span>, <span style="color:#006666">2</span>] = vu.z;
rm[<span style="color:#006666">1</span>, <span style="color:#006666">3</span>] = <span style="color:#006666">0.0f</span>;
rm[<span style="color:#006666">2</span>, <span style="color:#006666">0</span>] = vn.x;
rm[<span style="color:#006666">2</span>, <span style="color:#006666">1</span>] = vn.y;
rm[<span style="color:#006666">2</span>, <span style="color:#006666">2</span>] = vn.z;
rm[<span style="color:#006666">2</span>, <span style="color:#006666">3</span>] = <span style="color:#006666">0.0f</span>;
rm[<span style="color:#006666">3</span>, <span style="color:#006666">0</span>] = <span style="color:#006666">0.0f</span>;
rm[<span style="color:#006666">3</span>, <span style="color:#006666">1</span>] = <span style="color:#006666">0.0f</span>;
rm[<span style="color:#006666">3</span>, <span style="color:#006666">2</span>] = <span style="color:#006666">0.0f</span>;
rm[<span style="color:#006666">3</span>, <span style="color:#006666">3</span>] = <span style="color:#006666">1.0f</span>;
tm[<span style="color:#006666">0</span>, <span style="color:#006666">0</span>] = <span style="color:#006666">1.0f</span>;
tm[<span style="color:#006666">0</span>, <span style="color:#006666">1</span>] = <span style="color:#006666">0.0f</span>;
tm[<span style="color:#006666">0</span>, <span style="color:#006666">2</span>] = <span style="color:#006666">0.0f</span>;
tm[<span style="color:#006666">0</span>, <span style="color:#006666">3</span>] = -pe.x;
tm[<span style="color:#006666">1</span>, <span style="color:#006666">0</span>] = <span style="color:#006666">0.0f</span>;
tm[<span style="color:#006666">1</span>, <span style="color:#006666">1</span>] = <span style="color:#006666">1.0f</span>;
tm[<span style="color:#006666">1</span>, <span style="color:#006666">2</span>] = <span style="color:#006666">0.0f</span>;
tm[<span style="color:#006666">1</span>, <span style="color:#006666">3</span>] = -pe.y;
tm[<span style="color:#006666">2</span>, <span style="color:#006666">0</span>] = <span style="color:#006666">0.0f</span>;
tm[<span style="color:#006666">2</span>, <span style="color:#006666">1</span>] = <span style="color:#006666">0.0f</span>;
tm[<span style="color:#006666">2</span>, <span style="color:#006666">2</span>] = <span style="color:#006666">1.0f</span>;
tm[<span style="color:#006666">2</span>, <span style="color:#006666">3</span>] = -pe.z;
tm[<span style="color:#006666">3</span>, <span style="color:#006666">0</span>] = <span style="color:#006666">0.0f</span>;
tm[<span style="color:#006666">3</span>, <span style="color:#006666">1</span>] = <span style="color:#006666">0.0f</span>;
tm[<span style="color:#006666">3</span>, <span style="color:#006666">2</span>] = <span style="color:#006666">0.0f</span>;
tm[<span style="color:#006666">3</span>, <span style="color:#006666">3</span>] = <span style="color:#006666">1.0f</span>;
mirrorCamera.projectionMatrix = p; <span style="color:#880000">//矩陣組</span>
mirrorCamera.worldToCameraMatrix = rm * tm;
<span style="color:#000088">if</span> (!estimateViewFrustum) <span style="color:#000088">return</span>;
q.SetLookRotation((<span style="color:#006666">0.5f</span> * (pb + pc) - pe), vu); <span style="color:#880000">//旋轉攝像機</span>
mirrorCamera.transform.rotation = q; <span style="color:#880000">//聚焦到螢幕的中心點</span>
<span style="color:#880000">//估值 —— 三目簡寫</span>
mirrorCamera.fieldOfView = mirrorCamera.aspect >= <span style="color:#006666">1.0</span> ? Mathf.Rad2Deg * Mathf.Atan(((pb - pa).magnitude + (pc - pa).magnitude) / va.magnitude) : Mathf.Rad2Deg / mirrorCamera.aspect * Mathf.Atan(((pb - pa).magnitude + (pc - pa).magnitude) / va.magnitude);
<span style="color:#880000">//在攝像機角度考慮,保證視錐足夠寬</span>
}
}</code></span>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
- 112
- 113
- 114
- 115
- 116
- 117
- 118
- 119
- 120
- 121
- 122
- 123
- 124
- 125
- 126
- 127
- 128
- 129
- 130
- 131
- 132
- 133
- 134
- 135
- 136
- 137
- 138
- 139
- 140
- 141
- 142
- 143
- 144
- 145
- 146
- 147
- 148
- 149
- 150
- 151
- 152
- 153
- 154
- 155
- 156
3
Main Camera —— 主相機指令碼(方便看到測試效果)
4
Create Cube —— 建立一個立方體
為了看鏡子的效果
在場景中建立一個 Cube —— 用來作為參照物件
然後點選執行後,即可看到鏡子效果已經完成
5
Indistinct —— 顯示效果不清晰
如果發現,鏡子的顯示效果並不清晰
這是因為我們建立的 Render Texture 時使用的是預設的解析度 256*256
修改成較高的解析度即可,這裡我修改為:1024*1024 (可視情況自己設定)