using System.Collections; using UnityEngine; public class ReflectionCamera : MonoBehaviour { public float m_ClipPlaneOffset = 0.07f; public LayerMask CullingMask = -17; public bool HDR; public bool OcclusionCulling = true; public float TextureScale = 1f; public RenderTextureFormat RenderTextureFormat; public FilterMode FilterMode; public RenderingPath RenderingPath; public bool UseRealtimeUpdate; public int FPSWhenMoveCamera = 40; public int FPSWhenStaticCamera = 20; private RenderTexture renderTexture; private GameObject go; private Camera reflectionCamera; private Vector3 oldPosition; private Quaternion oldRotation; private Transform instanceCameraTransform; private int frameCountWhenCameraIsStatic; private bool canUpdateCamera; private bool isStaticUpdate; private WaitForSeconds fpsMove; private WaitForSeconds fpsStatic; private const int DropedFrames = 50; private Camera currentCamera; private void OnEnable() { Shader.EnableKeyword("editor_off"); Shader.EnableKeyword("cubeMap_off"); currentCamera = Camera.main; fpsMove = new WaitForSeconds(1f / (float)FPSWhenMoveCamera); fpsStatic = new WaitForSeconds(1f / (float)FPSWhenStaticCamera); if (!UseRealtimeUpdate) { StartCoroutine(RepeatCameraMove()); StartCoroutine(RepeatCameraStatic()); } else { canUpdateCamera = true; } } private IEnumerator RepeatCameraMove() { while (true) { if (!isStaticUpdate) { canUpdateCamera = true; } yield return fpsMove; } } private IEnumerator RepeatCameraStatic() { while (true) { if (isStaticUpdate) { canUpdateCamera = true; } yield return fpsStatic; } } private void OnBecameVisible() { if (go != null) { go.SetActive(true); } } private void OnBecameInvisible() { } private void Update() { Vector3 position = base.transform.position; Vector3 up = base.transform.up; float w = 0f - Vector3.Dot(up, position) - m_ClipPlaneOffset; Vector4 plane = new Vector4(up.x, up.y, up.z, w); Matrix4x4 reflectionMat = Matrix4x4.zero; CalculateReflectionMatrix(ref reflectionMat, plane); Vector3 position2 = currentCamera.transform.position; Vector3 position3 = reflectionMat.MultiplyPoint(position2); if (go == null) { renderTexture = new RenderTexture((int)((float)Screen.width * TextureScale), (int)((float)Screen.height * TextureScale), 16, RenderTextureFormat); renderTexture.DiscardContents(); go = new GameObject("Water Refl Camera"); reflectionCamera = go.AddComponent(); reflectionCamera.depth = currentCamera.depth - 1f; reflectionCamera.renderingPath = RenderingPath; reflectionCamera.depthTextureMode = DepthTextureMode.None; go.transform.position = base.transform.position; go.transform.rotation = base.transform.rotation; reflectionCamera.cullingMask = CullingMask; reflectionCamera.targetTexture = renderTexture; reflectionCamera.hdr = HDR; reflectionCamera.useOcclusionCulling = OcclusionCulling; Shader.SetGlobalTexture("_ReflectionTex", renderTexture); instanceCameraTransform = reflectionCamera.transform; } reflectionCamera.worldToCameraMatrix = currentCamera.worldToCameraMatrix * reflectionMat; Vector4 clipPlane = CameraSpacePlane(reflectionCamera, position, up, 1f); reflectionCamera.projectionMatrix = currentCamera.CalculateObliqueMatrix(clipPlane); GL.invertCulling = true; go.transform.position = position3; Vector3 eulerAngles = currentCamera.transform.eulerAngles; go.transform.eulerAngles = new Vector3(0f - eulerAngles.x, eulerAngles.y, eulerAngles.z); UpdateCameraPosition(); GL.invertCulling = false; } private void UpdateCameraPosition() { if (reflectionCamera == null) { return; } if (Vector3.SqrMagnitude(instanceCameraTransform.position - oldPosition) <= 1E-05f && instanceCameraTransform.rotation == oldRotation) { frameCountWhenCameraIsStatic++; if (frameCountWhenCameraIsStatic >= 50) { isStaticUpdate = true; } } else { frameCountWhenCameraIsStatic = 0; isStaticUpdate = false; } oldPosition = instanceCameraTransform.position; oldRotation = instanceCameraTransform.rotation; if (canUpdateCamera) { reflectionCamera.enabled = true; if (!UseRealtimeUpdate) { canUpdateCamera = false; } } else if (reflectionCamera.enabled) { reflectionCamera.enabled = false; } } private static float sgn(float a) { if (a > 0f) { return 1f; } if (a < 0f) { return -1f; } return 0f; } private Matrix4x4 CalculateObliqueMatrix(Matrix4x4 projection, Vector4 clipPlane) { Vector4 b = projection.inverse * new Vector4(sgn(clipPlane.x), sgn(clipPlane.y), 1f, 1f); Vector4 vector = clipPlane * (2f / Vector4.Dot(clipPlane, b)); projection[2] = vector.x - projection[3]; projection[6] = vector.y - projection[7]; projection[10] = vector.z - projection[11]; projection[14] = vector.w - projection[15]; return projection; } private 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; } private Vector4 CameraSpacePlane(Camera cam, Vector3 pos, Vector3 normal, float sideSign) { Vector3 point = pos + normal * m_ClipPlaneOffset; Matrix4x4 worldToCameraMatrix = cam.worldToCameraMatrix; Vector3 lhs = worldToCameraMatrix.MultiplyPoint(point); Vector3 rhs = worldToCameraMatrix.MultiplyVector(normal).normalized * sideSign; return new Vector4(rhs.x, rhs.y, rhs.z, 0f - Vector3.Dot(lhs, rhs)); } private void OnDisable() { ClearCamera(); Shader.DisableKeyword("editor_off"); Shader.DisableKeyword("cubeMap_off"); } private void ClearCamera() { if ((bool)go) { Object.DestroyImmediate(go); go = null; } if ((bool)renderTexture) { Object.DestroyImmediate(renderTexture); renderTexture = null; } } }