zoukankan      html  css  js  c++  java
  • 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
    		}
    	}
    }



    版权声明:本文博客原创文章,博客,未经同意,不得转载。

  • 相关阅读:
    UVA 1386 Cellular Automaton
    ZOJ 3331 Process the Tasks
    CodeForces 650B Image Preview
    CodeForces 650A Watchmen
    CodeForces 651B Beautiful Paintings
    CodeForces 651A Joysticks
    HUST 1601 Shepherd
    HUST 1602 Substring
    HUST 1600 Lucky Numbers
    POJ 3991 Seinfeld
  • 原文地址:https://www.cnblogs.com/blfshiye/p/4750579.html
Copyright © 2011-2022 走看看