256 lines
6.4 KiB
C#
256 lines
6.4 KiB
C#
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<Camera>();
|
|
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;
|
|
}
|
|
}
|
|
}
|