Unity Shader案例篇-镜子(一)
发表于2018-06-12
本篇文章和大家介绍下使用shader实现镜子效果,先上效果图:
其中使用的Unity版本是在5.3.3。
一、原理
1、首先要准备的素材是三个,对没错,就是三个因为镜子里面的那个物体其实是实物的复制体而已;一个Plane作为镜子,还有一个实物和虚物体。
2、新建一个材质使用下面的Shader代码,并将此材质球赋给那个虚物体
Shader "Custom/Cg shader for virtual objects in mirrors" { Properties{ _Color("Virtual Object's Color", Color) = (1, 1, 1, 1) } SubShader{ Tags{ "Queue" = "Transparent+20" } Pass{ Blend OneMinusDstAlpha DstAlpha CGPROGRAM #pragma vertex vert #pragma fragment frag #include "UnityCG.cginc" uniform float4 _Color; uniform float4x4 _WorldToMirror; struct vertexInput { float4 vertex : POSITION; }; struct vertexOutput { float4 pos : SV_POSITION; float4 posInMirror : TEXCOORD0; }; vertexOutput vert(vertexInput input) { vertexOutput output; output.posInMirror = mul(_WorldToMirror, mul(_Object2World, input.vertex)); output.pos = mul(UNITY_MATRIX_MVP, input.vertex); return output; } float4 frag(vertexOutput input) : COLOR { //如果镜子里的物体出来了就剔除掉 if (input.posInMirror.y > 0.0) { discard; } return float4(_Color.rgb, 0.0); } ENDCG } } }
3、另外在建一个材质使用下面的Shader代码,并将此材质球赋值给实物体
Shader "Unlit/Cg shader for Real objects" { Properties{ _Color("Virtual Object's Color", Color) = (1, 1, 1, 1) } SubShader { Pass { CGPROGRAM #pragma vertex vert #pragma fragment frag uniform float4 _Color; float4 vert(float4 vertexPos : POSITION) : SV_POSITION { return mul(UNITY_MATRIX_MVP, vertexPos); } float4 frag(void) : COLOR { return float4(_Color.rgb, 1.0); } ENDCG } } }
4、建立一个镜子的材质并将下面的Shader代码赋给作为镜子的那个面板
Shader "Unlit/Mirrors" { Properties{ _Color("Mirrors's Color", Color) = (1, 1, 1, 1) } SubShader{ Tags{ "Queue" = "Transparent+10" } // draw after all other geometry has been drawn // because we mess with the depth buffer //确保在所有的真实物体渲染之后再渲染 // 1st pass: mark mirror with alpha = 0 Pass{ CGPROGRAM #pragma vertex vert #pragma fragment frag float4 vert(float4 vertexPos : POSITION) : SV_POSITION { return mul(UNITY_MATRIX_MVP, vertexPos); } float4 frag(void) : COLOR { return float4(1.0, 0.0, 0.0, 0.0); // this color should never be visible, // only alpha is important } ENDCG } // 2nd pass: set depth to far plane such that // we can use the normal depth test for the reflected geometry Pass{ ZTest Always Blend OneMinusDstAlpha DstAlpha //==float4 result = float4(1.0 - pixel_color.a) * fragment_output + float4(pixel_color.a) * pixel_color; CGPROGRAM #pragma vertex vert #pragma fragment frag uniform float4 _Color; // user-specified background color in the mirror float4 vert(float4 vertexPos : POSITION) : SV_POSITION { float4 pos = mul(UNITY_MATRIX_MVP, vertexPos); pos.z = pos.w; // the perspective division will divide pos.z // by pos.w; thus, the depth is 1.0, // which represents the far clipping plane return pos; } float4 frag(void) : COLOR { return float4(_Color.rgb, 0.0); // set alpha to 0.0 and // the color to the user-specified background color } ENDCG } } }
5、最后新建一个C#脚本,代码如下,将此代码赋给虚物体,并将Plane和虚物体拖动赋值给里面的对应两个变量
using UnityEngine; using System.Collections; [ExecuteInEditMode] public class PlacingTheVirtualObj : MonoBehaviour { public GameObject objectInFrontOfMirror; public GameObject mirrorPlane; // Use this for initialization void Start () { } // Update is called once per frame void Update() { if (null != mirrorPlane) { //这句话决定了镜子里的物体是否可见 GetComponent<Renderer>().sharedMaterial.SetMatrix("_WorldToMirror",mirrorPlane.GetComponent<Renderer>().worldToLocalMatrix); if (null != objectInFrontOfMirror) { //将实物的颜色值赋给镜中的物体 Color realColor = objectInFrontOfMirror.GetComponent<Renderer>().material.GetColor("_Color"); GetComponent<Renderer>().material.SetColor("_Color", realColor); transform.position = objectInFrontOfMirror.transform.position; transform.rotation = objectInFrontOfMirror.transform.rotation; transform.localScale = -objectInFrontOfMirror.transform.localScale; //new Vector3(0.0f, 1.0f, 0.0f)为表面的法线方向 transform.RotateAround(objectInFrontOfMirror.transform.position,mirrorPlane.transform.TransformDirection( new Vector3(0.0f, 1.0f, 0.0f)), 180.0f); Vector3 positionInMirrorSpace = mirrorPlane.transform.InverseTransformPoint(objectInFrontOfMirror.transform.position); positionInMirrorSpace.y = -positionInMirrorSpace.y; transform.position = mirrorPlane.transform.TransformPoint( positionInMirrorSpace); } } } }