Unity-水面效果的C#文件

21 篇文章 0 订阅
using UnityEngine;
using System.Collections;

[ExecuteInEditMode] // Make water live-update even when not in play mode
public class Water : MonoBehaviour
{
public enum WaterMode {
   Simple = 0,
   Reflective = 1,
   Refractive = 2,
};
public WaterMode m_WaterMode = WaterMode.Refractive;
public bool m_DisablePixelLights = true;
public int m_TextureSize = 256;
public float m_ClipPlaneOffset = 0.07f;
public LayerMask m_ReflectLayers = -1;
public LayerMask m_RefractLayers = -1;
  
private Hashtable m_ReflectionCameras = new Hashtable(); // Camera -> Camera table
private Hashtable m_RefractionCameras = new Hashtable(); // Camera -> Camera table
private RenderTexture m_ReflectionTexture = null;
private RenderTexture m_RefractionTexture = null;
private WaterMode m_HardwareWaterSupport = WaterMode.Refractive;
private int m_OldReflectionTextureSize = 0;
private int m_OldRefractionTextureSize = 0;
private static bool s_InsideWater = false;
// This is called when it's known that the object will be rendered by some
// camera. We render reflections / refractions and do other updates here.
// Because the script executes in edit mode, reflections for the scene view
// camera will just work!
public void OnWillRenderObject()
{
   if( !enabled || !renderer || !renderer.sharedMaterial || !renderer.enabled )
    return;
   
   Camera cam = Camera.current;
   if( !cam )
    return;
   // Safeguard from recursive water reflections.  
   if( s_InsideWater )
    return;
   s_InsideWater = true;
  
   // Actual water rendering mode depends on both the current setting AND
   // the hardware support. There's no point in rendering refraction textures
   // if they won't be visible in the end.
   m_HardwareWaterSupport = FindHardwareWaterSupport();
   WaterMode mode = GetWaterMode();
  
   Camera reflectionCamera, refractionCamera;
   CreateWaterObjects( cam, out reflectionCamera, out refractionCamera );
  
   // find out the reflection plane: position and normal in world space
   Vector3 pos = transform.position;
   Vector3 normal = transform.up;
  
   // Optionally disable pixel lights for reflection/refraction
   int oldPixelLightCount = QualitySettings.pixelLightCount;
   if( m_DisablePixelLights )
    QualitySettings.pixelLightCount = 0;
  
   UpdateCameraModes( cam, reflectionCamera );
   UpdateCameraModes( cam, refractionCamera );
  
   // Render reflection if needed
   if( mode >= WaterMode.Reflective )
   {
    // Reflect camera around reflection plane
    float d = -Vector3.Dot (normal, pos) - m_ClipPlaneOffset;
    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;
  
    // Setup oblique projection matrix so that near plane is our reflection
    // plane. This way we clip everything below/above it for free.
    Vector4 clipPlane = CameraSpacePlane( reflectionCamera, pos, normal, 1.0f );
    Matrix4x4 projection = cam.projectionMatrix;
    CalculateObliqueMatrix (ref projection, clipPlane);
    reflectionCamera.projectionMatrix = projection;
   
    reflectionCamera.cullingMask = ~(1<<4) & m_ReflectLayers.value; // never render water layer
    reflectionCamera.targetTexture = m_ReflectionTexture;
    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);
    renderer.sharedMaterial.SetTexture( "_ReflectionTex", m_ReflectionTexture );
   }
  
   // Render refraction
   if( mode >= WaterMode.Refractive )
   {
    refractionCamera.worldToCameraMatrix = cam.worldToCameraMatrix;
  
    // Setup oblique projection matrix so that near plane is our reflection
    // plane. This way we clip everything below/above it for free.
    Vector4 clipPlane = CameraSpacePlane( refractionCamera, pos, normal, -1.0f );
    Matrix4x4 projection = cam.projectionMatrix;
    CalculateObliqueMatrix (ref projection, clipPlane);
    refractionCamera.projectionMatrix = projection;
   
    refractionCamera.cullingMask = ~(1<<4) & m_RefractLayers.value; // never render water layer
    refractionCamera.targetTexture = m_RefractionTexture;
    refractionCamera.transform.position = cam.transform.position;
    refractionCamera.transform.rotation = cam.transform.rotation;
    refractionCamera.Render();
    renderer.sharedMaterial.SetTexture( "_RefractionTex", m_RefractionTexture );
   }
  
