1 using System;
2 using
System.Collections.Generic;
3 using
UnityEngine;
4
5 namespace
UnityStandardAssets.Water
6 {
7     
[ExecuteInEditMode]
8     
[RequireComponent(typeof(WaterBase))]
9     
public class PlanarReflection : MonoBehaviour
10     {
11         
public LayerMask reflectionMask;
12         
public bool reflectSkybox = false;
13         
public Color clearColor = Color.grey;
14         
public String reflectionSampler = "_ReflectionTex";
15         
public float clipPlaneOffset = 0.07F;
16
17
18         Vector3 m_Oldpos;
19         Camera m_ReflectionCamera;
20         Material m_SharedMaterial;
21         Dictionary<Camera,
bool> m_HelperCameras;
22
23
24         
public void Start()
25         {
26             m_SharedMaterial = ((WaterBase)gameObject.GetComponent(
typeof(WaterBase))).sharedMaterial;
27         }
28
29
30         Camera CreateReflectionCameraFor(Camera cam)
31         {
32             String reflName = gameObject.name +
"Reflection" + cam.name;
33             GameObject go = GameObject.Find(reflName);
34
35             
if (!go)
36             {
37                 go =
new GameObject(reflName, typeof(Camera));
38             }
39             
if (!go.GetComponent(typeof(Camera)))
40             {
41                 go.AddComponent(
typeof(Camera));
42             }
43             Camera reflectCamera = go.GetComponent<Camera>();
44
45             reflectCamera.backgroundColor = clearColor;
46             reflectCamera.clearFlags = reflectSkybox ? CameraClearFlags.Skybox : CameraClearFlags.SolidColor;
47
48             SetStandardCameraParameter(reflectCamera, reflectionMask);
49
50             
if (!reflectCamera.targetTexture)
51             {
52                 reflectCamera.targetTexture = CreateTextureFor(cam);
53             }
54
55             
return reflectCamera;
56         }
57
58
59         
void SetStandardCameraParameter(Camera cam, LayerMask mask)
60         {
61             cam.cullingMask = mask & ~(
1 << LayerMask.NameToLayer("Water"));
62             cam.backgroundColor = Color.black;
63             cam.enabled =
false;
64         }
65
66
67         RenderTexture CreateTextureFor(Camera cam)
68         {
69             RenderTexture rt =
new RenderTexture(Mathf.FloorToInt(cam.pixelWidth * 0.5F),
70                 Mathf.FloorToInt(cam.pixelHeight *
0.5F), 24);
71             rt.hideFlags = HideFlags.DontSave;
72             
return rt;
73         }
74
75
76         
public void RenderHelpCameras(Camera currentCam)
77         {
78             
if (null == m_HelperCameras)
79             {
80                 m_HelperCameras =
new Dictionary<Camera, bool>();
81             }
82
83             
if (!m_HelperCameras.ContainsKey(currentCam))
84             {
85                 m_HelperCameras.Add(currentCam,
false);
86             }
87             
if (m_HelperCameras[currentCam])
88             {
89                 
return;
90             }
91
92             
if (!m_ReflectionCamera)
93             {
94                 m_ReflectionCamera = CreateReflectionCameraFor(currentCam);
95             }
96
97             RenderReflectionFor(currentCam, m_ReflectionCamera);
98
99             m_HelperCameras[currentCam] =
true;
100         }
101
102
103         
public void LateUpdate()
104         {
105             
if (null != m_HelperCameras)
106             {
107                 m_HelperCameras.Clear();
108             }
109         }
110
111
112         
public void WaterTileBeingRendered(Transform tr, Camera currentCam)
113         {
114             RenderHelpCameras(currentCam);
115
116             
if (m_ReflectionCamera && m_SharedMaterial)
117             {
118                 m_SharedMaterial.SetTexture(reflectionSampler, m_ReflectionCamera.targetTexture);
119             }
120         }
121
122
123         
public void OnEnable()
124         {
125             Shader.EnableKeyword(
"WATER_REFLECTIVE");
126             Shader.DisableKeyword(
"WATER_SIMPLE");
127         }
128
129
130         
public void OnDisable()
131         {
132             Shader.EnableKeyword(
"WATER_SIMPLE");
133             Shader.DisableKeyword(
"WATER_REFLECTIVE");
134         }
135
136
137         
void RenderReflectionFor(Camera cam, Camera reflectCamera)
138         {
139             
if (!reflectCamera)
140             {
141                 
return;
142             }
143
144             
if (m_SharedMaterial && !m_SharedMaterial.HasProperty(reflectionSampler))
145             {
146                 
return;
147             }
148
149             reflectCamera.cullingMask = reflectionMask & ~(
1 << LayerMask.NameToLayer("Water"));
150
151             SaneCameraSettings(reflectCamera);
152
153             reflectCamera.backgroundColor = clearColor;
154             reflectCamera.clearFlags = reflectSkybox ? CameraClearFlags.Skybox : CameraClearFlags.SolidColor;
155             
if (reflectSkybox)
156             {
157                 
if (cam.gameObject.GetComponent(typeof(Skybox)))
158                 {
159                     Skybox sb = (Skybox)reflectCamera.gameObject.GetComponent(
typeof(Skybox));
160                     
if (!sb)
161                     {
162                         sb = (Skybox)reflectCamera.gameObject.AddComponent(
typeof(Skybox));
163                     }
164                     sb.material = ((Skybox)cam.GetComponent(
typeof(Skybox))).material;
165                 }
166             }
167
168             GL.invertCulling =
true;
169
170             Transform reflectiveSurface = transform;
//waterHeight;
171
172             Vector3 eulerA = cam.transform.eulerAngles;
173
174             reflectCamera.transform.eulerAngles =
new Vector3(-eulerA.x, eulerA.y, eulerA.z);
175             reflectCamera.transform.position = cam.transform.position;
176
177             Vector3 pos = reflectiveSurface.transform.position;
178             pos.y = reflectiveSurface.position.y;
179             Vector3 normal = reflectiveSurface.transform.up;
180             
float d = -Vector3.Dot(normal, pos) - clipPlaneOffset;
181             Vector4 reflectionPlane =
new Vector4(normal.x, normal.y, normal.z, d);
182
183             Matrix4x4 reflection = Matrix4x4.zero;
184             reflection = CalculateReflectionMatrix(reflection, reflectionPlane);
185             m_Oldpos = cam.transform.position;
186             Vector3 newpos = reflection.MultiplyPoint(m_Oldpos);
187
188             reflectCamera.worldToCameraMatrix = cam.worldToCameraMatrix * reflection;
189
190             Vector4 clipPlane = CameraSpacePlane(reflectCamera, pos, normal,
1.0f);
191
192             Matrix4x4 projection = cam.projectionMatrix;
193             projection = CalculateObliqueMatrix(projection, clipPlane);
194             reflectCamera.projectionMatrix = projection;
195
196             reflectCamera.transform.position = newpos;
197             Vector3 euler = cam.transform.eulerAngles;
198             reflectCamera.transform.eulerAngles =
new Vector3(-euler.x, euler.y, euler.z);
199
200             reflectCamera.Render();
201
202             GL.invertCulling =
false;
203         }
204
205
206         
void SaneCameraSettings(Camera helperCam)
207         {
208             helperCam.depthTextureMode = DepthTextureMode.None;
209             helperCam.backgroundColor = Color.black;
210             helperCam.clearFlags = CameraClearFlags.SolidColor;
211             helperCam.renderingPath = RenderingPath.Forward;
212         }
213
214
215         
static Matrix4x4 CalculateObliqueMatrix(Matrix4x4 projection, Vector4 clipPlane)
216         {
217             Vector4 q = projection.inverse *
new Vector4(
218                 Sgn(clipPlane.x),
219                 Sgn(clipPlane.y),
220                 
1.0F,
221                 
1.0F
222                 );
223             Vector4 c = clipPlane * (
2.0F / (Vector4.Dot(clipPlane, q)));
224             
// third row = clip plane - fourth row
225             projection[
2] = c.x - projection[3];
226             projection[
6] = c.y - projection[7];
227             projection[
10] = c.z - projection[11];
228             projection[
14] = c.w - projection[15];
229
230             
return projection;
231         }
232
233
234         
static Matrix4x4 CalculateReflectionMatrix(Matrix4x4 reflectionMat, Vector4 plane)
235         {
236             reflectionMat.m00 = (
1.0F - 2.0F * plane[0] * plane[0]);
237             reflectionMat.m01 = (-
2.0F * plane[0] * plane[1]);
238             reflectionMat.m02 = (-
2.0F * plane[0] * plane[2]);
239             reflectionMat.m03 = (-
2.0F * plane[3] * plane[0]);
240
241             reflectionMat.m10 = (-
2.0F * plane[1] * plane[0]);
242             reflectionMat.m11 = (
1.0F - 2.0F * plane[1] * plane[1]);
243             reflectionMat.m12 = (-
2.0F * plane[1] * plane[2]);
244             reflectionMat.m13 = (-
2.0F * plane[3] * plane[1]);
245
246             reflectionMat.m20 = (-
2.0F * plane[2] * plane[0]);
247             reflectionMat.m21 = (-
2.0F * plane[2] * plane[1]);
248             reflectionMat.m22 = (
1.0F - 2.0F * plane[2] * plane[2]);
249             reflectionMat.m23 = (-
2.0F * plane[3] * plane[2]);
250
251             reflectionMat.m30 =
0.0F;
252             reflectionMat.m31 =
0.0F;
253             reflectionMat.m32 =
0.0F;
254             reflectionMat.m33 =
1.0F;
255
256             
return reflectionMat;
257         }
258
259
260         
static float Sgn(float a)
261         {
262             
if (a > 0.0F)
263             {
264                 
return 1.0F;
265             }
266             
if (a < 0.0F)
267             {
268                 
return -1.0F;
269             }
270             
return 0.0F;
271         }
272
273
274         Vector4 CameraSpacePlane(Camera cam, Vector3 pos, Vector3 normal,
float sideSign)
275         {
276             Vector3 offsetPos = pos + normal * clipPlaneOffset;
277             Matrix4x4 m = cam.worldToCameraMatrix;
278             Vector3 cpos = m.MultiplyPoint(offsetPos);
279             Vector3 cnormal = m.MultiplyVector(normal).normalized * sideSign;
280
281             
return new Vector4(cnormal.x, cnormal.y, cnormal.z, -Vector3.Dot(cpos, cnormal));
282         }
283     }
284 }