Cg Programming/Unity/Water Reflection and Refraction
Tools
General
Sister projects
In other projects
This page or section is an undeveloped draft or outline. You can help todevelop the work, or you can ask for assistance in theproject room. |
usingUnityEngine;usingSystem.Collections;usingSystem;usingSystem.Collections.Generic;/// <summary>/// 水面/// </summary>[AddComponentMenu("GameCore/Effect/Water/Water (Base)")][ExecuteInEditMode]publicclassWater:MonoBehaviour{publicenumFlageWaterRefType{Both=0,Reflection=1,Refraction=2}publicboolDisablePixelLights=false;publicLayerMaskLayers=-1;publicintTexSize=512;publicFlageWaterRefTypeRefType=FlageWaterRefType.Both;publicfloatReflectClipPlaneOffset=0;publicfloatRefractionAngle=0;privatestaticCamera_reflectionCamera;privatestaticCamera_refractionCamera;privateint_OldTexSize=0;privateRenderTexture_reflectionRenderTex;privateRenderTexture_refractionRenderTex;privatebool_insideRendering=false;privatefloat_refType=(float)FlageWaterRefType.Both;voidOnWillRenderObject(){if(!enabled||!renderer||!renderer.sharedMaterial||!renderer.enabled)return;Cameracam=Camera.current;if(!cam)return;Material[]materials=renderer.sharedMaterials;if(_insideRendering)return;_insideRendering=true;intoldPixelLightCount=QualitySettings.pixelLightCount;if(DisablePixelLights)QualitySettings.pixelLightCount=0;if(RefType==FlageWaterRefType.Both||RefType==FlageWaterRefType.Reflection){DrawReflectionRenderTexture(cam);foreach(Materialmatinmaterials){if(mat.HasProperty("_ReflectionTex"))mat.SetTexture("_ReflectionTex",_reflectionRenderTex);}}if(RefType==FlageWaterRefType.Both||RefType==FlageWaterRefType.Refraction){this.gameObject.layer=4;DrawRefractionRenderTexture(cam);foreach(Materialmatinmaterials){if(mat.HasProperty("_RefractionTex"))mat.SetTexture("_RefractionTex",_refractionRenderTex);}}_refType=(float)RefType;Matrix4x4projmtx=CoreTool.UV_Tex2DProj2Tex2D(transform,cam);foreach(Materialmatinmaterials){mat.SetMatrix("_ProjMatrix",projmtx);mat.SetFloat("_RefType",_refType);}if(DisablePixelLights)QualitySettings.pixelLightCount=oldPixelLightCount;_insideRendering=false;}/// <summary>/// 绘制反射RenderTexture/// </summary>privatevoidDrawReflectionRenderTexture(Cameracam){Vector3pos=transform.position;Vector3normal=transform.up;CreateObjects(cam,ref_reflectionRenderTex,ref_reflectionCamera);CoreTool.CloneCameraModes(cam,_reflectionCamera);floatd=-Vector3.Dot(normal,pos)-ReflectClipPlaneOffset;Vector4reflectionPlane=newVector4(normal.x,normal.y,normal.z,d);Matrix4x4reflection=CoreTool.CalculateReflectionMatrix(Matrix4x4.zero,reflectionPlane);Vector3oldpos=cam.transform.position;Vector3newpos=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.Vector4clipPlane=CoreTool.CameraSpacePlane(_reflectionCamera,pos,normal,1.0f,ReflectClipPlaneOffset);Matrix4x4projection=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;Vector3euler=cam.transform.eulerAngles;_reflectionCamera.transform.eulerAngles=newVector3(0,euler.y,euler.z);_reflectionCamera.Render();_reflectionCamera.transform.position=oldpos;GL.SetRevertBackfacing(false);}/// <summary>/// 绘制折射RenderTexture/// </summary>privatevoidDrawRefractionRenderTexture(Cameracam){CreateObjects(cam,ref_refractionRenderTex,ref_refractionCamera);CoreTool.CloneCameraModes(cam,_refractionCamera);Vector3pos=transform.position;Vector3normal=transform.up;Matrix4x4projection=cam.worldToCameraMatrix;projection*=Matrix4x4.Scale(newVector3(1,Mathf.Clamp(1-RefractionAngle,0.001f,1),1));_refractionCamera.worldToCameraMatrix=projection;Vector4clipPlane=CoreTool.CameraSpacePlane(_refractionCamera,pos,normal,1.0f,0);projection=cam.projectionMatrix;projection[2]=clipPlane.x+projection[3];//xprojection[6]=clipPlane.y+projection[7];//yprojection[10]=clipPlane.z+projection[11];//zprojection[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();}voidOnDisable(){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;}}voidCreateObjects(CamerasrcCam,refRenderTexturerenderTex,refCameradestCam){// Reflection render textureif(!renderTex||_OldTexSize!=TexSize){if(renderTex)DestroyImmediate(renderTex);renderTex=newRenderTexture(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{GameObjectgo=newGameObject("__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;}}}
usingUnityEngine;usingSystem.Collections;usingSystem;usingUnityEditor;[CustomEditor(typeof(Water))]publicclassWaterEditor:Editor{GUIContent[]_renderTextureOptions=newGUIContent[8]{newGUIContent("16"),newGUIContent("32"),newGUIContent("64"),newGUIContent("128"),newGUIContent("256"),newGUIContent("512"),newGUIContent("1024"),newGUIContent("2048")};int[]_renderTextureSize=newint[8]{16,32,64,128,256,512,1024,2048};publicoverridevoidOnInspectorGUI(){Waterwater=targetasWater;EditorGUILayout.PropertyField(this.serializedObject.FindProperty("RefType"),newGUIContent("RefType"));EditorGUILayout.PropertyField(this.serializedObject.FindProperty("DisablePixelLights"),newGUIContent("DisablePixelLights"));EditorGUILayout.PropertyField(this.serializedObject.FindProperty("Layers"),newGUIContent("Layers"));EditorGUILayout.IntPopup(this.serializedObject.FindProperty("TexSize"),_renderTextureOptions,_renderTextureSize,newGUIContent("TexSize"));if(NGUIEditorTools.DrawHeader("Reflect Settings")){NGUIEditorTools.BeginContents();{EditorGUILayout.Slider(this.serializedObject.FindProperty("ReflectClipPlaneOffset"),0,0.1f,newGUIContent("ClipPlane Offset"));}NGUIEditorTools.EndContents();}if(NGUIEditorTools.DrawHeader("Refraction Settings")){NGUIEditorTools.BeginContents();{EditorGUILayout.Slider(this.serializedObject.FindProperty("RefractionAngle"),0,1,newGUIContent("Refraction Angle"));}NGUIEditorTools.EndContents();}this.serializedObject.ApplyModifiedProperties();}}
usingSystem.Collections;usingSystem;usingUnityEngine;/// <summary>/// 工具类/// </summary>publicstaticclassCoreTool{#region Config配置/// <summary>/// 验证当前文件是否为配置文件/// </summary>/// <param name="filePath">文件路径</param>/// <returns></returns>publicstaticboolIsConfig(stringfilePath){returntrue;}#endregion#region Camera/// <summary>/// 将源摄像机状态克隆到目标相机/// </summary>/// <param name="src">源相机</param>/// <param name="dest">目标相机</param>publicstaticvoidCloneCameraModes(Camerasrc,Cameradest){if(dest==null)return;// set camera to clear the same way as current cameradest.clearFlags=src.clearFlags;dest.backgroundColor=src.backgroundColor;if(src.clearFlags==CameraClearFlags.Skybox){Skyboxsky=src.GetComponent(typeof(Skybox))asSkybox;Skyboxmysky=dest.GetComponent(typeof(Skybox))asSkybox;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>publicstaticMatrix4x4CalculateReflectionMatrix(Matrix4x4reflectionMat,Vector4plane){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;returnreflectionMat;}/// <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>publicstaticVector4CameraSpacePlane(Cameracam,Vector3pos,Vector3normal,floatsideSign,floatclipPlaneOffset){Vector3offsetPos=pos+normal*clipPlaneOffset;Matrix4x4m=cam.worldToCameraMatrix;Vector3cpos=m.MultiplyPoint(offsetPos);Vector3cnormal=m.MultiplyVector(normal).normalized*sideSign;returnnewVector4(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>publicstaticMatrix4x4CalculateObliqueMatrix(Matrix4x4projection,Vector4clipPlane,floatsideSign){Vector4q=projection.inverse*newVector4(sgn(clipPlane.x),sgn(clipPlane.y),1.0f,1.0f);Vector4c=clipPlane*(2.0F/(Vector4.Dot(clipPlane,q)));// third row = clip plane - fourth rowprojection[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];returnprojection;}privatestaticfloatsgn(floata){if(a>0.0f)return1.0f;if(a<0.0f)return-1.0f;return0.0f;}/// <summary>/// 由水平、垂直距离修改倾斜矩阵/// </summary>/// <param name="projMatrix">倾斜矩阵</param>/// <param name="horizObl">水平方向</param>/// <param name="vertObl">垂直方向</param>/// <returns>修改后的倾斜矩阵</returns>publicstaticMatrix4x4CalculateObliqueMatrix(Matrix4x4projMatrix,floathorizObl,floatvertObl){Matrix4x4mat=projMatrix;mat[0,2]=horizObl;mat[1,2]=vertObl;returnmat;}#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>publicstaticMatrix4x4UV_Tex2DProj2Tex2D(Transformtransform,Cameracam){Matrix4x4scaleOffset=Matrix4x4.TRS(newVector3(0.5f,0.5f,0.5f),Quaternion.identity,newVector3(0.5f,0.5f,0.5f));Vector3scale=transform.lossyScale;Matrix4x4_ProjMatrix=transform.localToWorldMatrix*Matrix4x4.Scale(newVector3(1.0f/scale.x,1.0f/scale.y,1.0f/scale.z));_ProjMatrix=scaleOffset*cam.projectionMatrix*cam.worldToCameraMatrix*_ProjMatrix;return_ProjMatrix;}#endregion}
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"}LOD100Pass{CGPROGRAM#pragma vertex vert#pragma fragment frag#include"UnityCG.cginc"uniformfloat4x4_ProjMatrix;uniformfloat_RefType;sampler2D_ReflectionTex;sampler2D_RefractionTex;float4_RefColor;structoutvertex{float4pos:SV_POSITION;float4uv0:TEXCOORD0;float4refparam:COLOR0;//r:fresnel,g:none,b:none,a:none};outvertexvert(appdata_tanv){outvertexo;o.pos=mul(UNITY_MATRIX_MVP,v.vertex);float4posProj=mul(_ProjMatrix,v.vertex);o.uv0=posProj;float3r=normalize(ObjSpaceViewDir(v.vertex));floatd=saturate(dot(r,normalize(v.normal)));//r+(1-r)*pow(d,5)o.refparam=float4(d,0,0,0);returno;}float4frag(outvertexi):COLOR{half4flecol=tex2D(_ReflectionTex,float2(i.uv0)/i.uv0.w);half4fracol=tex2D(_RefractionTex,float2(i.uv0)/i.uv0.w);half4outcolor=half4(1,1,1,1);if(_RefType==0){outcolor=lerp(flecol,fracol,i.refparam.r);}elseif(_RefType==1){outcolor=flecol;}elseif(_RefType==2){outcolor=fracol;}returnoutcolor*_RefColor;}ENDCG}}}