Unity镜子效果制作教程


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

Chinar 坚持将简单的生活方式,带给世人!

(拥有更好的阅读体验 —— 高分辨率用户请根据需求调整网页缩放比例)


Chinar —— 心分享、心创新!

助力快速实现一个简单的镜面反射效果

为新手节省宝贵的时间,避免采坑!

Chinar 教程效果:


  • 1

    • Create Mirror —— 创建镜子
  • 2
    • Create Camera —— 创建一个新相机
  • 3
    • Main Camera —— 主相机脚本(方便看到测试效果)
  • 4
    • Create Cube —— 创建一个立方体
  • 5
    • Indistinct —— 显示效果不清晰
  • 6
    • Project —— 项目文件
  • 支持
    • May Be —— 搞开发,总有一天要做的事!

全文高清图片,点击即可放大观看 (很多人竟然不知道)


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:

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; //镜子Planepublic  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);//在摄像机角度考虑,保证视锥足够宽}
}

3

Main Camera —— 主相机脚本(方便看到测试效果)

为了方便看到运行后的镜面效果, 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);}
}

4

Create Cube —— 创建一个立方体

为了看镜子的效果

在场景中创建一个 Cube —— 用来作为参照对象

然后点击运行后,即可看到镜子效果已经完成



5

Indistinct —— 显示效果不清晰

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

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

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

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


至此:镜子的制作教程结束


6

Project —— 项目文件

项目文件为 unitypackage 文件包:

下载导入 Unity 即可使用

点击下载 —— 项目资源


支持

May Be —— 搞开发,总有一天要做的事!

拥有自己的服务器,无需再找攻略!

Chinar 提供一站式教程,闭眼式创建!

为新手节省宝贵时间,避免采坑!

先点击领取 —— 阿里全产品优惠券 (享受最低优惠)

1 —— 云服务器超全购买流程 (新手必备!)

2 —— 阿里ECS云服务器自定义配置 - 购买教程(新手必备!)

3—— Windows 服务器配置、运行、建站一条龙 !

4 —— Linux 服务器配置、运行、建站一条龙 !



技术交流群:806091680 ! Chinar 欢迎你的加入


END

本博客为非营利性个人原创,除部分有明确署名的作品外,所刊登的所有作品的著作权均为本人所拥有,本人保留所有法定权利。违者必究

对于需要复制、转载、链接和传播博客文章或内容的,请及时和本博主进行联系,留言,Email: ichinar@icloud.com

对于经本博主明确授权和许可使用文章及内容的,使用时请注明文章或内容出处并注明网址

转载于:https://www.cnblogs.com/chinarbolg/p/9601388.html

Unity镜子效果的实现(无需镜子Shader)相关推荐

  1. 【Unity Shader】渲染纹理实现镜子效果

    1 基本概念 1.1 什么是渲染到纹理? 全称是Render To Texture,<入门精要>好像又把渲染目标纹理,即Render Target Texture也叫做RTT,但我认为&l ...

  2. shader镜子效果错误

    shader镜子效果错误 今天实现了下Unity shader的镜子效果,但是离谱的是跟着书上敲出来的代码,放在书上提供的场景中效果是正常的,我自己搭的场景就出现了左右都会映射,而且一层接一层.可能解 ...

  3. shader入门精要读书笔记23 高级纹理-渲染纹理-镜子效果、玻璃效果

    一.前言 现代GPU允许我们把整个三维场景目标渲染到一个中间缓冲中,即渲染目标纹理(RTT). 多重渲染目标(MRT),这种技术指的是GPU允许我们把场景同时渲染到多个渲染目标纹理中,而不再需要为每个 ...

  4. android 图形平移 镜子效果 倒影效果

    图形平移 setTranslate(1.5f,-10); 镜子效果 matrix.setScale(-1, 1); matrix.postTranslate(bmp.getWidth(),0); 倒影 ...

  5. Unity 屏幕特效 之 简单地使用 Shader 获取深度,实现景深效果

    Unity 屏幕特效 之 简单地使用 Shader 获取深度,实现景深效果 目录

  6. Unity 涂涂乐(不使用shader)

    一般来说他们做涂涂乐都用到shader,可惜渣渣的我不会shader,就算用了shader,我看不懂里面的原理,会让我很烦,所以就另寻他法 无意中看到unity的商店里有一个涂涂乐的免费教程 最终的实 ...

  7. Unity 毛玻璃效果(UGUI)—高斯模糊

    因为Unity 提供了GrabPass,可以在Shader中很方便的拿到Panel下面的图像,即_GrabTexture. 注:添加优化.原本的搞死模糊效果达不到苹果的那种细腻效果,修改参数即可. 修 ...

  8. Unity 3D学习视觉脚本无需编码即可创建高级游戏

    在本课程中,您将学习如何在Unity中使用可视化脚本(以前称为Bolt)以及如何在不编写一行代码的情况下创建自己的高级游戏所需的一切.本课程将教你如何掌握可视化脚本,即使你以前没有任何关于unity或 ...

  9. unity 半透明混合问题_Unity 实时 半透明 阴影 shader

    简单阴影制作思路: 1:在角色脚底 放置一块平板 2:shader中 根据平板传入的矩阵 以及 光照 对角色进行变换 3: 得到投影在地面上的阴影 4:阴影直接渲染到 屏幕上 缺点: 上面的阴影无法 ...

最新文章

  1. Uva 10537 过路费
  2. 碰撞的小球 ccf (模拟)
  3. 深度学习实践经验汇总
  4. UVA11525 Permutation 逆康托展开
  5. 微信支付api的服务器上,服务器微信支付接口笔记(与app端对接)
  6. 一道多线程通信实例分析
  7. 把地图中的道路以线条提取出来_自动驾驶中基于车道线的高清制图方法回顾
  8. Android之——图片的内存优化
  9. java api在jdk哪里,jdk api文档在哪里
  10. MATLAB图像处理学习日记之__图像的K-means均值法与局部阈值和迭代式阈值分割法算法——整理资源汇总
  11. or-tools求解指派问题
  12. 从智能家居的发展看对讲企业的定位
  13. ORA-00257错误解决方法
  14. java算法:1234组成无重复的三位数
  15. 百度直播消息系统的实践和演进
  16. win10 mac的clover双系统后 win10时间不同步问题
  17. linux 备份信息查看器,linux 全新的备份神器 Duplicity
  18. vue视频播放插件vue-video-player的具体使用方法
  19. 使用natapp实现内网穿透详细教程
  20. iOS代码混淆的探索

热门文章

  1. android 切换 preferencefragment,Android PreferenceActivity与PreferenceFragment详解及简单实例_Android_脚本之家...
  2. python读txt转array_np.array和txt文件的转换
  3. php数组合并如何不覆盖,php如何实现合并数组并且不覆盖重复值
  4. linux时间 a x z,关于linux的命令一个莫名的想法,a到z有哪些命令
  5. mysql怎么更新单一值_MySQL 如何更新某个字段的值为原来的值加1
  6. G502使用计算机配置,解决方案:Logitech G502宏设置教程方法
  7. html折线图怎么控制y轴数值,echarts Y轴数据类型不同怎么让折线图显示差距不大...
  8. el-tree设置某节点不能折叠_如何设置动画角色
  9. 芯片设计抽象层及其设计风格
  10. 用css动画写一个下红包雨的效果