You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

369 lines
13 KiB

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