Unity Docker镜像构建与物体镜像管理

unity docker镜像 unity物体镜像_世界坐标系

Create Mirror —— 创建镜子,点击下载 —— 项目资源

本教程,无需自己找镜子Shader,只需2个脚本即可在Unity中创建一个简单的模拟镜面反射效果

1. 在场景中创建一个 Plane —— 用来作为镜子

2. 同时创建一个材质球 /Material —— 给到 Plane 上

3. 修改新创建的 Material 的 Shader 为 Unlit/Texture

unity docker镜像 unity物体镜像_ci_02

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 材质的属性为:


unity docker镜像 unity物体镜像_世界坐标_03

unity docker镜像 unity物体镜像_世界坐标系_04

两个脚本,都需要挂载到 Camera:

登录后复制

using UnityEngine;


/// <summary>
/// 镜子管理脚本 —— 挂在新建的Camera上
/// </summary>
[ExecuteInEditMode]
public class ChinarMirror : MonoBehaviour
{
    public  GameObject mirrorPlane;  //镜子
    public  Camera     mainCamera;   //主摄像机
    private Camera     mirrorCamera; //镜像摄像机


    private void Start()
    {
        mirrorCamera = GetComponent<Camera>();
    }


    private void Update()
    {
        if (null == mirrorPlane || null == mirrorCamera || null == mainCamera) return;
        Vector3 postionInMirrorSpace    = mirrorPlane.transform.InverseTransformPoint(mainCamera.transform.position); //将主摄像机的世界坐标位置转换为镜子的局部坐标位置
        postionInMirrorSpace.y          = -postionInMirrorSpace.y;                                                    //一般y为镜面的法线方向
        mirrorCamera.transform.position = mirrorPlane.transform.TransformPoint(postionInMirrorSpace);                 //转回到世界坐标系的位置
    }
}

  

登录后复制

using UnityEngine;

/// <summary>
/// Plane管理脚本 —— 挂载新建的Camera上
/// </summary>
[ExecuteInEditMode] //编辑模式中执行
public class ChinarMirrorPlane : MonoBehaviour
{
    public  GameObject mirrorPlane; //镜子Plane
    public  bool       estimateViewFrustum    = true;
    public  bool       setNearClipPlane       = true;   //是否设置*剪切*面
    public  float      nearClipDistanceOffset = -0.01f; //*剪切*面的距离
    private Camera     mirrorCamera;                    //镜像摄像机
    private Vector3    vn;                              //屏幕的法线
    private float      l;                               //到屏幕左边缘的距离
    private float      r;                               //到屏幕右边缘的距离
    private float      b;                               //到屏幕下边缘的距离
    private float      t;                               //到屏幕上边缘的距离
    private float      d;                               //从镜像摄像机到屏幕的距离
    private float      n;                               //镜像摄像机的*剪切面的距离
    private float      f;                               //镜像摄像机的远剪切面的距离
    private Vector3    pa;                              //世界坐标系的左下角
    private Vector3    pb;                              //世界坐标系的右下角
    private Vector3    pc;                              //世界坐标系的左上角
    private Vector3    pe;                              //镜像观察角度的世界坐标位置
    private Vector3    va;                              //从镜像摄像机到左下角
    private Vector3    vb;                              //从镜像摄像机到右下角
    private Vector3    vc;                              //从镜像摄像机到左上角
    private Vector3    vr;                              //屏幕的右侧旋转轴
    private Vector3    vu;                              //屏幕的上侧旋转轴
    private Matrix4x4  p  = new Matrix4x4();
    private Matrix4x4  rm = new Matrix4x4();
    private Matrix4x4  tm = new Matrix4x4();
    private Quaternion q  = new Quaternion();


    private void Start()
    {
        mirrorCamera = GetComponent<Camera>();
    }


