using UnityEngine; using System.Collections; using System.Collections.Generic; public class MirrorCameraScript : MonoBehaviour { public GameObject MirrorObject; public bool VRMode; private Renderer mirrorRenderer; private Material mirrorMaterial; private MirrorScript mirrorScript; private Camera cameraObject; private RenderTexture reflectionTexture; private Matrix4x4 reflectionMatrix; private int oldReflectionTextureSize; private static bool renderingMirror; private void Start() { mirrorScript = GetComponentInParent(); cameraObject = GetComponent(); //cameraObject.enabled = true; if (mirrorScript.AddFlareLayer) { cameraObject.gameObject.AddComponent(); } mirrorRenderer = MirrorObject.GetComponent(); if (Application.isPlaying) { foreach (Material m in mirrorRenderer.sharedMaterials) { if (m.name == "MirrorMaterial") { mirrorRenderer.sharedMaterial = m; break; } } } mirrorMaterial = mirrorRenderer.sharedMaterial; CreateRenderTexture(); } private void CreateRenderTexture() { if (reflectionTexture == null || oldReflectionTextureSize != mirrorScript.TextureSize) { if (reflectionTexture) { DestroyImmediate(reflectionTexture); } reflectionTexture = new RenderTexture(mirrorScript.TextureSize, mirrorScript.TextureSize, 16); reflectionTexture.filterMode = FilterMode.Bilinear; reflectionTexture.antiAliasing = 1; reflectionTexture.name = "MirrorRenderTexture_" + GetInstanceID(); reflectionTexture.hideFlags = HideFlags.HideAndDontSave; reflectionTexture.autoGenerateMips = false; reflectionTexture.wrapMode = TextureWrapMode.Clamp; mirrorMaterial.SetTexture("_MainTex", reflectionTexture); oldReflectionTextureSize = mirrorScript.TextureSize; } if (cameraObject.targetTexture != reflectionTexture) { cameraObject.targetTexture = reflectionTexture; } } private void Update() { if (VRMode && Camera.current == Camera.main) { return; } CreateRenderTexture(); } private void UpdateCameraProperties(Camera src, Camera dest) { dest.clearFlags = src.clearFlags; dest.backgroundColor = src.backgroundColor; if (src.clearFlags == CameraClearFlags.Skybox) { Skybox sky = src.GetComponent(); Skybox mysky = dest.GetComponent(); if (!sky || !sky.material) { mysky.enabled = false; } else { mysky.enabled = true; mysky.material = sky.material; } } dest.orthographic = src.orthographic; dest.orthographicSize = src.orthographicSize; if (mirrorScript.AspectRatio > 0.0f) { dest.aspect = mirrorScript.AspectRatio; } else { dest.aspect = src.aspect; } dest.renderingPath = src.renderingPath; } internal void RenderMirror() { Camera cameraLookingAtThisMirror; // bail if we don't have a camera or renderer if (renderingMirror || !enabled || (cameraLookingAtThisMirror = Camera.current) == null || mirrorRenderer == null || mirrorMaterial == null || !mirrorRenderer.enabled) { return; } renderingMirror = true; int oldPixelLightCount = QualitySettings.pixelLightCount; if (QualitySettings.pixelLightCount != mirrorScript.MaximumPerPixelLights) { QualitySettings.pixelLightCount = mirrorScript.MaximumPerPixelLights; } try { UpdateCameraProperties(cameraLookingAtThisMirror, cameraObject); if (mirrorScript.MirrorRecursion) { mirrorMaterial.EnableKeyword("MIRROR_RECURSION"); cameraObject.ResetWorldToCameraMatrix(); cameraObject.ResetProjectionMatrix(); cameraObject.projectionMatrix = cameraObject.projectionMatrix * Matrix4x4.Scale(new Vector3(-1, 1, 1)); cameraObject.cullingMask = ~(1 << 4) & mirrorScript.ReflectLayers.value; GL.invertCulling = true; cameraObject.Render(); GL.invertCulling = false; } else { mirrorMaterial.DisableKeyword("MIRROR_RECURSION"); Vector3 pos = transform.position; Vector3 normal = (mirrorScript.NormalIsForward ? transform.forward : transform.up); // Reflect camera around reflection plane float d = -Vector3.Dot(normal, pos) - mirrorScript.ClipPlaneOffset; Vector4 reflectionPlane = new Vector4(normal.x, normal.y, normal.z, d); CalculateReflectionMatrix(ref reflectionPlane); Vector3 oldpos = cameraObject.transform.position; float oldclip = cameraObject.farClipPlane; Vector3 newpos = reflectionMatrix.MultiplyPoint(oldpos); Matrix4x4 worldToCameraMatrix = cameraLookingAtThisMirror.worldToCameraMatrix; if (VRMode) { if(cameraLookingAtThisMirror.stereoActiveEye == Camera.MonoOrStereoscopicEye.Left) { worldToCameraMatrix[12] += 0.011f; } else if (cameraLookingAtThisMirror.stereoActiveEye == Camera.MonoOrStereoscopicEye.Right) { worldToCameraMatrix[12] -= 0.011f; } } worldToCameraMatrix *= reflectionMatrix; cameraObject.worldToCameraMatrix = worldToCameraMatrix; // Clip out background Vector4 clipPlane = CameraSpacePlane(ref worldToCameraMatrix, ref pos, ref normal, 1.0f); cameraObject.projectionMatrix = cameraLookingAtThisMirror.CalculateObliqueMatrix(clipPlane); GL.invertCulling = true; cameraObject.transform.position = newpos; cameraObject.farClipPlane = mirrorScript.FarClipPlane; cameraObject.cullingMask = ~(1 << 4) & mirrorScript.ReflectLayers.value; cameraObject.Render(); cameraObject.transform.position = oldpos; cameraObject.farClipPlane = oldclip; GL.invertCulling = false; } } finally { renderingMirror = false; if (QualitySettings.pixelLightCount != oldPixelLightCount) { QualitySettings.pixelLightCount = oldPixelLightCount; } } } // Cleanup all the objects we possibly have created private void OnDisable() { if (reflectionTexture) { DestroyImmediate(reflectionTexture); reflectionTexture = null; } } private Vector4 CameraSpacePlane(ref Matrix4x4 worldToCameraMatrix, ref Vector3 pos, ref Vector3 normal, float sideSign) { Vector3 offsetPos = pos + normal * mirrorScript.ClipPlaneOffset; Vector3 cpos = worldToCameraMatrix.MultiplyPoint(offsetPos); Vector3 cnormal = worldToCameraMatrix.MultiplyVector(normal).normalized * sideSign; return new Vector4(cnormal.x, cnormal.y, cnormal.z, -Vector3.Dot(cpos, cnormal)); } private void CalculateReflectionMatrix(ref Vector4 plane) { // Calculates reflection matrix around the given plane reflectionMatrix.m00 = (1F - 2F * plane[0] * plane[0]); reflectionMatrix.m01 = (-2F * plane[0] * plane[1]); reflectionMatrix.m02 = (-2F * plane[0] * plane[2]); reflectionMatrix.m03 = (-2F * plane[3] * plane[0]); reflectionMatrix.m10 = (-2F * plane[1] * plane[0]); reflectionMatrix.m11 = (1F - 2F * plane[1] * plane[1]); reflectionMatrix.m12 = (-2F * plane[1] * plane[2]); reflectionMatrix.m13 = (-2F * plane[3] * plane[1]); reflectionMatrix.m20 = (-2F * plane[2] * plane[0]); reflectionMatrix.m21 = (-2F * plane[2] * plane[1]); reflectionMatrix.m22 = (1F - 2F * plane[2] * plane[2]); reflectionMatrix.m23 = (-2F * plane[3] * plane[2]); reflectionMatrix.m30 = 0F; reflectionMatrix.m31 = 0F; reflectionMatrix.m32 = 0F; reflectionMatrix.m33 = 1F; } private static void CalculateObliqueMatrix(ref Matrix4x4 projection, ref Vector4 clipPlane) { Vector4 q = projection.inverse * new Vector4 ( Sign(clipPlane.x), Sign(clipPlane.y), 1.0f, 1.0f ); Vector4 c = clipPlane * (2.0F / (Vector4.Dot(clipPlane, q))); // third row = clip plane - fourth row projection[2] = c.x - projection[3]; projection[6] = c.y - projection[7]; projection[10] = c.z - projection[11]; projection[14] = c.w - projection[15]; } private static float Sign(float a) { if (a > 0.0f) return 1.0f; if (a < 0.0f) return -1.0f; return 0.0f; } }