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


Gõ tìm kiếm nhanh...