   // Restore pixel light count
   if( m_DisablePixelLights )
    QualitySettings.pixelLightCount = oldPixelLightCount;
  
   // Setup shader keywords based on water mode
   switch( mode )
   {
   case WaterMode.Simple:
    Shader.EnableKeyword( "WATER_SIMPLE" );
    Shader.DisableKeyword( "WATER_REFLECTIVE" );
    Shader.DisableKeyword( "WATER_REFRACTIVE" );
    break;
   case WaterMode.Reflective:
    Shader.DisableKeyword( "WATER_SIMPLE" );
    Shader.EnableKeyword( "WATER_REFLECTIVE" );
    Shader.DisableKeyword( "WATER_REFRACTIVE" );
    break;
   case WaterMode.Refractive:
    Shader.DisableKeyword( "WATER_SIMPLE" );
    Shader.DisableKeyword( "WATER_REFLECTIVE" );
    Shader.EnableKeyword( "WATER_REFRACTIVE" );
    break;
   }
   
   s_InsideWater = false;
}

// Cleanup all the objects we possibly have created
void OnDisable()
{
   if( m_ReflectionTexture ) {
    DestroyImmediate( m_ReflectionTexture );
    m_ReflectionTexture = null;
   }
   if( m_RefractionTexture ) {
    DestroyImmediate( m_RefractionTexture );
    m_RefractionTexture = null;
   }
   foreach( DictionaryEntry kvp in m_ReflectionCameras )
        DestroyImmediate( ((Camera)kvp.Value).gameObject );
        m_ReflectionCameras.Clear();
   foreach( DictionaryEntry kvp in m_RefractionCameras )
        DestroyImmediate( ((Camera)kvp.Value).gameObject );
        m_RefractionCameras.Clear();
}

