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 (可视情况自己设定)

猜你喜欢

转载自blog.csdn.net/qq_34412086/article/details/81114335
今日推荐