镜面反射

C#代码

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

[ExecuteInEditMode]
public class Mirror : MonoBehaviour {

    public bool m_bDisablePixelLights = true;
    public int m_nTextureSize = 256;                    // 渲染的图片尺寸
    public float m_fClipPlaneOffset = 0.07f;            // 剪切面偏移
    public bool m_bIsFlatMirror = true;                // 是否是平的镜面

    public LayerMask m_suReflectLayers = -1;            // 反射层遮罩
    private Hashtable m_pReflectionCameras = new Hashtable();            // 反射的摄像机集合

    private RenderTexture m_pReflectionTexture = null;                    // 反射贴图
    private int m_nOldReflectionTextureSize = 0;                            // 旧的反射贴图的尺寸 用来控制只生成一次贴图

    private static bool s_bInsideRendering = false;                        // 是否正在渲染


    private Renderer m_pRenderer;

    void Awake()
    {
        m_pRenderer = GetComponent<Renderer> ();
    }

    /// <summary>
    /// 渲染当前物体前执行
    /// </summary>
    public void OnWillRenderObject()
    {
        if(!enabled || !m_pRenderer || !m_pRenderer.sharedMaterial || !m_pRenderer.enabled) return;

        Camera cam = Camera.current;            // 可能会有多个摄像机渲染当前物体 并为每个摄像机创建一个反射的摄像机
        if (!cam)
            return;

        if (s_bInsideRendering)
            return;
        s_bInsideRendering = true;

        Camera reflectionCamera;                // 用来反射的摄像机
        CreateMirrorObjects(cam, out reflectionCamera);

        Vector3 pos = transform.position;
        Vector3 normal;
        if (m_bIsFlatMirror) {
            normal = transform.up;
        } else {
            normal = transform.position - cam.transform.position;
            normal.Normalize ();
        }

        int oldPixelLightCount = QualitySettings.pixelLightCount;                            // 像素灯的数量
        // 不使用像素灯
        if (m_bDisablePixelLights) {
            QualitySettings.pixelLightCount = 0;
        }

        UpdateCameraModes (cam, reflectionCamera);

        float d = -Vector3.Dot (normal, pos) - m_fClipPlaneOffset;        // ?
        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;        // 计算反射摄像机的世界到摄像机坐标系的矩阵

        Vector4 clipPlane = CameraSpacePlane (reflectionCamera, pos, normal, 1.0f);
        Matrix4x4 projection = cam.projectionMatrix;            // 摄像机的投影矩阵
        CalculateObliqueMatrix(ref projection, clipPlane);
        reflectionCamera.projectionMatrix = projection;                                        // 计算反射摄像机的投影矩阵

        reflectionCamera.cullingMask = ~(1 << 4) & m_suReflectLayers.value;
        reflectionCamera.targetTexture = m_pReflectionTexture;
        GL.invertCulling = true;                                                            // 选择是否反转背面剔除
        reflectionCamera.transform.position = newpos;
        Vector3 euler = cam.transform.eulerAngles;
        reflectionCamera.transform.eulerAngles = new Vector3 (0, euler.y, euler.z);            // 反射摄像机的旋转是当前摄像机的绕y轴及z轴旋转
        reflectionCamera.Render ();                            // 把当前摄像机的内容渲染到 targetTexture
        reflectionCamera.transform.position = oldpos;                                        // 反射摄像机的位置仍然是当前摄像机的位置
        GL.invertCulling = false;
        Material[] materials = m_pRenderer.sharedMaterials;                                    // 物体所有的共享材质
        foreach (Material mat in materials) {
            if (mat.HasProperty ("_Ref"))
                mat.SetTexture ("_Ref", m_pReflectionTexture);
        }
        if (m_bDisablePixelLights)
            QualitySettings.pixelLightCount = oldPixelLightCount;
        s_bInsideRendering = false;
    }

    void OnDisable()
    {
        if (m_pReflectionTexture) {
            DestroyImmediate (m_pReflectionTexture);
            m_pReflectionTexture = null;
        }
        foreach (DictionaryEntry kvp in m_pReflectionCameras) {
            DestroyImmediate (((Camera)kvp.Value).gameObject);
        }
        m_pReflectionCameras.Clear ();
    }