    private void Update()
    {
        if (null == mirrorPlane || null == mirrorCamera) return;
        pa = mirrorPlane.transform.TransformPoint(new Vector3(-5.0f, 0.0f, -5.0f)); //世界坐标系的左下角
        pb = mirrorPlane.transform.TransformPoint(new Vector3(5.0f,  0.0f, -5.0f)); //世界坐标系的右下角
        pc = mirrorPlane.transform.TransformPoint(new Vector3(-5.0f, 0.0f, 5.0f));  //世界坐标系的左上角
        pe = transform.position;                                                    //镜像观察角度的世界坐标位置
        n  = mirrorCamera.nearClipPlane;                                            //镜像摄像机的*剪切面的距离
        f  = mirrorCamera.farClipPlane;                                             //镜像摄像机的远剪切面的距离
        va = pa - pe;                                                               //从镜像摄像机到左下角
        vb = pb - pe;                                                               //从镜像摄像机到右下角
        vc = pc - pe;                                                               //从镜像摄像机到左上角
        vr = pb - pa;                                                               //屏幕的右侧旋转轴
        vu = pc - pa;                                                               //屏幕的上侧旋转轴
        if (Vector3.Dot(-Vector3.Cross(va, vc), vb) < 0.0f)                         //如果看向镜子的背面
        {
            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); //两个向量的叉乘,最后在取负,因为Unity是使用左手坐标系
        vn.Normalize();
        d = -Vector3.Dot(va, vn);
        if (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;


        //投影矩阵
        p[0, 0] = 2.0f * n / (r - l);
        p[0, 1] = 0.0f;
        p[0, 2] = (r + l) / (r - l);
        p[0, 3] = 0.0f;

        p[1, 0] = 0.0f;
        p[1, 1] = 2.0f * n / (t - b);
        p[1, 2] = (t + b) / (t - b);
        p[1, 3] = 0.0f;

        p[2, 0] = 0.0f;
        p[2, 1] = 0.0f;
        p[2, 2] = (f + n) / (n - f);
        p[2, 3] = 2.0f * f * n / (n - f);

        p[3, 0] = 0.0f;
        p[3, 1] = 0.0f;
        p[3, 2] = -1.0f;
        p[3, 3] = 0.0f;

        //旋转矩阵
        rm[0, 0] = vr.x;
        rm[0, 1] = vr.y;
        rm[0, 2] = vr.z;
        rm[0, 3] = 0.0f;

        rm[1, 0] = vu.x;
        rm[1, 1] = vu.y;
        rm[1, 2] = vu.z;
        rm[1, 3] = 0.0f;

        rm[2, 0] = vn.x;
        rm[2, 1] = vn.y;
        rm[2, 2] = vn.z;
        rm[2, 3] = 0.0f;

        rm[3, 0] = 0.0f;
        rm[3, 1] = 0.0f;
        rm[3, 2] = 0.0f;
        rm[3, 3] = 1.0f;

        tm[0, 0] = 1.0f;
        tm[0, 1] = 0.0f;
        tm[0, 2] = 0.0f;
        tm[0, 3] = -pe.x;

        tm[1, 0] = 0.0f;
        tm[1, 1] = 1.0f;
        tm[1, 2] = 0.0f;
        tm[1, 3] = -pe.y;

        tm[2, 0] = 0.0f;
        tm[2, 1] = 0.0f;
        tm[2, 2] = 1.0f;
        tm[2, 3] = -pe.z;

        tm[3, 0] = 0.0f;
        tm[3, 1] = 0.0f;
        tm[3, 2] = 0.0f;
        tm[3, 3] = 1.0f;


        mirrorCamera.projectionMatrix    = p; //矩阵组
        mirrorCamera.worldToCameraMatrix = rm * tm;
        if (!estimateViewFrustum) return;
        q.SetLookRotation((0.5f * (pb + pc) - pe), vu); //旋转摄像机
        mirrorCamera.transform.rotation = q;            //聚焦到屏幕的中心点

        //估值 —— 三目简写
        mirrorCamera.fieldOfView = mirrorCamera.aspect >= 1.0 ? 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);
        //在摄像机角度考虑,保证视锥足够宽
    }
}

为了方便看到运行后的镜面效果, Chinar 在这里提供了一个第三人称的脚本

用于转镜头,看不同方位,需要挂载到主相机上,并将层次列表中的 Plane 拖到 Pivot 上

登录后复制

using UnityEngine;

