| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403 |
- using System;
- using System.Collections.Generic;
- using UnityEngine;
- namespace UnityStandardAssets.Water
- {
- [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 waterMode = WaterMode.Refractive;
- public bool disablePixelLights = true;
- public int textureSize = 256;
- public float clipPlaneOffset = 0.07f;
- public LayerMask reflectLayers = -1;
- public LayerMask refractLayers = -1;
- private Dictionary<Camera, Camera> m_ReflectionCameras = new Dictionary<Camera, Camera>(); // Camera -> Camera table
- private Dictionary<Camera, Camera> m_RefractionCameras = new Dictionary<Camera, Camera>(); // Camera -> Camera table
- private RenderTexture m_ReflectionTexture;
- private RenderTexture m_RefractionTexture;
- private WaterMode m_HardwareWaterSupport = WaterMode.Refractive;
- private int m_OldReflectionTextureSize;
- private int m_OldRefractionTextureSize;
- private static bool s_InsideWater;
- // 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 || !GetComponent<Renderer>() || !GetComponent<Renderer>().sharedMaterial ||
- !GetComponent<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 (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) - 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);
- reflectionCamera.projectionMatrix = cam.CalculateObliqueMatrix(clipPlane);
- // Set custom culling matrix from the current camera
- reflectionCamera.cullingMatrix = cam.projectionMatrix * cam.worldToCameraMatrix;
- reflectionCamera.cullingMask = ~(1 << 4) & reflectLayers.value; // never render water layer
- reflectionCamera.targetTexture = m_ReflectionTexture;
- bool oldCulling = GL.invertCulling;
- GL.invertCulling = !oldCulling;
- reflectionCamera.transform.position = newpos;
- Vector3 euler = cam.transform.eulerAngles;
- reflectionCamera.transform.eulerAngles = new Vector3(-euler.x, euler.y, euler.z);
- reflectionCamera.Render();
- reflectionCamera.transform.position = oldpos;
- GL.invertCulling = oldCulling;
- GetComponent<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);
- refractionCamera.projectionMatrix = cam.CalculateObliqueMatrix(clipPlane);
- // Set custom culling matrix from the current camera
- refractionCamera.cullingMatrix = cam.projectionMatrix * cam.worldToCameraMatrix;
- refractionCamera.cullingMask = ~(1 << 4) & refractLayers.value; // never render water layer
- refractionCamera.targetTexture = m_RefractionTexture;
- refractionCamera.transform.position = cam.transform.position;
- refractionCamera.transform.rotation = cam.transform.rotation;
- refractionCamera.Render();
- GetComponent<Renderer>().sharedMaterial.SetTexture("_RefractionTex", m_RefractionTexture);
- }
- // Restore pixel light count
- if (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 (var kvp in m_ReflectionCameras)
- {
- DestroyImmediate((kvp.Value).gameObject);
- }
- m_ReflectionCameras.Clear();
- foreach (var kvp in m_RefractionCameras)
- {
- DestroyImmediate((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 (!GetComponent<Renderer>())
- {
- return;
- }
- Material mat = GetComponent<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)Math.IEEERemainder(waveSpeed.x * waveScale4.x * t, 1.0),
- (float)Math.IEEERemainder(waveSpeed.y * waveScale4.y * t, 1.0),
- (float)Math.IEEERemainder(waveSpeed.z * waveScale4.z * t, 1.0),
- (float)Math.IEEERemainder(waveSpeed.w * waveScale4.w * t, 1.0)
- );
- mat.SetVector("_WaveOffset", offsetClamped);
- mat.SetVector("_WaveScale4", waveScale4);
- }
- 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<Skybox>();
- Skybox mysky = dest.GetComponent<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
- 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 != textureSize)
- {
- if (m_ReflectionTexture)
- {
- DestroyImmediate(m_ReflectionTexture);
- }
- m_ReflectionTexture = new RenderTexture(textureSize, textureSize, 16);
- m_ReflectionTexture.name = "__WaterReflection" + GetInstanceID();
- m_ReflectionTexture.isPowerOfTwo = true;
- m_ReflectionTexture.hideFlags = HideFlags.DontSave;
- m_OldReflectionTextureSize = textureSize;
- }
- // Camera for reflection
- m_ReflectionCameras.TryGetValue(currentCamera, out reflectionCamera);
- 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.GetComponent<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 != textureSize)
- {
- if (m_RefractionTexture)
- {
- DestroyImmediate(m_RefractionTexture);
- }
- m_RefractionTexture = new RenderTexture(textureSize, textureSize, 16);
- m_RefractionTexture.name = "__WaterRefraction" + GetInstanceID();
- m_RefractionTexture.isPowerOfTwo = true;
- m_RefractionTexture.hideFlags = HideFlags.DontSave;
- m_OldRefractionTextureSize = textureSize;
- }
- // Camera for refraction
- m_RefractionCameras.TryGetValue(currentCamera, out refractionCamera);
- 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.GetComponent<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;
- }
- }
- }
- WaterMode GetWaterMode()
- {
- if (m_HardwareWaterSupport < waterMode)
- {
- return m_HardwareWaterSupport;
- }
- return waterMode;
- }
- WaterMode FindHardwareWaterSupport()
- {
- if (!GetComponent<Renderer>())
- {
- return WaterMode.Simple;
- }
- Material mat = GetComponent<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;
- }
- // Given position/normal of the plane, calculates plane in camera space.
- Vector4 CameraSpacePlane(Camera cam, Vector3 pos, Vector3 normal, float sideSign)
- {
- 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));
- }
- // Calculates reflection matrix around the given plane
- 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;
- }
- }
- }
|