unity镜子 - CSDN
精华内容
参与话题
  • unity中实现镜子效果

    千次阅读 2018-02-26 08:16:05
    上一篇介绍了有关镜子的制作,有关理论部分的内容我会在后续相关的文章中陆续介绍,莫急,我先趁着自己脑子还是对此技术比较热,趁热打铁尽早把实现部分先写出来。上一章的介绍制作的镜子其实只是一个取巧的方法,并...

    本文转载自http://blog.csdn.net/zhangxiao13627093203/article/details/52403186

    上一篇介绍了有关镜子的制作,有关理论部分的内容我会在后续相关的文章中陆续介绍,莫急,我先趁着自己脑子还是对此技术比较热,趁热打铁尽早把实现部分先写出来。上一章的介绍制作的镜子其实只是一个取巧的方法,并不能做到实时的反射出实物,但是思路还是比较有意思的。

    
    

    还是废话不多说先上图,我用的Unity版本5.3.3

    二、Unity中制作原理

    1、简单说明:其实这个原理就是用一个摄像机去拍镜子上面的物体将得到的图像投影给Plane,最后主摄像机就能看到Plane上物体的镜像,所以关键的部分就是计算摄像机上的投影矩阵和主摄像机的投影矩阵的关系,因为站在不同的角度看(主摄像机转动或移动)镜像是要跟着偏移的

    2、创建一个Camera作为镜像摄像机,将下面计算摄像机的投影平面的脚本代码拖到这个Camera上

    using UnityEngine;
    using System.Collections;
    [ExecuteInEditMode]
    public class ViewPlane : MonoBehaviour {
        public GameObject mirrorPlane;                      //镜子屏幕
    
        public bool estimateViewFrustum = true;
        public bool setNearClipPlane = false;               //是否设置近剪切平面
    
        public float nearClipDistanceOffset = -0.01f;       //近剪切平面的距离
    
        private Camera mirrorCamera;                        //镜像摄像机
        // Use this for initialization
        void Start () {
            mirrorCamera = GetComponent<Camera>();
    
        }
    
        // Update is called once per frame
        void Update () {
    
            if (null != mirrorPlane && null != mirrorCamera)
            {
                //世界坐标系的左下角
                Vector3 pa = mirrorPlane.transform.TransformPoint(new Vector3(-5.0f, 0.0f, -5.0f));
    
                //世界坐标系的右下角
                Vector3 pb = mirrorPlane.transform.TransformPoint(new Vector3(5.0f, 0.0f, -5.0f));
    
                //世界坐标系的左上角
                Vector3 pc = mirrorPlane.transform.TransformPoint(new Vector3(-5.0f, 0.0f, 5.0f));
    
                //镜像观察角度的世界坐标位置
                Vector3 pe = transform.position;
    
                //镜像摄像机的近剪切面的距离
                float n = mirrorCamera.nearClipPlane;
    
                //镜像摄像机的远剪切面的距离
                float f = mirrorCamera.farClipPlane;
    
                //从镜像摄像机到左下角
                Vector3 va = pa - pe;
    
                //从镜像摄像机到右下角
                Vector3 vb = pb - pe;
    
                //从镜像摄像机到左上角
                Vector3 vc = pc - pe;
    
                //屏幕的右侧旋转轴
                Vector3 vr = pb - pa;
    
                //屏幕的上侧旋转轴
                Vector3 vu = pc - pa;
    
                //屏幕的法线
                Vector3 vn;
    
                //到屏幕左边缘的距离
                float l;
    
                //到屏幕右边缘的距离
                float r;
    
                //到屏幕下边缘的距离
                float b;
    
                //到屏幕上边缘的距离
                float t;
    
                //从镜像摄像机到屏幕的距离
                float d;
    
                //如果看向镜子的背面
                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();
    
                //两个向量的叉乘,最后在取负,因为Unity是使用左手坐标系
                vn = -Vector3.Cross(vr, vu);
    
                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;
    
                //投影矩阵
                Matrix4x4 p = new Matrix4x4();
                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;
    
                //旋转矩阵
                Matrix4x4 rm = new Matrix4x4();
                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;
    
                Matrix4x4 tm = new Matrix4x4();
                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)
                {
                    //旋转摄像机
                    Quaternion q = new Quaternion();
                    q.SetLookRotation((0.5f * (pb + pc) - pe), vu);
                    //聚焦到屏幕的中心点
                    mirrorCamera.transform.rotation = q;
    
                    //保守估计fieldOfView的值
                    if (mirrorCamera.aspect >= 1.0)
                    {
                        mirrorCamera.fieldOfView = Mathf.Rad2Deg *
                           Mathf.Atan(((pb - pa).magnitude + (pc - pa).magnitude)
                           / va.magnitude);
                    }
                    else
                    {
                        //在摄像机角度考虑,保证视锥足够宽
                        mirrorCamera.fieldOfView =
                           Mathf.Rad2Deg / mirrorCamera.aspect *
                           Mathf.Atan(((pb - pa).magnitude + (pc - pa).magnitude)
                           / va.magnitude);
                    }
                }
            }
        }
    }
    
    3、创建一个Plane作为镜子,这个Plane的Shader必须要是一个能接受贴图的,所以这里可以自行使用Unity自带的Shader,我选择了Unlit/Texture

    4、将下面的代码赋给2中创建的Camera,将主摄像机给MainCamrea变量,镜子Plane赋值给MirrorPlane变量,

    using UnityEngine;
    using System.Collections;
    [ExecuteInEditMode]
    public class Mirrors2 : MonoBehaviour {
    
        public GameObject mirrorPlane;//镜子
        public Camera mainCamera;//主摄像机
        private  Camera mirrorCamera;//镜像摄像机
        // Use this for initialization
        void Start () {
            mirrorCamera = GetComponent<Camera>();
    
        }
    
        // Update is called once per frame
        void Update () {
    
            if(null!=mirrorPlane&&null!=mirrorCamera&&null!=mainCamera)
            {
                //将主摄像机的世界坐标位置转换为镜子的局部坐标位置
                Vector3 postionInMirrorSpace = mirrorPlane.transform.InverseTransformPoint(mainCamera.transform.position);
    
                //一般y为镜面的法线方向
                postionInMirrorSpace.y = -postionInMirrorSpace.y;
    
                //转回到世界坐标系的位置
                mirrorCamera.transform.position = mirrorPlane.transform.TransformPoint(postionInMirrorSpace);
            }
        }
    }
    
    5、如果是刚创建的Camera的投影梯形一定是非常规则的如图所示,勾选这个Camera

    上的ViewPlane脚本上的setNearClipPlane,这个时候其实是在设置这个Camera的近剪切屏幕,使得这个平面尽量与镜子Plane平面重合,如图所示,这个还不是完全的重合,可以通过调整参数nearClipDistanceOffset来调整,默认的-0.01其实就是我已经调

    整好了的参数,只要勾选setNearClipPlane就会自动调整到与镜子平面重合,如图所示


    6、最后创建一个RenderTexture,这个Texture就是用来将镜像摄像机投影出来的图像信息传递给镜子Plane的中间变量,所以将这个Texture分别托给Plane的Shader材质中的Texture和镜像摄像机中的TargetTexture。最后设置这个Texture的分辨率,我设置成了1024×1024,默认的是256×256,这样的会造成镜像模糊,如图所示


    设置成1024×1024之后就如第一章图所示,清晰度比较高。

    三、总结

    1、缺点:镜子不能有自己的贴图、不能实现多面镜子同时相互反射(还有待发现)

    2、相比上一篇的优点:可以实时的反射所有在镜面上的物体,可以改变物体的光照和贴图

    3、什么不是Shader,一句Shader代码都没有怎么还是Shader的案例,跟Shader有毛关系?是的,的确没有一句Shader的代码,然而使用C#代码控制其实都是Shader里面的参数,比如投影矩阵的计算,尤其是此处的投影区域有不规则倾斜的情况。

    后续待…

    原文转载请注明出处点击打开链接

    工程文件下载地址点击打开链接

    展开全文
  • 做项目的时候要用到镜子,折腾了一下午,发现做镜子还是蛮快的,总结了一下网上的教程,大致有这么几种做法: 1、使用反射探针(Reflection Probe) 2、使用Render Texture 3、使用Shader着色器,我发现这种方法...

    做项目的时候要用到镜子,折腾了一下午,发现做镜子还是蛮快的,总结了一下网上的教程,大致有这么几种做法:

    1、使用反射探针(Reflection Probe)

    2、使用Render Texture

    3、使用Shader着色器,我发现这种方法步骤比较少,而且重复使用起来也方便

    下面就介绍一下这个方法,以及期间我踩过的坑;官方的教程在这里:MirrorReflection4

    (我之所以说比较简单,是因为Shader方法可以用网上现成的代码,基本不用改

    话不多说进入正题:

    首先利用Shader做镜子,需要两段代码,一个是.shader着色器代码,另一个是C#脚本代码

    着色器代码如下:

    Mirror.shader

    Shader "FX/MirrorReflection"
    {
    	Properties
    	{
    		_MainTex ("Base (RGB)", 2D) = "white" {}
    		[HideInInspector] _ReflectionTex ("", 2D) = "white" {}
    	}
    	SubShader
    	{
    		Tags { "RenderType"="Opaque" }
    		LOD 100
     
    		Pass {
    			CGPROGRAM
    			#pragma vertex vert
    			#pragma fragment frag
    			#include "UnityCG.cginc"
    			struct v2f
    			{
    				float2 uv : TEXCOORD0;
    				float4 refl : TEXCOORD1;
    				float4 pos : SV_POSITION;
    			};
    			float4 _MainTex_ST;
    			v2f vert(float4 pos : POSITION, float2 uv : TEXCOORD0)
    			{
    				v2f o;
    				o.pos = mul (UNITY_MATRIX_MVP, pos);
    				o.uv = TRANSFORM_TEX(uv, _MainTex);
    				o.refl = ComputeScreenPos (o.pos);
    				return o;
    			}
    			sampler2D _MainTex;
    			sampler2D _ReflectionTex;
    			fixed4 frag(v2f i) : SV_Target
    			{
    				fixed4 tex = tex2D(_MainTex, i.uv);
    				fixed4 refl = tex2Dproj(_ReflectionTex, UNITY_PROJ_COORD(i.refl));
    				return tex * refl;
    			}
    			ENDCG
    	    }
    	}
    }
    

    C#脚本代码如下:

    MirrorReflection.cs

    using UnityEngine;
    using System.Collections;
     
    // This is in fact just the Water script from Pro Standard Assets,
    // just with refraction stuff removed.
     
    [ExecuteInEditMode] // Make mirror live-update even when not in play mode
    public class MirrorReflection : MonoBehaviour
    {
    	public bool m_DisablePixelLights = true;
    	public int m_TextureSize = 256;
    	public float m_ClipPlaneOffset = 0.07f;
     
    	public LayerMask m_ReflectLayers = -1;
     
    	private Hashtable m_ReflectionCameras = new Hashtable(); // Camera -> Camera table
     
    	private RenderTexture m_ReflectionTexture = null;
    	private int m_OldReflectionTextureSize = 0;
     
    	private static bool s_InsideRendering = false;
     
    	// This is called when it's known that the object will be rendered by some
    	// camera. We render reflections and do other updates here.
    	// Because the script executes in edit mode, reflections for the scene view
    	// camera will just work!
    	public void OnWillRenderObject()
    	{
    		var rend = GetComponent<Renderer>();
    		if (!enabled || !rend || !rend.sharedMaterial || !rend.enabled)
    			return;
     
    		Camera cam = Camera.current;
    		if( !cam )
    			return;
     
    		// Safeguard from recursive reflections.        
    		if( s_InsideRendering )
    			return;
    		s_InsideRendering = true;
     
    		Camera reflectionCamera;
    		CreateMirrorObjects( cam, out reflectionCamera );
     
    		// find out the reflection plane: position and normal in world space
    		Vector3 pos = transform.position;
    		Vector3 normal = transform.up;
     
    		// Optionally disable pixel lights for reflection
    		int oldPixelLightCount = QualitySettings.pixelLightCount;
    		if( m_DisablePixelLights )
    			QualitySettings.pixelLightCount = 0;
     
    		UpdateCameraModes( cam, reflectionCamera );
     
    		// Render reflection
    		// Reflect camera around reflection plane
    		float d = -Vector3.Dot (normal, pos) - m_ClipPlaneOffset;
    		Vector4 reflectionPlane = new Vector4 (normal.x, normal.y, normal.z, d);
     
    		Matrix4x4 reflection = Matrix4x4.zero;
    		CalculateReflectionMatrix (ref reflection, reflectionPlane);
    		Vector3 oldpos = cam.transform.position;
    		Vector3 newpos = reflection.MultiplyPoint( oldpos );
    		reflectionCamera.worldToCameraMatrix = cam.worldToCameraMatrix * reflection;
     
    		// Setup oblique projection matrix so that near plane is our reflection
    		// plane. This way we clip everything below/above it for free.
    		Vector4 clipPlane = CameraSpacePlane( reflectionCamera, pos, normal, 1.0f );
    		//Matrix4x4 projection = cam.projectionMatrix;
    		Matrix4x4 projection = cam.CalculateObliqueMatrix(clipPlane);
    		reflectionCamera.projectionMatrix = projection;
     
    		reflectionCamera.cullingMask = ~(1<<4) & m_ReflectLayers.value; // never render water layer
    		reflectionCamera.targetTexture = m_ReflectionTexture;
    		GL.SetRevertBackfacing (true);
    		reflectionCamera.transform.position = newpos;
    		Vector3 euler = cam.transform.eulerAngles;
    		reflectionCamera.transform.eulerAngles = new Vector3(0, euler.y, euler.z);
    		reflectionCamera.Render();
    		reflectionCamera.transform.position = oldpos;
    		GL.SetRevertBackfacing (false);
    		Material[] materials = rend.sharedMaterials;
    		foreach( Material mat in materials ) {
    			if( mat.HasProperty("_ReflectionTex") )
    				mat.SetTexture( "_ReflectionTex", m_ReflectionTexture );
    		}
     
    		// Restore pixel light count
    		if( m_DisablePixelLights )
    			QualitySettings.pixelLightCount = oldPixelLightCount;
     
    		s_InsideRendering = false;
    	}
     
     
    	// Cleanup all the objects we possibly have created
    	void OnDisable()
    	{
    		if( m_ReflectionTexture ) {
    			DestroyImmediate( m_ReflectionTexture );
    			m_ReflectionTexture = null;
    		}
    		foreach( DictionaryEntry kvp in m_ReflectionCameras )
    			DestroyImmediate( ((Camera)kvp.Value).gameObject );
    		m_ReflectionCameras.Clear();
    	}
     
     
    	private void UpdateCameraModes( Camera src, Camera dest )
    	{
    		if( dest == null )
    			return;
    		// set camera to clear the same way as current camera
    		dest.clearFlags = src.clearFlags;
    		dest.backgroundColor = src.backgroundColor;        
    		if( src.clearFlags == CameraClearFlags.Skybox )
    		{
    			Skybox sky = src.GetComponent(typeof(Skybox)) as Skybox;
    			Skybox mysky = dest.GetComponent(typeof(Skybox)) as Skybox;
    			if( !sky || !sky.material )
    			{
    				mysky.enabled = false;
    			}
    			else
    			{
    				mysky.enabled = true;
    				mysky.material = sky.material;
    			}
    		}
    		// update other values to match current camera.
    		// even if we are supplying custom camera&projection matrices,
    		// some of values are used elsewhere (e.g. skybox uses far plane)
    		dest.farClipPlane = src.farClipPlane;
    		dest.nearClipPlane = src.nearClipPlane;
    		dest.orthographic = src.orthographic;
    		dest.fieldOfView = src.fieldOfView;
    		dest.aspect = src.aspect;
    		dest.orthographicSize = src.orthographicSize;
    	}
     
    	// On-demand create any objects we need
    	private void CreateMirrorObjects( Camera currentCamera, out Camera reflectionCamera )
    	{
    		reflectionCamera = null;
     
    		// Reflection render texture
    		if( !m_ReflectionTexture || m_OldReflectionTextureSize != m_TextureSize )
    		{
    			if( m_ReflectionTexture )
    				DestroyImmediate( m_ReflectionTexture );
    			m_ReflectionTexture = new RenderTexture( m_TextureSize, m_TextureSize, 16 );
    			m_ReflectionTexture.name = "__MirrorReflection" + GetInstanceID();
    			m_ReflectionTexture.isPowerOfTwo = true;
    			m_ReflectionTexture.hideFlags = HideFlags.DontSave;
    			m_OldReflectionTextureSize = m_TextureSize;
    		}
     
    		// Camera for reflection
    		reflectionCamera = m_ReflectionCameras[currentCamera] as Camera;
    		if( !reflectionCamera ) // catch both not-in-dictionary and in-dictionary-but-deleted-GO
    		{
    			GameObject go = new GameObject( "Mirror Refl Camera id" + GetInstanceID() + " for " + currentCamera.GetInstanceID(), typeof(Camera), typeof(Skybox) );
    			reflectionCamera = go.camera;
    			reflectionCamera.enabled = false;
    			reflectionCamera.transform.position = transform.position;
    			reflectionCamera.transform.rotation = transform.rotation;
    			reflectionCamera.gameObject.AddComponent("FlareLayer");
    			go.hideFlags = HideFlags.HideAndDontSave;
    			m_ReflectionCameras[currentCamera] = reflectionCamera;
    		}        
    	}
     
    	// Extended sign: returns -1, 0 or 1 based on sign of a
    	private static float sgn(float a)
    	{
    		if (a > 0.0f) return 1.0f;
    		if (a < 0.0f) return -1.0f;
    		return 0.0f;
    	}
     
    	// Given position/normal of the plane, calculates plane in camera space.
    	private Vector4 CameraSpacePlane (Camera cam, Vector3 pos, Vector3 normal, float sideSign)
    	{
    		Vector3 offsetPos = pos + normal * m_ClipPlaneOffset;
    		Matrix4x4 m = cam.worldToCameraMatrix;
    		Vector3 cpos = m.MultiplyPoint( offsetPos );
    		Vector3 cnormal = m.MultiplyVector( normal ).normalized * sideSign;
    		return new Vector4( cnormal.x, cnormal.y, cnormal.z, -Vector3.Dot(cpos,cnormal) );
    	}
     
    	// Calculates reflection matrix around the given plane
    	private static void CalculateReflectionMatrix (ref Matrix4x4 reflectionMat, Vector4 plane)
    	{
    		reflectionMat.m00 = (1F - 2F*plane[0]*plane[0]);
    		reflectionMat.m01 = (   - 2F*plane[0]*plane[1]);
    		reflectionMat.m02 = (   - 2F*plane[0]*plane[2]);
    		reflectionMat.m03 = (   - 2F*plane[3]*plane[0]);
     
    		reflectionMat.m10 = (   - 2F*plane[1]*plane[0]);
    		reflectionMat.m11 = (1F - 2F*plane[1]*plane[1]);
    		reflectionMat.m12 = (   - 2F*plane[1]*plane[2]);
    		reflectionMat.m13 = (   - 2F*plane[3]*plane[1]);
     
    		reflectionMat.m20 = (   - 2F*plane[2]*plane[0]);
    		reflectionMat.m21 = (   - 2F*plane[2]*plane[1]);
    		reflectionMat.m22 = (1F - 2F*plane[2]*plane[2]);
    		reflectionMat.m23 = (   - 2F*plane[3]*plane[2]);
     
    		reflectionMat.m30 = 0F;
    		reflectionMat.m31 = 0F;
    		reflectionMat.m32 = 0F;
    		reflectionMat.m33 = 1F;
    	}
    }
    

    下面说说该如何使用这两段代码:

    1、首先我们搭建一个简单的场景,这里我放了一些小方块和球之类的

    然后再创建一个平面,一会儿用来做镜子

    2、在Project中创建一个Shader

    然后重命名一下并打开,原来的代码可以全删掉,把上面的Mirror.Shader代码复制进去

     

    保存,回到Unity

    接下来新建一个脚本MirrorReflection,将上面MirrorReflection.cs中的脚本复制进去

    注意:因为版本问题,脚本可能会有一些错误,我用的版本是2018.2.14,

    这些错误多半是因为版本的更新导致的API改动,根据相应的修改提示,改用新的API,问题解决;

    以下时候我改过后的截图:

    3、在Project中新建一个材质Material,将这个材质的Shader设置成FX/MirrorReflection

    4、将MirrorMaterial材质、MirrorReflection脚本 挂在Mirror也就是刚刚用来做镜子的平面上

    完成以后的属性视图:

    5、回到场景视图:

    效果有了,就是镜子很模糊?

    别急,刚刚的脚本里给我们提供了一个分辨率的设置参数,我们把它调大一点就好了

    这里我设置了1024,这样一面清晰的镜子就完成了!

     

    展开全文
  • Unity Shader镜子

    2020-07-30 23:32:12
    Unity5.3.3实现的镜子效果
  • Unity&Shader案例篇-镜子2

    千次阅读 2016-11-15 17:03:04
    一、前言上一篇介绍了有关镜子的制作,有关理论部分的内容我会在后续相关的文章中陆续介绍,莫急,我先趁着自己脑子还是对此技术比较热,趁热打铁尽早把实现部分先写出来。上一章的介绍制作的镜子其实只是一个取巧的...

    一、前言

    上一篇介绍了有关镜子的制作,有关理论部分的内容我会在后续相关的文章中陆续介绍,莫急,我先趁着自己脑子还是对此技术比较热,趁热打铁尽早把实现部分先写出来。上一章的介绍制作的镜子其实只是一个取巧的方法,并不能做到实时的反射出实物,但是思路还是比较有意思的。

    
    

    还是废话不多说先上图,我用的Unity版本5.3.3

    二、Unity中制作原理

    1、简单说明:其实这个原理就是用一个摄像机去拍镜子上面的物体将得到的图像投影给Plane,最后主摄像机就能看到Plane上物体的镜像,所以关键的部分就是计算摄像机上的投影矩阵和主摄像机的投影矩阵的关系,因为站在不同的角度看(主摄像机转动或移动)镜像是要跟着偏移的

    2、创建一个Camera作为镜像摄像机,将下面计算摄像机的投影平面的脚本代码拖到这个Camera上

    using UnityEngine;
    using System.Collections;
    [ExecuteInEditMode]
    public class ViewPlane : MonoBehaviour {
        public GameObject mirrorPlane;                      //镜子屏幕
    
        public bool estimateViewFrustum = true;
        public bool setNearClipPlane = false;               //是否设置近剪切平面
    
        public float nearClipDistanceOffset = -0.01f;       //近剪切平面的距离
    
        private Camera mirrorCamera;                        //镜像摄像机
        // Use this for initialization
        void Start () {
            mirrorCamera = GetComponent<Camera>();
    	
    	}
    	
    	// Update is called once per frame
    	void Update () {
    
            if (null != mirrorPlane && null != mirrorCamera)
            {
                //世界坐标系的左下角
                Vector3 pa = mirrorPlane.transform.TransformPoint(new Vector3(-5.0f, 0.0f, -5.0f));
    
                //世界坐标系的右下角
                Vector3 pb = mirrorPlane.transform.TransformPoint(new Vector3(5.0f, 0.0f, -5.0f));
    
                //世界坐标系的左上角
                Vector3 pc = mirrorPlane.transform.TransformPoint(new Vector3(-5.0f, 0.0f, 5.0f));
    
                //镜像观察角度的世界坐标位置
                Vector3 pe = transform.position;
    
                //镜像摄像机的近剪切面的距离
                float n = mirrorCamera.nearClipPlane;
    
                //镜像摄像机的远剪切面的距离
                float f = mirrorCamera.farClipPlane;
    
                //从镜像摄像机到左下角
                Vector3 va = pa - pe;
    
                //从镜像摄像机到右下角
                Vector3 vb = pb - pe;
    
                //从镜像摄像机到左上角
                Vector3 vc = pc - pe;
    
                //屏幕的右侧旋转轴
                Vector3 vr = pb - pa;
    
                //屏幕的上侧旋转轴
                Vector3 vu = pc - pa;
    
                //屏幕的法线
                Vector3 vn;
    
                //到屏幕左边缘的距离
                float l;
    
                //到屏幕右边缘的距离
                float r;
    
                //到屏幕下边缘的距离
                float b;
    
                //到屏幕上边缘的距离
                float t;
    
                //从镜像摄像机到屏幕的距离
                float d;
    
                //如果看向镜子的背面
                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();
    
                //两个向量的叉乘,最后在取负,因为Unity是使用左手坐标系
                vn = -Vector3.Cross(vr, vu);
    
                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;
    
                //投影矩阵
                Matrix4x4 p = new Matrix4x4();
                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;
    
                //旋转矩阵
                Matrix4x4 rm = new Matrix4x4();
                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;
    
                Matrix4x4 tm = new Matrix4x4();
                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)
                {
                    //旋转摄像机
                    Quaternion q = new Quaternion();
                    q.SetLookRotation((0.5f * (pb + pc) - pe), vu);
                    //聚焦到屏幕的中心点
                    mirrorCamera.transform.rotation = q;
    
                    //保守估计fieldOfView的值
                    if (mirrorCamera.aspect >= 1.0)
                    {
                        mirrorCamera.fieldOfView = Mathf.Rad2Deg *
                           Mathf.Atan(((pb - pa).magnitude + (pc - pa).magnitude)
                           / va.magnitude);
                    }
                    else
                    {
                        //在摄像机角度考虑,保证视锥足够宽
                        mirrorCamera.fieldOfView =
                           Mathf.Rad2Deg / mirrorCamera.aspect *
                           Mathf.Atan(((pb - pa).magnitude + (pc - pa).magnitude)
                           / va.magnitude);
                    }
                }
            }
    	}
    }
    
    3、创建一个Plane作为镜子,这个Plane的Shader必须要是一个能接受贴图的,所以这里可以自行使用Unity自带的Shader,我选择了Unlit/Texture

    4、将下面的代码赋给2中创建的Camera,将主摄像机给MainCamrea变量,镜子Plane赋值给MirrorPlane变量,

    using UnityEngine;
    using System.Collections;
    [ExecuteInEditMode]
    public class Mirrors2 : MonoBehaviour {
    
        public GameObject mirrorPlane;//镜子
        public Camera mainCamera;//主摄像机
        private  Camera mirrorCamera;//镜像摄像机
    	// Use this for initialization
    	void Start () {
            mirrorCamera = GetComponent<Camera>();
    	
    	}
    	
    	// Update is called once per frame
    	void Update () {
    	
            if(null!=mirrorPlane&&null!=mirrorCamera&&null!=mainCamera)
            {
                //将主摄像机的世界坐标位置转换为镜子的局部坐标位置
                Vector3 postionInMirrorSpace = mirrorPlane.transform.InverseTransformPoint(mainCamera.transform.position);
                
                //一般y为镜面的法线方向
                postionInMirrorSpace.y = -postionInMirrorSpace.y;
    
                //转回到世界坐标系的位置
                mirrorCamera.transform.position = mirrorPlane.transform.TransformPoint(postionInMirrorSpace);
            }
    	}
    }
    
    5、如果是刚创建的Camera的投影梯形一定是非常规则的如图所示,勾选这个Camera

    上的ViewPlane脚本上的setNearClipPlane,这个时候其实是在设置这个Camera的近剪切屏幕,使得这个平面尽量与镜子Plane平面重合,如图所示,这个还不是完全的重合,可以通过调整参数nearClipDistanceOffset来调整,默认的-0.01其实就是我已经调

    整好了的参数,只要勾选setNearClipPlane就会自动调整到与镜子平面重合,如图所示


    6、最后创建一个RenderTexture,这个Texture就是用来将镜像摄像机投影出来的图像信息传递给镜子Plane的中间变量,所以将这个Texture分别托给Plane的Shader材质中的Texture和镜像摄像机中的TargetTexture。最后设置这个Texture的分辨率,我设置成了1024×1024,默认的是256×256,这样的会造成镜像模糊,如图所示


    设置成1024×1024之后就如第一章图所示,清晰度比较高。

    三、总结

    1、缺点:镜子不能有自己的贴图、不能实现多面镜子同时相互反射(还有待发现)

    2、相比上一篇的优点:可以实时的反射所有在镜面上的物体,可以改变物体的光照和贴图

    3、什么不是Shader,一句Shader代码都没有怎么还是Shader的案例,跟Shader有毛关系?是的,的确没有一句Shader的代码,然而使用C#代码控制其实都是Shader里面的参数,比如投影矩阵的计算,尤其是此处的投影区域有不规则倾斜的情况。

    后续待...

    原文转载请注明出处点击打开链接

    工程文件下载地址点击打开链接

    展开全文
  • 无需自己找镜子Shader,只需2个脚本即可在Unity中创建一个简单的模拟镜面反射效果。 使用教程链接:https://blog.csdn.net/ChinarCSDN/article/details/80862999
  • Unity 镜子效果

    2019-08-10 01:29:27
    本教程,无需自己找镜子Shader,只需2个脚本即可在Unity中创建一个简单的模拟镜面反射效果 1. 在场景中创建一个 Plane —— 用来作为镜子 2. 同时创建一个材质球 /Material —— 给到 Plane 上 3. 修改新创建的 ...

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

     

     


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

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

    用于转镜头,看不同方位

    需要挂载到主相机上,并将层次列表中的 Plane 拖到 Pivot 上

     

    4
    Create Cube —— 创建一个立方体

    为了看镜子的效果

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

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

     

    5
    Indistinct —— 显示效果不清晰

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

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

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

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

     

    转载于:https://www.cnblogs.com/guaishoudashu/p/10579241.html

    展开全文
  • Unity实现镜子效果

    千次阅读 2019-04-12 14:42:19
    详细步骤参考http://www.sikiedu.com/course/306/task/16510/show Mirror Shader 导入资源:链接:https://pan.baidu.com/s/1tBmAycMEC7oxP2EV3o0LzQ 密码:23n2
  • Unity镜子效果的实现(无需镜子Shader) Unity镜子效果制作教程 助力快速实现一个简单的镜面反射效果 本教程,无需自己找镜子Shader,只需2个脚本即可在Unity中创建一个简单的模拟镜面反射效果 本教程,无需自己找...
  • Unity 镜子反射特效

    千次阅读 2017-01-12 11:43:57
    最近项目中要用到镜子的反射特效,镜子反射由物理学知识可以知道,镜子中的景物是实际物体的对称表现。由此就衍生了俩种 思路,第一种是讲物体实实在在渲染到镜子之后,第二种是生成一个摄像机使其关于镜子对称,将...
  • 创建一个相机,放到和镜子相同的位置(根据具体效果微调),再新建一个renderTexture,链接到相机的TargetTexture,并赋给镜子的材质球当纹理贴图,如下就能把摄像机拍摄到的东西显示在镜子上。 三、实现镜面效果 ...
  • Unity3D中添加一面镜子

    热门讨论 2020-07-30 23:32:37
    Unity3D中实现镜面效果,从镜面中可以看到场景中物体的镜像。
  • UnityShader中级篇——镜子效果

    千次阅读 2018-03-01 15:29:13
    // Upgrade NOTE: replaced '_Object2World' with 'unity_ObjectToWorld' // Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)' Shader "Unity Shaders Book/Chapter 10/...
  • Unity3D Shader:镜子的效果

    千次阅读 2018-10-09 20:24:03
    Shader部分代码: Shader "Custom/FanShe" { Properties{ _MainTex("Albedo",2D) = "white"{} _MainTint("Diffuse Color",Color)=(1,1,1,1) ...Reflection Amount&
  • Unity3d 镜面材质

    2020-07-28 23:33:34
    Unity3d 镜面材质
  • Unity Shader 镜子2

    2020-07-30 23:30:23
    Unity 5.3.3实现的镜子2效果
1 2 3 4 5 ... 20
收藏数 660
精华内容 264
热门标签
关键字:

unity镜子