/// <summary>
/// 主相机脚本 —— 挂载到主相机上,并 层次列表中的 Plane 拖到 pivot 
/// </summary>
public class ChinarCamera : MonoBehaviour
{
    public  Transform pivot;
    public  Vector3   pivotOffset = Vector3.zero;
    public  Transform target;
    public  float     distance       = 10.0f;
    public  float     minDistance    = 2f;
    public  float     maxDistance    = 15f;
    public  float     zoomSpeed      = 1f;
    public  float     xSpeed         = 250.0f;
    public  float     ySpeed         = 120.0f;
    public  bool      allowYTilt     = true;
    public  float     yMinLimit      = -90f;
    public  float     yMaxLimit      = 90f;
    private float     x              = 0.0f;
    private float     y              = 0.0f;
    private float     targetX        = 0f;
    private float     targetY        = 0f;
    private float     targetDistance = 0f;
    private float     xVelocity      = 1f;
    private float     yVelocity      = 1f;
    private float     zoomVelocity   = 1f;


    private void Start()
    {
        var angles     = transform.eulerAngles;
        targetX        = x = angles.x;
        targetY        = y = ClampAngle(angles.y, yMinLimit, yMaxLimit);
        targetDistance = distance;
    }


    private void LateUpdate()
    {
        if (!pivot) return;
        var scroll = Input.GetAxis("Mouse ScrollWheel");

        if (scroll > 0.0f) targetDistance -= zoomSpeed;
        else if (scroll < 0.0f)
            targetDistance += zoomSpeed;
        targetDistance     =  Mathf.Clamp(targetDistance, minDistance, maxDistance);
        if (Input.GetMouseButton(1) || (Input.GetMouseButton(0) && (Input.GetKey(KeyCode.LeftControl) || Input.GetKey(KeyCode.RightControl))))
        {
            targetX += Input.GetAxis("Mouse X") * xSpeed * 0.02f;
            if (allowYTilt)
            {
                targetY -= Input.GetAxis("Mouse Y") * ySpeed * 0.02f;
                targetY =  ClampAngle(targetY, yMinLimit, yMaxLimit);
            }
        }
        x                   = Mathf.SmoothDampAngle(x,              targetX, ref xVelocity, 0.3f);
        y                   = allowYTilt ? Mathf.SmoothDampAngle(y, targetY, ref yVelocity, 0.3f) : targetY;
        Quaternion rotation = Quaternion.Euler(y, x, 0);
        distance            = Mathf.SmoothDamp(distance, targetDistance, ref zoomVelocity, 0.5f);
        Vector3 position    = rotation * new Vector3(0.0f, 0.0f, -distance) + pivot.position + pivotOffset;
        transform.rotation  = rotation;
        transform.position  = position;
    }


    private static float ClampAngle(float angle, float min, float max)
    {
        if (angle < -360) angle += 360;
        if (angle > 360) angle  -= 360;
        return Mathf.Clamp(angle, min, max);
    }
}

Indistinct —— 显示效果不清晰

如果发现,镜子的显示效果并不清晰

这是因为我们创建的 Render Texture 时使用的是默认的分辨率 256*256

修改成较高的分辨率即可,这里我修改为:1024*1024 (可视情况自己设定)

注意:分辨率设置越高,是越耗性能的 

 

免责声明:本文系网络转载或改编,未找到原创作者,版权归原作者所有。如涉及版权,请联系删

QR Code
微信扫一扫,欢迎咨询~

联系我们
武汉格发信息技术有限公司
湖北省武汉市经开区科技园西路6号103孵化器
电话:155-2731-8020 座机:027-59821821
邮件:tanzw@gofarlic.com
Copyright © 2023 Gofarsoft Co.,Ltd. 保留所有权利
遇到许可问题?该如何解决!?
评估许可证实际采购量? 
不清楚软件许可证使用数据? 
收到软件厂商律师函!?  
想要少购买点许可证,节省费用? 
收到软件厂商侵权通告!?  
有正版license,但许可证不够用,需要新购? 
联系方式 155-2731-8020
预留信息,一起解决您的问题
* 姓名:
* 手机:

* 公司名称:

姓名不为空

手机不正确

公司不为空