    private static float sgn(float a)
    {
        if (a > 0.0f) return 1.0f;
        if (a < 0.0f) return -1.0f;
        return 0.0f;
    }

    /// <summary>
    /// Cameras the space plane.
    /// </summary>
    /// <returns>The space plane.</returns>
    /// <param name="cam">Cam.</param>
    /// <param name="pos">Position.</param>
    /// <param name="normal">Normal.</param>
    /// <param name="sideSign">Side sign.</param>
    private Vector4 CameraSpacePlane (Camera cam, Vector3 pos, Vector3 normal, float sideSign)
    {
        Vector3 offsetPos = pos + normal * m_fClipPlaneOffset;
        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) );  // ?
    }

    /// <summary>
    /// 计算倾斜的矩阵
    /// </summary>
    /// <param name="projection">Projection.</param>
    /// <param name="clipPlane">Clip plane.</param>
    private static void CalculateObliqueMatrix (ref Matrix4x4 projection, Vector4 clipPlane)
    {
        Vector4 q = projection.inverse * new Vector4(
            sgn(clipPlane.x),
            sgn(clipPlane.y),
            1.0f,
            1.0f
        );
        Vector4 c = clipPlane * (2.0F / (Vector4.Dot (clipPlane, q)));

        projection[2] = c.x - projection[3];
        projection[6] = c.y - projection[7];
        projection[10] = c.z - projection[11];
        projection[14] = c.w - projection[15];
    }

    /// <summary>
    /// 创建镜像物体
    /// </summary>
    /// <param name="currentCamera">Current camera.</param>
    /// <param name="reflectionCamera">Reflection camera.</param>
    private void CreateMirrorObjects(Camera currentCamera, out Camera reflectionCamera)
    {
        reflectionCamera = null;

        // 反射贴图的相关设置
        if (!m_pReflectionTexture || m_nOldReflectionTextureSize != m_nTextureSize) {
            if (m_pReflectionTexture)
                DestroyImmediate (m_pReflectionTexture);

            m_pReflectionTexture = new RenderTexture (m_nTextureSize, m_nTextureSize, 16);            // 0 16 24
            m_pReflectionTexture.name = "__MirrorReflection" + GetInstanceID();
            m_pReflectionTexture.isPowerOfTwo = true;                                            // 设置图片的尺寸为2的幂
            m_pReflectionTexture.hideFlags = HideFlags.DontSave;
            m_nOldReflectionTextureSize = m_nTextureSize;
        }

        // 先从容器中获取摄像机,如果不是当前摄像机 则创建一个并添加到容器中
        reflectionCamera = m_pReflectionCameras [currentCamera] as Camera;
        if (!reflectionCamera) {
            GameObject go = new GameObject ("Mirror Refl Camera id" + GetInstanceID () + " for " + currentCamera.GetInstanceID (), typeof(Camera), typeof(Skybox));
            reflectionCamera = go.GetComponent<Camera> ();
            reflectionCamera.enabled = false;
            go.transform.position = transform.position;
            go.transform.rotation = transform.rotation;
            go.hideFlags = HideFlags.HideAndDontSave;
            m_pReflectionCameras [currentCamera] = reflectionCamera;
        }
    }

    /// <summary>
    /// 同步摄像机设置
    /// </summary>
    /// <param name="src">Source.</param>
    /// <param name="dest">Destination.</param>
    private void UpdateCameraModes(Camera src, Camera dest)
    {
        if (dest == null) return;

        dest.clearFlags = src.clearFlags;
        dest.backgroundColor = src.backgroundColor;
        if (src.clearFlags == CameraClearFlags.Skybox) {
            Skybox sky = src.GetComponent<Skybox> ();
            Skybox mySky = dest.GetComponent<Skybox> ();
            if (!sky || !sky.material) {
                mySky.enabled = false;
            } else {
                mySky.enabled = true;
                mySky.material = sky.material;
            }
        }

        dest.farClipPlane = src.farClipPlane;
        dest.nearClipPlane = src.nearClipPlane;
        dest.orthographic = src.orthographic;
        dest.fieldOfView = src.fieldOfView;
        dest.aspect = src.aspect;
        dest.orthographicSize = src.orthographicSize;
        dest.renderingPath = src.renderingPath;
    }

    /// <summary>
    /// 计算反射矩阵
    /// </summary>
    /// <param name="reflectionMat">Reflection mat.</param>
    /// <param name="plane">Plane.</param>
    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;
    }
}