// This just sets up some matrices in the material; for really
// old cards to make water texture scroll.
void Update()
{
   if( !renderer )
    return;
   Material mat = renderer.sharedMaterial;
   if( !mat )
    return;
   
   Vector4 waveSpeed = mat.GetVector( "WaveSpeed" );
   float waveScale = mat.GetFloat( "_WaveScale" );
   Vector4 waveScale4 = new Vector4(waveScale, waveScale, waveScale * 0.4f, waveScale * 0.45f);
  
   // Time since level load, and do intermediate calculations with doubles
   double t = Time.timeSinceLevelLoad / 20.0;
   Vector4 offsetClamped = new Vector4(
    (float)System.Math.IEEERemainder(waveSpeed.x * waveScale4.x * t, 1.0),
    (float)System.Math.IEEERemainder(waveSpeed.y * waveScale4.y * t, 1.0),
    (float)System.Math.IEEERemainder(waveSpeed.z * waveScale4.z * t, 1.0),
    (float)System.Math.IEEERemainder(waveSpeed.w * waveScale4.w * t, 1.0)
   );
  
   mat.SetVector( "_WaveOffset", offsetClamped );
   mat.SetVector( "_WaveScale", waveScale4 );
   
   Vector3 waterSize = renderer.bounds.size;  
   Vector3 scale = new Vector3( waterSize.x*waveScale4.x, waterSize.z*waveScale4.y, 1 );
   Matrix4x4 scrollMatrix = Matrix4x4.TRS( new Vector3(offsetClamped.x,offsetClamped.y,0), Quaternion.identity, scale );
   mat.SetMatrix( "_WaveMatrix", scrollMatrix );
    
   scale = new Vector3( waterSize.x*waveScale4.z, waterSize.z*waveScale4.w, 1 );
   scrollMatrix = Matrix4x4.TRS( new Vector3(offsetClamped.z,offsetClamped.w,0), Quaternion.identity, scale );
   mat.SetMatrix( "_WaveMatrix2", scrollMatrix );
}
private void UpdateCameraModes( Camera src, Camera dest )
{
   if( dest == null )
    return;
   // set water 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.farClipPlane = src.farClipPlane;
   dest.nearClipPlane = src.nearClipPlane;
   dest.orthographic = src.orthographic;
   dest.fieldOfView = src.fieldOfView;
   dest.aspect = src.aspect;
   dest.orthographicSize = src.orthographicSize;
}
// On-demand create any objects we need for water
private void CreateWaterObjects( Camera currentCamera, out Camera reflectionCamera, out Camera refractionCamera )
{
   WaterMode mode = GetWaterMode();
  
   reflectionCamera = null;
   refractionCamera = null;
  
   if( mode >= WaterMode.Reflective )
   {
    // Reflection render texture
    if( !m_ReflectionTexture || m_OldReflectionTextureSize != m_TextureSize )
    {
     if( m_ReflectionTexture )
      DestroyImmediate( m_ReflectionTexture );
     m_ReflectionTexture = new RenderTexture( m_TextureSize, m_TextureSize, 16 );
     m_ReflectionTexture.name = "__WaterReflection" + GetInstanceID();
     m_ReflectionTexture.isPowerOfTwo = true;
     m_ReflectionTexture.hideFlags = HideFlags.DontSave;
     m_OldReflectionTextureSize = m_TextureSize;
    }
   
    // Camera for reflection
    reflectionCamera = m_ReflectionCameras[currentCamera] as Camera;
    if( !reflectionCamera ) // catch both not-in-dictionary and in-dictionary-but-deleted-GO
    {
     GameObject go = new GameObject( "Water Refl Camera id" + GetInstanceID() + " for " + currentCamera.GetInstanceID(), typeof(Camera), typeof(Skybox) );
     reflectionCamera = go.camera;
     reflectionCamera.enabled = false;
     reflectionCamera.transform.position = transform.position;
     reflectionCamera.transform.rotation = transform.rotation;
     reflectionCamera.gameObject.AddComponent("FlareLayer");
     go.hideFlags = HideFlags.HideAndDontSave;
     m_ReflectionCameras[currentCamera] = reflectionCamera;
    }
   }
  
   if( mode >= WaterMode.Refractive )
   {
    // Refraction render texture
    if( !m_RefractionTexture || m_OldRefractionTextureSize != m_TextureSize )
    {
     if( m_RefractionTexture )
      DestroyImmediate( m_RefractionTexture );
     m_RefractionTexture = new RenderTexture( m_TextureSize, m_TextureSize, 16 );
     m_RefractionTexture.name = "__WaterRefraction" + GetInstanceID();
     m_RefractionTexture.isPowerOfTwo = true;
     m_RefractionTexture.hideFlags = HideFlags.DontSave;
     m_OldRefractionTextureSize = m_TextureSize;
    }
   
    // Camera for refraction
    refractionCamera = m_RefractionCameras[currentCamera] as Camera;
    if( !refractionCamera ) // catch both not-in-dictionary and in-dictionary-but-deleted-GO
    {
     GameObject go = new GameObject( "Water Refr Camera id" + GetInstanceID() + " for " + currentCamera.GetInstanceID(), typeof(Camera), typeof(Skybox) );
     refractionCamera = go.camera;
     refractionCamera.enabled = false;
     refractionCamera.transform.position = transform.position;
     refractionCamera.transform.rotation = transform.rotation;
     refractionCamera.gameObject.AddComponent("FlareLayer");
     go.hideFlags = HideFlags.HideAndDontSave;
     m_RefractionCameras[currentCamera] = refractionCamera;
    }
   }
}
private WaterMode GetWaterMode()
{
   if( m_HardwareWaterSupport < m_WaterMode )
    return m_HardwareWaterSupport;
   else
    return m_WaterMode;
}
private WaterMode FindHardwareWaterSupport()
{
   if( !SystemInfo.supportsRenderTextures || !renderer )
    return WaterMode.Simple;
   
   Material mat = renderer.sharedMaterial;
   if( !mat )
    return WaterMode.Simple;
   
   string mode = mat.GetTag("WATERMODE", false);
   if( mode == "Refractive" )
    return WaterMode.Refractive;
   if( mode == "Reflective" )
    return WaterMode.Reflective;
   
   return WaterMode.Simple;
}
// Extended sign: returns -1, 0 or 1 based on sign of a
private static float sgn(float a)
{
        if (a > 0.0f) return 1.0f;
        if (a < 0.0f) return -1.0f;
        return 0.0f;
}
// Given position/normal of the plane, calculates plane in camera space.
private Vector4 CameraSpacePlane (Camera cam, Vector3 pos, Vector3 normal, float sideSign)
{
   Vector3 offsetPos = pos + normal * m_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) );
}
// Adjusts the given projection matrix so that near plane is the given clipPlane
// clipPlane is given in camera space. See article in Game Programming Gems 5 and
// http://aras-p.info/texts/obliqueortho.html
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)));
   // third row = clip plane - fourth row
   projection[2] = c.x - projection[3];
   projection[6] = c.y - projection[7];
   projection[10] = c.z - projection[11];
   projection[14] = c.w - projection[15];
}
// Calculates reflection matrix around the given plane
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;
}
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值