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
    		}
    	}
    }



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

  • 相关阅读:
    下载及爬取网页内容
    对于for循环的理解
    记录安装fiddle出现的问题
    Django
    12种可以参考的思路关于代码能干什么
    “字符文本中字符太多”错误及解决方法
    jQuery参考:jquery中的$(document).ready()与window.onload的区别
    页面定时刷新功能实现
    HTML:关于位置的几个概念
    Lambda表达式
  • 原文地址:https://www.cnblogs.com/blfshiye/p/4750579.html
Copyright © 2011-2022 走看看