Shader代码

Shader "Mirrors/Bumped Specular" {
Properties {
    _Color ("Main Color", Color) = (1,1,1,1)
    _MainTex ("Base (RGB) Gloss (A)", 2D) = "white" {}
    _BlendLevel("Main Material Blend Level",Range(0,1))=1
    _SpecColor ("Specular Color", Color) = (0.5, 0.5, 0.5, 1)
    _Shininess ("Shininess", Range (0.03, 1)) = 0.078125
    _BumpMap ("Normalmap", 2D) = "bump" {}
    _Bumpness ("Bump Rate",Range(0,1))= 0.5
    _Ref ("For Mirror reflection,don't set it!", 2D) = "white" {}
    _RefColor("Reflection Color",Color) = (1,1,1,1)
    _RefRate ("Reflective Rate", Range (0, 1)) = 1
    _Distortion ("Reflective Distortion", Range (0, 1)) = 0

}
SubShader { 
    Tags { "RenderType"="Opaque" }
    LOD 400

CGPROGRAM
#pragma surface surf BlinnPhong
#pragma target 3.0
#pragma debug

sampler2D _MainTex;
sampler2D _BumpMap;
fixed4 _Color;
half _Shininess;
half _RefRate;
half _Bumpness;
half _BlendLevel;
half _Distortion;
fixed4 _RefColor;
sampler2D _Ref;

struct Input {
    float2 uv_MainTex;
    float2 uv_BumpMap;
    float2 uv_Ref ;
    float4 screenPos;
};


void surf (Input IN, inout SurfaceOutput o) {
    fixed3 nor = UnpackNormal (tex2D(_BumpMap, IN.uv_BumpMap));
    fixed4 tex = tex2D(_MainTex, IN.uv_MainTex);
    float2 screenUV = IN.screenPos.xy / IN.screenPos.w;
    screenUV += nor.xy * _Distortion;
    fixed4 ref = tex2D(_Ref, screenUV);
    o.Albedo = tex.rgb * _Color.rgb * _BlendLevel;
    o.Emission = ref.rgb * _RefColor.rgb * _RefRate;
    o.Normal = nor.rgb * _Bumpness;
    o.Gloss = tex.a;
    o.Alpha = tex.a * _Color.a;
    o.Specular = _Shininess;    
}
ENDCG
}

FallBack "Specular"
}

