1. 程式人生 > >Unity無需Shader實現鏡子效果

Unity無需Shader實現鏡子效果

Unity鏡子效果製作教程
 

 


本文提供全流程,中文翻譯。

Chinar 堅持將簡單的生活方式,帶給世人!

(擁有更好的閱讀體驗 —— 高解析度使用者請根據需求調整網頁縮放比例)

 


Chinar —— 心分享、心創新!

助力快速實現一個簡單的鏡面反射效果

為新手節省寶貴的時間,避免採坑!

 

Chinar 教程效果: 
這裡寫圖片描述


 

 


 

全文高清圖片,點選即可放大觀看 (很多人竟然不知道)

 


1

Create Mirror —— 建立鏡子

 

本教程,無需自己找鏡子Shader,只需2個指令碼即可在Unity中建立一個簡單的模擬鏡面反射效果

1. 在場景中建立一個 Plane —— 用來作為鏡子

2. 同時建立一個材質球 /Material —— 給到 Plane 上

3. 修改新建立的 Material 的 Shader 為 Unlit/Texture

舉個栗子黑白88 
這裡寫圖片描述


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 材質的屬性為: 
這裡寫圖片描述 
具體流程其實很簡單,如下 
舉個栗子黑白88 
這裡寫圖片描述 
兩個指令碼,都需要掛載到 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 (可視情況自己設定)