镜面反射
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> ();
}
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);
reflectionCamera.Render ();
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;
}
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) );
}
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];
}
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);
m_pReflectionTexture.name = "__MirrorReflection" + GetInstanceID();
m_pReflectionTexture.isPowerOfTwo = true;
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;
}
}
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;
}
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"
}