Shader "Mirrors/Transparent Bumped Specular Flat" {
Properties {
    _Transparency("Transparency", Range (0, 1)) = 1
    _Distortion ("Distortion", range (0,1)) = 0
    _Color ("Main Color", Color) = (1,1,1,1)
    _MainTex ("Base (RGB) TransGloss (A)", 2D) = "white" {}
    _BlendLevel("Main Material Blend Level",Range(0,1))=1
    _SpecColor ("Specular Color", Color) = (0.5, 0.5, 0.5, 0)
    _Shininess ("Shininess", Range (0.01, 1)) = 0.078125
    _BumpMap ("Normalmap", 2D) = "bump" {}
    _Bumpness ("Bump Rate",Range(0,1))= 1
    _Ref ("For Mirror reflection,don't set it!", 2D) = "white" {}
}

SubShader {
    Tags { "Queue"="Transparent" "RenderType"="Opaque" }

    GrabPass {                            
            Name "BASE"
            Tags { "LightMode" = "Always" }
         }

CGPROGRAM
#pragma surface surf BlinnPhong
#pragma target 3.0
#pragma debug

sampler2D _MainTex;
sampler2D _BumpMap;
sampler2D _Ref;
fixed4 _Color;
half _BlendLevel;
half _Transparency;
half _Bumpness;
half _Shininess;
sampler2D _GrabTexture;
float4 _GrabTexture_TexelSize;
half _Distortion;



struct Input {
    float2 uv_MainTex;
    float2 uv_BumpMap;
    float4 screenPos;
};


void surf (Input IN, inout SurfaceOutput o) {
    fixed3 nor = UnpackNormal (tex2D(_BumpMap, IN.uv_BumpMap));
    fixed4 tex = tex2D(_MainTex, IN.uv_MainTex);

    float2 screenUV = IN.screenPos.xy / IN.screenPos.w;
    screenUV += nor.xy * _Distortion ;
    fixed4 ref = tex2D(_Ref, screenUV);

    float4 screenUV2 = IN.screenPos;
    #if UNITY_UV_STARTS_AT_TOP
    float scale = -1.0;
    #else
    float scale = 1.0;
    #endif
    screenUV2.y = (screenUV2.y - screenUV2.w*0.5)* scale+ screenUV2.w * 0.5;
    screenUV2.xy = screenUV2.xy / screenUV2.w;
    screenUV2.xy += nor.xy * _Distortion;

    fixed4 trans = tex2D(_GrabTexture,screenUV2.xy);

    o.Albedo = tex.rgb * _Color.rgb * _BlendLevel;
    o.Emission = lerp(ref.rgb,trans.rgb,_Transparency);
    o.Normal = nor.rgb * _Bumpness;
    o.Gloss = tex.a;
    o.Alpha = tex.a * _Color.a;
    o.Specular = _Shininess;    
}
ENDCG
}

FallBack "Transparent/VertexLit"
}

Shader "Mirrors/Transparent Specular Sphere" {
Properties {
    _Transparency("Transparency", Range (0, 1)) = 1
    _Distortion ("Distortion", range (0,30)) = 10
    _Color ("Main Color", Color) = (1,1,1,1)
    _MainTex ("Base (RGB) TransGloss (A)", 2D) = "white" {}
    _BlendLevel("Main Material Blend Level",Range(0,1))=1
    _SpecColor ("Specular Color", Color) = (0.5, 0.5, 0.5, 0)
    _Shininess ("Shininess", Range (0.01, 1)) = 0.078125
    _Ref ("For Mirror reflection,don't set it!", 2D) = "white" {}
}

SubShader {
    Tags { "Queue"="Transparent" "RenderType"="Opaque" }

    GrabPass {                            
            Name "BASE"
            Tags { "LightMode" = "Always" }
         }

CGPROGRAM
#pragma surface surf BlinnPhong
#pragma target 3.0
#pragma debug

sampler2D _MainTex;
sampler2D _Ref;
fixed4 _Color;
half _BlendLevel;
half _Transparency;
half _Shininess;
sampler2D _GrabTexture;
float4 _GrabTexture_TexelSize;
half _Distortion;



struct Input {
    float2 uv_MainTex;
    float4 screenPos;
};


void surf (Input IN, inout SurfaceOutput o) {
    fixed4 tex = tex2D(_MainTex, IN.uv_MainTex);

    #if UNITY_UV_STARTS_AT_TOP
    float scale = -1.0;
    #else
    float scale = 1.0;
    #endif

    float4 screenUV = IN.screenPos;
    float2 offset = (_Distortion * o.Normal) ;
    screenUV.xy  += offset ;
    float3 ref = tex2Dproj( _Ref, screenUV);

    float4 screenUV2 = IN.screenPos;
    screenUV2.y = (screenUV2.y - screenUV2.w*0.5)* scale+ screenUV2.w * 0.5;
    offset = _Distortion * o.Normal;
    screenUV2.xy  += offset  ;
    float3 trans = tex2Dproj( _GrabTexture, screenUV2);

    o.Albedo = tex.rgb * _Color.rgb * _BlendLevel;
    o.Emission = lerp(ref.rgb,trans.rgb,_Transparency);
    o.Gloss = tex.a;
    o.Alpha = tex.a * _Color.a;
    o.Specular = _Shininess;    
}
ENDCG
}

FallBack "Transparent/VertexLit"
}