Water.cs 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403
  1. using System;
  2. using System.Collections.Generic;
  3. using UnityEngine;
  4. namespace UnityStandardAssets.Water
  5. {
  6. [ExecuteInEditMode] // Make water live-update even when not in play mode
  7. public class Water : MonoBehaviour
  8. {
  9. public enum WaterMode
  10. {
  11. Simple = 0,
  12. Reflective = 1,
  13. Refractive = 2,
  14. };
  15. public WaterMode waterMode = WaterMode.Refractive;
  16. public bool disablePixelLights = true;
  17. public int textureSize = 256;
  18. public float clipPlaneOffset = 0.07f;
  19. public LayerMask reflectLayers = -1;
  20. public LayerMask refractLayers = -1;
  21. private Dictionary<Camera, Camera> m_ReflectionCameras = new Dictionary<Camera, Camera>(); // Camera -> Camera table
  22. private Dictionary<Camera, Camera> m_RefractionCameras = new Dictionary<Camera, Camera>(); // Camera -> Camera table
  23. private RenderTexture m_ReflectionTexture;
  24. private RenderTexture m_RefractionTexture;
  25. private WaterMode m_HardwareWaterSupport = WaterMode.Refractive;
  26. private int m_OldReflectionTextureSize;
  27. private int m_OldRefractionTextureSize;
  28. private static bool s_InsideWater;
  29. // This is called when it's known that the object will be rendered by some
  30. // camera. We render reflections / refractions and do other updates here.
  31. // Because the script executes in edit mode, reflections for the scene view
  32. // camera will just work!
  33. public void OnWillRenderObject()
  34. {
  35. if (!enabled || !GetComponent<Renderer>() || !GetComponent<Renderer>().sharedMaterial ||
  36. !GetComponent<Renderer>().enabled)
  37. {
  38. return;
  39. }
  40. Camera cam = Camera.current;
  41. if (!cam)
  42. {
  43. return;
  44. }
  45. // Safeguard from recursive water reflections.
  46. if (s_InsideWater)
  47. {
  48. return;
  49. }
  50. s_InsideWater = true;
  51. // Actual water rendering mode depends on both the current setting AND
  52. // the hardware support. There's no point in rendering refraction textures
  53. // if they won't be visible in the end.
  54. m_HardwareWaterSupport = FindHardwareWaterSupport();
  55. WaterMode mode = GetWaterMode();
  56. Camera reflectionCamera, refractionCamera;
  57. CreateWaterObjects(cam, out reflectionCamera, out refractionCamera);
  58. // find out the reflection plane: position and normal in world space
  59. Vector3 pos = transform.position;
  60. Vector3 normal = transform.up;
  61. // Optionally disable pixel lights for reflection/refraction
  62. int oldPixelLightCount = QualitySettings.pixelLightCount;
  63. if (disablePixelLights)
  64. {
  65. QualitySettings.pixelLightCount = 0;
  66. }
  67. UpdateCameraModes(cam, reflectionCamera);
  68. UpdateCameraModes(cam, refractionCamera);
  69. // Render reflection if needed
  70. if (mode >= WaterMode.Reflective)
  71. {
  72. // Reflect camera around reflection plane
  73. float d = -Vector3.Dot(normal, pos) - clipPlaneOffset;
  74. Vector4 reflectionPlane = new Vector4(normal.x, normal.y, normal.z, d);
  75. Matrix4x4 reflection = Matrix4x4.zero;
  76. CalculateReflectionMatrix(ref reflection, reflectionPlane);
  77. Vector3 oldpos = cam.transform.position;
  78. Vector3 newpos = reflection.MultiplyPoint(oldpos);
  79. reflectionCamera.worldToCameraMatrix = cam.worldToCameraMatrix * reflection;
  80. // Setup oblique projection matrix so that near plane is our reflection
  81. // plane. This way we clip everything below/above it for free.
  82. Vector4 clipPlane = CameraSpacePlane(reflectionCamera, pos, normal, 1.0f);
  83. reflectionCamera.projectionMatrix = cam.CalculateObliqueMatrix(clipPlane);
  84. // Set custom culling matrix from the current camera
  85. reflectionCamera.cullingMatrix = cam.projectionMatrix * cam.worldToCameraMatrix;
  86. reflectionCamera.cullingMask = ~(1 << 4) & reflectLayers.value; // never render water layer
  87. reflectionCamera.targetTexture = m_ReflectionTexture;
  88. bool oldCulling = GL.invertCulling;
  89. GL.invertCulling = !oldCulling;
  90. reflectionCamera.transform.position = newpos;
  91. Vector3 euler = cam.transform.eulerAngles;
  92. reflectionCamera.transform.eulerAngles = new Vector3(-euler.x, euler.y, euler.z);
  93. reflectionCamera.Render();
  94. reflectionCamera.transform.position = oldpos;
  95. GL.invertCulling = oldCulling;
  96. GetComponent<Renderer>().sharedMaterial.SetTexture("_ReflectionTex", m_ReflectionTexture);
  97. }
  98. // Render refraction
  99. if (mode >= WaterMode.Refractive)
  100. {
  101. refractionCamera.worldToCameraMatrix = cam.worldToCameraMatrix;
  102. // Setup oblique projection matrix so that near plane is our reflection
  103. // plane. This way we clip everything below/above it for free.
  104. Vector4 clipPlane = CameraSpacePlane(refractionCamera, pos, normal, -1.0f);
  105. refractionCamera.projectionMatrix = cam.CalculateObliqueMatrix(clipPlane);
  106. // Set custom culling matrix from the current camera
  107. refractionCamera.cullingMatrix = cam.projectionMatrix * cam.worldToCameraMatrix;
  108. refractionCamera.cullingMask = ~(1 << 4) & refractLayers.value; // never render water layer
  109. refractionCamera.targetTexture = m_RefractionTexture;
  110. refractionCamera.transform.position = cam.transform.position;
  111. refractionCamera.transform.rotation = cam.transform.rotation;
  112. refractionCamera.Render();
  113. GetComponent<Renderer>().sharedMaterial.SetTexture("_RefractionTex", m_RefractionTexture);
  114. }
  115. // Restore pixel light count
  116. if (disablePixelLights)
  117. {
  118. QualitySettings.pixelLightCount = oldPixelLightCount;
  119. }
  120. // Setup shader keywords based on water mode
  121. switch (mode)
  122. {
  123. case WaterMode.Simple:
  124. Shader.EnableKeyword("WATER_SIMPLE");
  125. Shader.DisableKeyword("WATER_REFLECTIVE");
  126. Shader.DisableKeyword("WATER_REFRACTIVE");
  127. break;
  128. case WaterMode.Reflective:
  129. Shader.DisableKeyword("WATER_SIMPLE");
  130. Shader.EnableKeyword("WATER_REFLECTIVE");
  131. Shader.DisableKeyword("WATER_REFRACTIVE");
  132. break;
  133. case WaterMode.Refractive:
  134. Shader.DisableKeyword("WATER_SIMPLE");
  135. Shader.DisableKeyword("WATER_REFLECTIVE");
  136. Shader.EnableKeyword("WATER_REFRACTIVE");
  137. break;
  138. }
  139. s_InsideWater = false;
  140. }
  141. // Cleanup all the objects we possibly have created
  142. void OnDisable()
  143. {
  144. if (m_ReflectionTexture)
  145. {
  146. DestroyImmediate(m_ReflectionTexture);
  147. m_ReflectionTexture = null;
  148. }
  149. if (m_RefractionTexture)
  150. {
  151. DestroyImmediate(m_RefractionTexture);
  152. m_RefractionTexture = null;
  153. }
  154. foreach (var kvp in m_ReflectionCameras)
  155. {
  156. DestroyImmediate((kvp.Value).gameObject);
  157. }
  158. m_ReflectionCameras.Clear();
  159. foreach (var kvp in m_RefractionCameras)
  160. {
  161. DestroyImmediate((kvp.Value).gameObject);
  162. }
  163. m_RefractionCameras.Clear();
  164. }
  165. // This just sets up some matrices in the material; for really
  166. // old cards to make water texture scroll.
  167. void Update()
  168. {
  169. if (!GetComponent<Renderer>())
  170. {
  171. return;
  172. }
  173. Material mat = GetComponent<Renderer>().sharedMaterial;
  174. if (!mat)
  175. {
  176. return;
  177. }
  178. Vector4 waveSpeed = mat.GetVector("WaveSpeed");
  179. float waveScale = mat.GetFloat("_WaveScale");
  180. Vector4 waveScale4 = new Vector4(waveScale, waveScale, waveScale * 0.4f, waveScale * 0.45f);
  181. // Time since level load, and do intermediate calculations with doubles
  182. double t = Time.timeSinceLevelLoad / 20.0;
  183. Vector4 offsetClamped = new Vector4(
  184. (float)Math.IEEERemainder(waveSpeed.x * waveScale4.x * t, 1.0),
  185. (float)Math.IEEERemainder(waveSpeed.y * waveScale4.y * t, 1.0),
  186. (float)Math.IEEERemainder(waveSpeed.z * waveScale4.z * t, 1.0),
  187. (float)Math.IEEERemainder(waveSpeed.w * waveScale4.w * t, 1.0)
  188. );
  189. mat.SetVector("_WaveOffset", offsetClamped);
  190. mat.SetVector("_WaveScale4", waveScale4);
  191. }
  192. void UpdateCameraModes(Camera src, Camera dest)
  193. {
  194. if (dest == null)
  195. {
  196. return;
  197. }
  198. // set water camera to clear the same way as current camera
  199. dest.clearFlags = src.clearFlags;
  200. dest.backgroundColor = src.backgroundColor;
  201. if (src.clearFlags == CameraClearFlags.Skybox)
  202. {
  203. Skybox sky = src.GetComponent<Skybox>();
  204. Skybox mysky = dest.GetComponent<Skybox>();
  205. if (!sky || !sky.material)
  206. {
  207. mysky.enabled = false;
  208. }
  209. else
  210. {
  211. mysky.enabled = true;
  212. mysky.material = sky.material;
  213. }
  214. }
  215. // update other values to match current camera.
  216. // even if we are supplying custom camera&projection matrices,
  217. // some of values are used elsewhere (e.g. skybox uses far plane)
  218. dest.farClipPlane = src.farClipPlane;
  219. dest.nearClipPlane = src.nearClipPlane;
  220. dest.orthographic = src.orthographic;
  221. dest.fieldOfView = src.fieldOfView;
  222. dest.aspect = src.aspect;
  223. dest.orthographicSize = src.orthographicSize;
  224. }
  225. // On-demand create any objects we need for water
  226. void CreateWaterObjects(Camera currentCamera, out Camera reflectionCamera, out Camera refractionCamera)
  227. {
  228. WaterMode mode = GetWaterMode();
  229. reflectionCamera = null;
  230. refractionCamera = null;
  231. if (mode >= WaterMode.Reflective)
  232. {
  233. // Reflection render texture
  234. if (!m_ReflectionTexture || m_OldReflectionTextureSize != textureSize)
  235. {
  236. if (m_ReflectionTexture)
  237. {
  238. DestroyImmediate(m_ReflectionTexture);
  239. }
  240. m_ReflectionTexture = new RenderTexture(textureSize, textureSize, 16);
  241. m_ReflectionTexture.name = "__WaterReflection" + GetInstanceID();
  242. m_ReflectionTexture.isPowerOfTwo = true;
  243. m_ReflectionTexture.hideFlags = HideFlags.DontSave;
  244. m_OldReflectionTextureSize = textureSize;
  245. }
  246. // Camera for reflection
  247. m_ReflectionCameras.TryGetValue(currentCamera, out reflectionCamera);
  248. if (!reflectionCamera) // catch both not-in-dictionary and in-dictionary-but-deleted-GO
  249. {
  250. GameObject go = new GameObject("Water Refl Camera id" + GetInstanceID() + " for " + currentCamera.GetInstanceID(), typeof(Camera), typeof(Skybox));
  251. reflectionCamera = go.GetComponent<Camera>();
  252. reflectionCamera.enabled = false;
  253. reflectionCamera.transform.position = transform.position;
  254. reflectionCamera.transform.rotation = transform.rotation;
  255. reflectionCamera.gameObject.AddComponent<FlareLayer>();
  256. go.hideFlags = HideFlags.HideAndDontSave;
  257. m_ReflectionCameras[currentCamera] = reflectionCamera;
  258. }
  259. }
  260. if (mode >= WaterMode.Refractive)
  261. {
  262. // Refraction render texture
  263. if (!m_RefractionTexture || m_OldRefractionTextureSize != textureSize)
  264. {
  265. if (m_RefractionTexture)
  266. {
  267. DestroyImmediate(m_RefractionTexture);
  268. }
  269. m_RefractionTexture = new RenderTexture(textureSize, textureSize, 16);
  270. m_RefractionTexture.name = "__WaterRefraction" + GetInstanceID();
  271. m_RefractionTexture.isPowerOfTwo = true;
  272. m_RefractionTexture.hideFlags = HideFlags.DontSave;
  273. m_OldRefractionTextureSize = textureSize;
  274. }
  275. // Camera for refraction
  276. m_RefractionCameras.TryGetValue(currentCamera, out refractionCamera);
  277. if (!refractionCamera) // catch both not-in-dictionary and in-dictionary-but-deleted-GO
  278. {
  279. GameObject go =
  280. new GameObject("Water Refr Camera id" + GetInstanceID() + " for " + currentCamera.GetInstanceID(),
  281. typeof(Camera), typeof(Skybox));
  282. refractionCamera = go.GetComponent<Camera>();
  283. refractionCamera.enabled = false;
  284. refractionCamera.transform.position = transform.position;
  285. refractionCamera.transform.rotation = transform.rotation;
  286. refractionCamera.gameObject.AddComponent<FlareLayer>();
  287. go.hideFlags = HideFlags.HideAndDontSave;
  288. m_RefractionCameras[currentCamera] = refractionCamera;
  289. }
  290. }
  291. }
  292. WaterMode GetWaterMode()
  293. {
  294. if (m_HardwareWaterSupport < waterMode)
  295. {
  296. return m_HardwareWaterSupport;
  297. }
  298. return waterMode;
  299. }
  300. WaterMode FindHardwareWaterSupport()
  301. {
  302. if (!GetComponent<Renderer>())
  303. {
  304. return WaterMode.Simple;
  305. }
  306. Material mat = GetComponent<Renderer>().sharedMaterial;
  307. if (!mat)
  308. {
  309. return WaterMode.Simple;
  310. }
  311. string mode = mat.GetTag("WATERMODE", false);
  312. if (mode == "Refractive")
  313. {
  314. return WaterMode.Refractive;
  315. }
  316. if (mode == "Reflective")
  317. {
  318. return WaterMode.Reflective;
  319. }
  320. return WaterMode.Simple;
  321. }
  322. // Given position/normal of the plane, calculates plane in camera space.
  323. Vector4 CameraSpacePlane(Camera cam, Vector3 pos, Vector3 normal, float sideSign)
  324. {
  325. Vector3 offsetPos = pos + normal * clipPlaneOffset;
  326. Matrix4x4 m = cam.worldToCameraMatrix;
  327. Vector3 cpos = m.MultiplyPoint(offsetPos);
  328. Vector3 cnormal = m.MultiplyVector(normal).normalized * sideSign;
  329. return new Vector4(cnormal.x, cnormal.y, cnormal.z, -Vector3.Dot(cpos, cnormal));
  330. }
  331. // Calculates reflection matrix around the given plane
  332. static void CalculateReflectionMatrix(ref Matrix4x4 reflectionMat, Vector4 plane)
  333. {
  334. reflectionMat.m00 = (1F - 2F * plane[0] * plane[0]);
  335. reflectionMat.m01 = (- 2F * plane[0] * plane[1]);
  336. reflectionMat.m02 = (- 2F * plane[0] * plane[2]);
  337. reflectionMat.m03 = (- 2F * plane[3] * plane[0]);
  338. reflectionMat.m10 = (- 2F * plane[1] * plane[0]);
  339. reflectionMat.m11 = (1F - 2F * plane[1] * plane[1]);
  340. reflectionMat.m12 = (- 2F * plane[1] * plane[2]);
  341. reflectionMat.m13 = (- 2F * plane[3] * plane[1]);
  342. reflectionMat.m20 = (- 2F * plane[2] * plane[0]);
  343. reflectionMat.m21 = (- 2F * plane[2] * plane[1]);
  344. reflectionMat.m22 = (1F - 2F * plane[2] * plane[2]);
  345. reflectionMat.m23 = (- 2F * plane[3] * plane[2]);
  346. reflectionMat.m30 = 0F;
  347. reflectionMat.m31 = 0F;
  348. reflectionMat.m32 = 0F;
  349. reflectionMat.m33 = 1F;
  350. }
  351. }
  352. }