Unity3d 实时折射和反射
这里只是张贴在实时折射和脚本反思shader,
大约NGUI第一部分请下载。
这个版本的主要缺点是折射平面部Layer必须是water层。假设有专家谁可以摆脱这一个。请记得把代码回该条,谢谢!
Water.cs
using UnityEngine; using System.Collections; using System; using System.Collections.Generic; /// <summary> /// 水面 /// </summary> [AddComponentMenu("GameCore/Effect/Water/Water (Base)")] [ExecuteInEditMode] public class Water : MonoBehaviour { public enum FlageWaterRefType { Both = 0, Reflection = 1, Refraction = 2 } public bool DisablePixelLights = false; public LayerMask Layers = -1; public int TexSize = 512; public FlageWaterRefType RefType = FlageWaterRefType.Both; public float ReflectClipPlaneOffset = 0; public float RefractionAngle = 0; private static Camera _reflectionCamera; private static Camera _refractionCamera; private int _OldTexSize = 0; private RenderTexture _reflectionRenderTex; private RenderTexture _refractionRenderTex; private bool _insideRendering = false; private float _refType = (float)FlageWaterRefType.Both; void OnWillRenderObject() { if (!enabled || !renderer || !renderer.sharedMaterial || !renderer.enabled) return; Camera cam = Camera.current; if (!cam) return; Material[] materials = renderer.sharedMaterials; if (_insideRendering) return; _insideRendering = true; int oldPixelLightCount = QualitySettings.pixelLightCount; if (DisablePixelLights) QualitySettings.pixelLightCount = 0; if (RefType == FlageWaterRefType.Both || RefType == FlageWaterRefType.Reflection) { DrawReflectionRenderTexture(cam); foreach (Material mat in materials) { if (mat.HasProperty("_ReflectionTex")) mat.SetTexture("_ReflectionTex", _reflectionRenderTex); } } if (RefType == FlageWaterRefType.Both || RefType == FlageWaterRefType.Refraction) { this.gameObject.layer = 4; DrawRefractionRenderTexture(cam); foreach (Material mat in materials) { if (mat.HasProperty("_RefractionTex")) mat.SetTexture("_RefractionTex", _refractionRenderTex); } } _refType = (float)RefType; Matrix4x4 projmtx = CoreTool.UV_Tex2DProj2Tex2D(transform, cam); foreach (Material mat in materials) { mat.SetMatrix("_ProjMatrix", projmtx); mat.SetFloat("_RefType", _refType); } if (DisablePixelLights) QualitySettings.pixelLightCount = oldPixelLightCount; _insideRendering = false; } /// <summary> /// 绘制反射RenderTexture /// </summary> private void DrawReflectionRenderTexture(Camera cam) { Vector3 pos = transform.position; Vector3 normal = transform.up; CreateObjects(cam,ref _reflectionRenderTex, ref _reflectionCamera); CoreTool.CloneCameraModes(cam, _reflectionCamera); float d = -Vector3.Dot(normal, pos) - ReflectClipPlaneOffset; Vector4 reflectionPlane = new Vector4(normal.x, normal.y, normal.z, d); Matrix4x4 reflection = CoreTool.CalculateReflectionMatrix(Matrix4x4.zero, 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 = CoreTool.CameraSpacePlane(_reflectionCamera, pos, normal, 1.0f, ReflectClipPlaneOffset); Matrix4x4 projection = cam.projectionMatrix; projection = CoreTool.CalculateObliqueMatrix(projection, clipPlane, -1); _reflectionCamera.projectionMatrix = projection; _reflectionCamera.cullingMask = ~(1 << 4) & Layers.value; // never render water layer _reflectionCamera.targetTexture = _reflectionRenderTex; 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); } /// <summary> /// 绘制折射RenderTexture /// </summary> private void DrawRefractionRenderTexture(Camera cam) { CreateObjects(cam, ref _refractionRenderTex, ref _refractionCamera); CoreTool.CloneCameraModes(cam, _refractionCamera); Vector3 pos = transform.position; Vector3 normal = transform.up; Matrix4x4 projection = cam.worldToCameraMatrix; projection *= Matrix4x4.Scale(new Vector3(1,Mathf.Clamp(1-RefractionAngle,0.001f,1),1)); _refractionCamera.worldToCameraMatrix = projection; Vector4 clipPlane = CoreTool.CameraSpacePlane(_refractionCamera, pos, normal, 1.0f, 0); projection = cam.projectionMatrix; projection[2] = clipPlane.x + projection[3];//x projection[6] = clipPlane.y + projection[7];//y projection[10] = clipPlane.z + projection[11];//z projection[14] = clipPlane.w + projection[15];//w _refractionCamera.projectionMatrix = projection; _refractionCamera.cullingMask = ~(1 << 4) & Layers.value; // never render water layer _refractionCamera.targetTexture = _refractionRenderTex; _refractionCamera.transform.position = cam.transform.position; _refractionCamera.transform.eulerAngles = cam.transform.eulerAngles; _refractionCamera.Render(); } void OnDisable() { if (_reflectionRenderTex) { DestroyImmediate(_reflectionRenderTex); _reflectionRenderTex = null; } if (_reflectionCamera) { DestroyImmediate(_reflectionCamera.gameObject); _reflectionCamera = null; } if (_refractionRenderTex) { DestroyImmediate(_refractionRenderTex); _refractionRenderTex = null; } if (_refractionCamera) { DestroyImmediate(_refractionCamera.gameObject); _refractionCamera = null; } } void CreateObjects(Camera srcCam, ref RenderTexture renderTex, ref Camera destCam) { // Reflection render texture if (!renderTex || _OldTexSize != TexSize) { if (renderTex) DestroyImmediate(renderTex); renderTex = new RenderTexture(TexSize, TexSize, 0); renderTex.name = "__RefRenderTexture" + renderTex.GetInstanceID(); renderTex.isPowerOfTwo = true; renderTex.hideFlags = HideFlags.DontSave; renderTex.antiAliasing = 4; renderTex.anisoLevel = 0; _OldTexSize = TexSize; } if (!destCam) // catch both not-in-dictionary and in-dictionary-but-deleted-GO { GameObject go = new GameObject("__RefCamera for " + srcCam.GetInstanceID(), typeof(Camera), typeof(Skybox)); destCam = go.camera; destCam.enabled = false; destCam.transform.position = transform.position; destCam.transform.rotation = transform.rotation; destCam.gameObject.AddComponent("FlareLayer"); go.hideFlags = HideFlags.HideAndDontSave; } } }
WaterEditor.cs
using UnityEngine; using System.Collections; using System; using UnityEditor; [CustomEditor(typeof(Water))] public class WaterEditor : Editor { GUIContent[] _renderTextureOptions = new GUIContent[8] {new GUIContent("16"), new GUIContent("32"), new GUIContent("64"), new GUIContent("128"), new GUIContent("256"), new GUIContent("512"), new GUIContent("1024"), new GUIContent("2048") }; int[] _renderTextureSize = new int[8] { 16, 32, 64, 128, 256, 512, 1024, 2048 }; public override void OnInspectorGUI() { Water water = target as Water; EditorGUILayout.PropertyField(this.serializedObject.FindProperty("RefType"), new GUIContent("RefType")); EditorGUILayout.PropertyField(this.serializedObject.FindProperty("DisablePixelLights"), new GUIContent("DisablePixelLights")); EditorGUILayout.PropertyField(this.serializedObject.FindProperty("Layers"), new GUIContent("Layers")); EditorGUILayout.IntPopup(this.serializedObject.FindProperty("TexSize"), _renderTextureOptions, _renderTextureSize, new GUIContent("TexSize")); if (NGUIEditorTools.DrawHeader("Reflect Settings")) { NGUIEditorTools.BeginContents(); { EditorGUILayout.Slider(this.serializedObject.FindProperty("ReflectClipPlaneOffset"),0,0.1f,new GUIContent("ClipPlane Offset")); } NGUIEditorTools.EndContents(); } if (NGUIEditorTools.DrawHeader("Refraction Settings")) { NGUIEditorTools.BeginContents(); { EditorGUILayout.Slider(this.serializedObject.FindProperty("RefractionAngle"),0,1, new GUIContent("Refraction Angle")); } NGUIEditorTools.EndContents(); } this.serializedObject.ApplyModifiedProperties(); } }
CoreTool.cs
using System.Collections; using System; using UnityEngine; /// <summary> /// 工具类 /// </summary> public static class CoreTool { #region Config配置 /// <summary> /// 验证当前文件是否为配置文件 /// </summary> /// <param name="filePath">文件路径</param> /// <returns></returns> public static bool IsConfig(string filePath) { return true; } #endregion #region Camera /// <summary> /// 将源摄像机状态克隆到目标相机 /// </summary> /// <param name="src">源相机</param> /// <param name="dest">目标相机</param> public static void CloneCameraModes(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.depth = src.depth; dest.farClipPlane = src.farClipPlane; dest.nearClipPlane = src.nearClipPlane; dest.orthographic = src.orthographic; dest.fieldOfView = src.fieldOfView; dest.aspect = src.aspect; dest.orthographicSize = src.orthographicSize; } /// <summary> /// 计算反射矩阵 /// </summary> /// <param name="reflectionMat">原始矩阵</param> /// <param name="plane">反射平面</param> /// <returns>反射矩阵</returns> public static Matrix4x4 CalculateReflectionMatrix(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; return reflectionMat; } /// <summary> /// 计算指定平面在摄像机中的空间位置 /// </summary> /// <param name="cam">摄像机</param> /// <param name="pos">平面上的点</param> /// <param name="normal">平面法线</param> /// <param name="sideSign">1:平面正面。-1:平面反面</param> /// <param name="clipPlaneOffset">平面法线位置偏移量</param> /// <returns></returns> public static Vector4 CameraSpacePlane(Camera cam, Vector3 pos, Vector3 normal, float sideSign,float clipPlaneOffset) { Vector3 offsetPos = pos + normal * 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)); } /// <summary> /// 由剪裁面计算投影倾斜矩阵 /// </summary> /// <param name="projection">投影矩阵</param> /// <param name="clipPlane">剪裁面</param> /// <param name="sideSign">剪裁平面(-1:平面以下,1:平面上面)</param> public static Matrix4x4 CalculateObliqueMatrix(Matrix4x4 projection, Vector4 clipPlane,float sideSign) { 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))); // third row = clip plane - fourth row projection[2] = c.x + Mathf.Sign(sideSign)*projection[3]; projection[6] = c.y + Mathf.Sign(sideSign) * projection[7]; projection[10] = c.z + Mathf.Sign(sideSign) * projection[11]; projection[14] = c.w + Mathf.Sign(sideSign) * projection[15]; return projection; } private static float sgn(float a) { if (a > 0.0f) return 1.0f; if (a < 0.0f) return -1.0f; return 0.0f; } /// <summary> /// 由水平、垂直距离改动倾斜矩阵 /// </summary> /// <param name="projMatrix">倾斜矩阵</param> /// <param name="horizObl">水平方向</param> /// <param name="vertObl">垂直方向</param> /// <returns>改动后的倾斜矩阵</returns> public static Matrix4x4 CalculateObliqueMatrix(Matrix4x4 projMatrix, float horizObl, float vertObl) { Matrix4x4 mat = projMatrix; mat[0, 2] = horizObl; mat[1, 2] = vertObl; return mat; } #endregion #region Shader Matrix4x4 /// <summary> /// tex2DProj到tex2D的uv纹理转换矩阵 /// 在shader中, /// vert=>o.posProj = mul(_ProjMatrix, v.vertex); /// frag=>tex2D(_RefractionTex,float2(i.posProj) / i.posProj.w) /// </summary> /// <param name="transform">要显示纹理的对象</param> /// <param name="cam">当前观察的摄像机</param> /// <returns>返回转换矩阵</returns> public static Matrix4x4 UV_Tex2DProj2Tex2D(Transform transform,Camera cam) { Matrix4x4 scaleOffset = Matrix4x4.TRS( new Vector3(0.5f, 0.5f, 0.5f), Quaternion.identity, new Vector3(0.5f, 0.5f, 0.5f)); Vector3 scale = transform.lossyScale; Matrix4x4 _ProjMatrix = transform.localToWorldMatrix * Matrix4x4.Scale(new Vector3(1.0f / scale.x, 1.0f / scale.y, 1.0f / scale.z)); _ProjMatrix = scaleOffset * cam.projectionMatrix * cam.worldToCameraMatrix * _ProjMatrix; return _ProjMatrix; } #endregion }
Shader
Shader "GameCore/Mobile/Water/Diffuse" { Properties { _ReflectionTex ("Reflection", 2D) = "white" {} _RefractionTex ("Refraction", 2D) = "white" {} _RefColor("Color",Color) = (1,1,1,1) } SubShader { Tags { "RenderType"="Opaque"} LOD 100 Pass { CGPROGRAM #pragma vertex vert #pragma fragment frag #include "UnityCG.cginc" uniform float4x4 _ProjMatrix; uniform float _RefType; sampler2D _ReflectionTex; sampler2D _RefractionTex; float4 _RefColor; struct outvertex { float4 pos : SV_POSITION; float4 uv0 : TEXCOORD0; float4 refparam : COLOR0;//r:fresnel,g:none,b:none,a:none }; outvertex vert(appdata_tan v) { outvertex o; o.pos = mul (UNITY_MATRIX_MVP,v.vertex); float4 posProj = mul(_ProjMatrix, v.vertex); o.uv0 = posProj; float3 r =normalize(ObjSpaceViewDir(v.vertex)); float d = saturate(dot(r,normalize(v.normal)));//r+(1-r)*pow(d,5) o.refparam =float4(d,0,0,0); return o; } float4 frag(outvertex i) : COLOR { half4 flecol = tex2D(_ReflectionTex,float2(i.uv0) / i.uv0.w); half4 fracol = tex2D(_RefractionTex,float2(i.uv0) / i.uv0.w); half4 outcolor = half4(1,1,1,1); if(_RefType == 0) { outcolor = lerp(flecol,fracol,i.refparam.r); } else if(_RefType == 1) { outcolor = flecol; } else if(_RefType == 2) { outcolor = fracol; } return outcolor*_RefColor; } ENDCG } } }
版权声明:本文博客原创文章,博客,未经同意,不得转载。