using System; using System.Collections.Generic; using System.Reflection; using UnityEngine; using UnityEngine.Rendering; using UnityEngine.Rendering.PostProcessing; namespace PlanarReflections3 { [ExecuteAlways] public class PlanarReflectionsRenderer : MonoBehaviour { private string version = "3.4"; private float frameTime; protected static ReflectionSettings globalReflectionSettings = new ReflectionSettings(); [SerializeField] protected ReflectionSettings settings = new ReflectionSettings(); [SerializeField] protected RenderTexture externalReflectionTex; [SerializeField] protected RenderTexture externalReflectionDepth; protected RenderTexture reflectionTex; protected RenderTexture reflectionDepth; [SerializeField] protected bool SRPMode; public Shader internalDepthShader; public bool castersActive; private Dictionary gameReflectors = new Dictionary(); private InternalReflectionRenderer sceneViewReflector = new InternalReflectionRenderer(); [SerializeField] protected List releasables = new List(); private List rTex = new List(); public string Version => version; public ReflectionSettings GlobalSettings => globalReflectionSettings; public ReflectionSettings Settings => settings; public RenderTexture ReflectionTex { get { if (!externalReflectionTex) { return reflectionTex; } return externalReflectionTex; } } public RenderTexture ReflectionDepth { get { if (!externalReflectionDepth) { return reflectionDepth; } return externalReflectionDepth; } } public bool isSRP => SRPMode; public void OnEnable() { Camera[] array = Resources.FindObjectsOfTypeAll(); Camera[] array2 = array; foreach (Camera camera in array2) { if (camera.name.Contains("PLANAR3_") && !SRPMode) { if (camera.targetTexture != externalReflectionTex) { RenderTexture.ReleaseTemporary(camera.targetTexture); } camera.targetTexture = null; } } if (GraphicsSettings.renderPipelineAsset != null) { SRPMode = true; } else { SRPMode = false; } if (SRPMode) { array2 = array; foreach (Camera camera2 in array2) { if (camera2.name.Contains("PLANAR3_")) { camera2.targetTexture = null; UnityEngine.Object.Destroy(camera2.gameObject); } } RenderPipelineManager.beginFrameRendering -= SRPRenderReflection; RenderPipelineManager.beginFrameRendering += SRPRenderReflection; RenderPipelineManager.beginFrameRendering -= CleanupTextures; RenderPipelineManager.beginFrameRendering += CleanupTextures; base.gameObject.layer = 4; } else { Camera.onPreCull = (Camera.CameraCallback)Delegate.Remove(Camera.onPreCull, new Camera.CameraCallback(RenderReflection)); Camera.onPreCull = (Camera.CameraCallback)Delegate.Combine(Camera.onPreCull, new Camera.CameraCallback(RenderReflection)); base.gameObject.layer = 4; } } private void Start() { Camera[] array = Resources.FindObjectsOfTypeAll(); Camera[] array2 = array; foreach (Camera camera in array2) { if (camera.name.Contains("PLANAR3_") && !SRPMode) { RenderTexture.ReleaseTemporary(camera.targetTexture); camera.targetTexture = null; UnityEngine.Object.DestroyImmediate(camera.gameObject); } } if (GraphicsSettings.renderPipelineAsset != null) { SRPMode = true; } else { SRPMode = false; } if (SRPMode) { array2 = array; foreach (Camera camera2 in array2) { if (camera2.name.Contains("PLANAR3_")) { camera2.targetTexture = null; UnityEngine.Object.DestroyImmediate(camera2.gameObject); } } RenderPipelineManager.beginFrameRendering -= SRPRenderReflection; RenderPipelineManager.beginFrameRendering += SRPRenderReflection; RenderPipelineManager.beginFrameRendering -= CleanupTextures; RenderPipelineManager.beginFrameRendering += CleanupTextures; base.gameObject.layer = 4; } else { Camera.onPreCull = (Camera.CameraCallback)Delegate.Remove(Camera.onPreCull, new Camera.CameraCallback(RenderReflection)); Camera.onPreCull = (Camera.CameraCallback)Delegate.Combine(Camera.onPreCull, new Camera.CameraCallback(RenderReflection)); base.gameObject.layer = 4; } } public void SRPRenderReflection(ScriptableRenderContext context, Camera[] srcCamera) { for (int i = 0; i < srcCamera.Length; i++) { if (!srcCamera[i].name.Contains("PLANAR3_")) { RenderReflection(srcCamera[i]); } } } public void CleanupTextures(ScriptableRenderContext context, Camera[] srcCamera) { for (int i = 0; i < releasables.Count; i++) { if (releasables[i] != null) { RenderTexture.ReleaseTemporary(releasables[i]); releasables.RemoveAt(i); if (i > 0) { i--; } } } } public void RenderReflection(Camera srcCamera) { if ((Settings.updateOnCastOnly && !castersActive && Application.isPlaying) || (Settings.trackCamerasWithTag && srcCamera.tag != Settings.CamerasTag && srcCamera.cameraType != CameraType.SceneView) || srcCamera.cameraType == CameraType.Reflection || srcCamera.cameraType == CameraType.Preview) { return; } InternalReflectionRenderer internalReflectionRenderer = null; if (srcCamera.depthTextureMode == DepthTextureMode.None) { srcCamera.depthTextureMode = DepthTextureMode.Depth; } if (srcCamera.cameraType == CameraType.SceneView) { if (Application.isPlaying && SRPMode) { if ((bool)sceneViewReflector.refCamera) { sceneViewReflector.refCamera.enabled = false; } return; } if (!sceneViewReflector.refCamera) { sceneViewReflector.refCamera = new GameObject("PLANAR3_SCENEVIEW", typeof(Camera)).GetComponent(); GameObject obj = sceneViewReflector.refCamera.gameObject; HideFlags hideFlags = (sceneViewReflector.refCamera.hideFlags = HideFlags.HideAndDontSave); obj.hideFlags = hideFlags; } internalReflectionRenderer = sceneViewReflector; } else if (srcCamera.cameraType == CameraType.Game) { if (!gameReflectors.ContainsKey(srcCamera) || gameReflectors[srcCamera] == null) { gameReflectors.Add(srcCamera, new InternalReflectionRenderer()); } if (!gameReflectors[srcCamera].refCamera) { gameReflectors[srcCamera].refCamera = new GameObject("PLANAR3_GAMECAM_" + srcCamera.GetInstanceID(), typeof(Camera)).GetComponent(); GameObject obj2 = gameReflectors[srcCamera].refCamera.gameObject; HideFlags hideFlags = (gameReflectors[srcCamera].refCamera.hideFlags = HideFlags.HideAndDontSave); obj2.hideFlags = hideFlags; } else { GameObject obj3 = gameReflectors[srcCamera].refCamera.gameObject; HideFlags hideFlags = (gameReflectors[srcCamera].refCamera.hideFlags = HideFlags.HideAndDontSave); obj3.hideFlags = hideFlags; } internalReflectionRenderer = gameReflectors[srcCamera]; } if (internalReflectionRenderer == null) { return; } internalReflectionRenderer.refCamera.CopyFrom(srcCamera); if (!SRPMode) { internalReflectionRenderer.refCamera.cameraType = CameraType.Reflection; } if (internalReflectionRenderer.refCamera.clearFlags == CameraClearFlags.Nothing || internalReflectionRenderer.refCamera.clearFlags == CameraClearFlags.Depth) { internalReflectionRenderer.refCamera.clearFlags = CameraClearFlags.Color; } internalReflectionRenderer.refCamera.enabled = SRPMode; if (!externalReflectionTex) { if (!SRPMode) { if (!Application.isPlaying || Settings.targetFramerate == 0 || Time.realtimeSinceStartup > frameTime) { if ((bool)reflectionTex) { internalReflectionRenderer.assignedTexture = null; RenderTexture.ReleaseTemporary(reflectionTex); } int width = (int)(((Settings.resolutionMode == ResolutionMode.ExplicitValue) ? Settings.explicitResolution.x : ((float)srcCamera.pixelWidth)) * Settings.resolutionDownscale); int height = (int)(((Settings.resolutionMode == ResolutionMode.ExplicitValue) ? Settings.explicitResolution.y : ((float)srcCamera.pixelHeight)) * Settings.resolutionDownscale); if (settings.forceFloatOutput && settings.usePostFX) { reflectionTex = RenderTexture.GetTemporary(new RenderTextureDescriptor(width, height, RenderTextureFormat.ARGBFloat, 24)); } else { reflectionTex = RenderTexture.GetTemporary(new RenderTextureDescriptor(width, height, RenderTextureFormat.Default, 24)); } reflectionTex.name = "PIDI_REFTEX" + ((srcCamera.cameraType == CameraType.SceneView) ? "SCENE" : "GAME"); if (!rTex.Contains(reflectionTex)) { rTex.Add(reflectionTex); } if (!reflectionTex.IsCreated()) { reflectionTex.useMipMap = Settings.useMipMaps; reflectionTex.antiAliasing = ((!Settings.useAntialiasing) ? 1 : 4); reflectionTex.Create(); } internalReflectionRenderer.assignedTexture = reflectionTex; } } else if (!internalReflectionRenderer.assignedTexture) { int width2 = (int)(((Settings.resolutionMode == ResolutionMode.ExplicitValue) ? Settings.explicitResolution.x : ((float)srcCamera.pixelWidth)) * Settings.resolutionDownscale); int height2 = (int)(((Settings.resolutionMode == ResolutionMode.ExplicitValue) ? Settings.explicitResolution.y : ((float)srcCamera.pixelHeight)) * Settings.resolutionDownscale); if (settings.forceFloatOutput && settings.usePostFX) { reflectionTex = RenderTexture.GetTemporary(new RenderTextureDescriptor(width2, height2, RenderTextureFormat.ARGBFloat, 24)); } else { reflectionTex = RenderTexture.GetTemporary(new RenderTextureDescriptor(width2, height2, RenderTextureFormat.Default, 24)); } reflectionTex.name = "PIDI_REFTEX" + ((srcCamera.cameraType == CameraType.SceneView) ? "SCENE" : "GAME"); if (!rTex.Contains(reflectionTex)) { rTex.Add(reflectionTex); } reflectionTex.useMipMap = Settings.useMipMaps; reflectionTex.antiAliasing = ((!Settings.useAntialiasing) ? 1 : 4); reflectionTex.Create(); internalReflectionRenderer.assignedTexture = reflectionTex; } else { int num = (int)(((Settings.resolutionMode == ResolutionMode.ExplicitValue) ? Settings.explicitResolution.x : ((float)srcCamera.pixelWidth)) * Settings.resolutionDownscale); int num2 = (int)(((Settings.resolutionMode == ResolutionMode.ExplicitValue) ? Settings.explicitResolution.y : ((float)srcCamera.pixelHeight)) * Settings.resolutionDownscale); if (!internalReflectionRenderer.assignedTexture || internalReflectionRenderer.assignedTexture.width != num || internalReflectionRenderer.assignedTexture.height != num2) { if (internalReflectionRenderer.assignedTexture != null && !releasables.Contains(internalReflectionRenderer.assignedTexture)) { releasables.Add(internalReflectionRenderer.assignedTexture); } internalReflectionRenderer.refCamera.targetTexture = null; if (settings.forceFloatOutput && settings.usePostFX) { reflectionTex = RenderTexture.GetTemporary(new RenderTextureDescriptor(num, num2, RenderTextureFormat.ARGBFloat, 24)); reflectionTex.name = "PIDI_REFTEX" + ((srcCamera.cameraType == CameraType.SceneView) ? "SCENE" : "GAME"); if (!rTex.Contains(reflectionTex)) { rTex.Add(reflectionTex); } reflectionTex.useMipMap = Settings.useMipMaps; reflectionTex.antiAliasing = ((!Settings.useAntialiasing) ? 1 : 4); reflectionTex.Create(); internalReflectionRenderer.assignedTexture = reflectionTex; } else { reflectionTex = RenderTexture.GetTemporary(new RenderTextureDescriptor(num, num2, RenderTextureFormat.Default, 24)); reflectionTex.name = "PIDI_REFTEX" + ((srcCamera.cameraType == CameraType.SceneView) ? "SCENE" : "GAME"); if (!rTex.Contains(reflectionTex)) { rTex.Add(reflectionTex); } reflectionTex.useMipMap = Settings.useMipMaps; reflectionTex.antiAliasing = ((!Settings.useAntialiasing) ? 1 : 4); reflectionTex.Create(); internalReflectionRenderer.assignedTexture = reflectionTex; } } reflectionTex = internalReflectionRenderer.assignedTexture; } reflectionTex.filterMode = FilterMode.Bilinear; } float shadowDistance = QualitySettings.shadowDistance; if (!SRPMode && Settings.customShadowDistance) { QualitySettings.shadowDistance = Settings.shadowDistance; } internalReflectionRenderer.refCamera.targetTexture = ReflectionTex; internalReflectionRenderer.refCamera.aspect = srcCamera.aspect; internalReflectionRenderer.refCamera.rect = new Rect(0f, 0f, 1f, 1f); Vector3 forward = srcCamera.transform.forward; Vector3 up = srcCamera.transform.up; Vector3 position = srcCamera.transform.position; Vector3 direction = base.transform.InverseTransformDirection(forward); Vector3 direction2 = base.transform.InverseTransformDirection(up); Vector3 position2 = base.transform.InverseTransformPoint(position); direction.y *= -1f; direction2.y *= -1f; position2.y *= -1f; forward = base.transform.TransformDirection(direction); up = base.transform.TransformDirection(direction2); position = base.transform.TransformPoint(position2); internalReflectionRenderer.refCamera.transform.position = position; internalReflectionRenderer.refCamera.transform.LookAt(position + forward, up); internalReflectionRenderer.refCamera.nearClipPlane = Settings.nearClipDistance; internalReflectionRenderer.refCamera.farClipPlane = Settings.farClipDistance; internalReflectionRenderer.refCamera.renderingPath = Settings.renderingPath; internalReflectionRenderer.refCamera.cullingMask = Settings.reflectLayers; if (Settings.usePostFX) { PostProcessLayer postProcessLayer = ((Settings.PostFXSettingsMode == PostFXSettingsMode.CustomSettings) ? GetComponent() : srcCamera.GetComponent()); if ((bool)postProcessLayer) { PostProcessLayer component = internalReflectionRenderer.refCamera.GetComponent(); if ((bool)component) { component.volumeTrigger = internalReflectionRenderer.refCamera.transform; component.volumeLayer = postProcessLayer.volumeLayer; component.antialiasingMode = ((postProcessLayer.antialiasingMode != PostProcessLayer.Antialiasing.None) ? PostProcessLayer.Antialiasing.FastApproximateAntialiasing : PostProcessLayer.Antialiasing.None); component.fastApproximateAntialiasing = postProcessLayer.fastApproximateAntialiasing; } else { component = internalReflectionRenderer.refCamera.gameObject.AddComponent(); BindingFlags bindingAttr = BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic; FieldInfo field = typeof(PostProcessLayer).GetField("m_Resources", bindingAttr); component.Init((PostProcessResources)field.GetValue(postProcessLayer)); component.volumeTrigger = internalReflectionRenderer.refCamera.transform; component.volumeLayer = postProcessLayer.volumeLayer; component.antialiasingMode = ((postProcessLayer.antialiasingMode != PostProcessLayer.Antialiasing.None) ? PostProcessLayer.Antialiasing.FastApproximateAntialiasing : PostProcessLayer.Antialiasing.None); component.fastApproximateAntialiasing = postProcessLayer.fastApproximateAntialiasing; } } } else { if ((bool)GetComponent()) { UnityEngine.Object.DestroyImmediate(GetComponent()); if ((bool)GetComponent()) { UnityEngine.Object.DestroyImmediate(GetComponent()); } } if ((bool)internalReflectionRenderer.refCamera.GetComponent()) { UnityEngine.Object.DestroyImmediate(internalReflectionRenderer.refCamera.GetComponent()); } } internalReflectionRenderer.refCamera.transform.Rotate(0.01f, 0.01f, 0.01f); internalReflectionRenderer.refCamera.transform.Translate(0.002f, 0.002f, 0.002f); if (settings.reflectionClipMode == ReflectionClipMode.AccurateClipping) { internalReflectionRenderer.refCamera.projectionMatrix = internalReflectionRenderer.refCamera.CalculateObliqueMatrix(CameraSpacePlane(internalReflectionRenderer.refCamera, base.transform.position, base.transform.up)); } if (!SRPMode) { if (!Application.isPlaying || Settings.targetFramerate == 0) { internalReflectionRenderer.refCamera.Render(); } else if (srcCamera.cameraType != CameraType.SceneView && Application.isPlaying && Time.realtimeSinceStartup > frameTime) { frameTime = Time.realtimeSinceStartup + 1f / (float)Settings.targetFramerate; internalReflectionRenderer.refCamera.Render(); } } else { if (!Application.isPlaying || Settings.targetFramerate == 0) { internalReflectionRenderer.refCamera.enabled = true; } else if (Time.realtimeSinceStartup > frameTime) { frameTime = Time.realtimeSinceStartup + 1f / (float)Settings.targetFramerate; internalReflectionRenderer.refCamera.enabled = true; } internalReflectionRenderer.refCamera.depth = -999f; } if (Settings.useDepth && (bool)internalDepthShader && !SRPMode) { if (!externalReflectionDepth) { if ((bool)reflectionDepth) { RenderTexture.ReleaseTemporary(reflectionDepth); } int width3 = (int)(((Settings.resolutionMode == ResolutionMode.ExplicitValue) ? Settings.explicitResolution.x : ((float)srcCamera.pixelWidth)) * Settings.resolutionDownscale); int height3 = (int)(((Settings.resolutionMode == ResolutionMode.ExplicitValue) ? Settings.explicitResolution.y : ((float)srcCamera.pixelHeight)) * Settings.resolutionDownscale); reflectionDepth = RenderTexture.GetTemporary(width3, height3, 0, RenderTextureFormat.ARGBFloat, RenderTextureReadWrite.Linear); reflectionDepth.name = "PIDI_REFDEPTH" + ((srcCamera.cameraType == CameraType.SceneView) ? "SCENE" : "GAME"); if (!rTex.Contains(reflectionDepth)) { rTex.Add(reflectionDepth); } } internalReflectionRenderer.refCamera.targetTexture = ReflectionDepth; internalReflectionRenderer.refCamera.backgroundColor = Color.green; internalReflectionRenderer.refCamera.clearFlags = CameraClearFlags.Color; Shader.SetGlobalVector("_Planar3DepthPlaneOrigin", new Vector4(base.transform.position.x, base.transform.position.y, base.transform.position.z)); Shader.SetGlobalVector("_Planar3DepthPlaneNormal", -new Vector4(base.transform.up.x, base.transform.up.y, base.transform.up.z)); internalReflectionRenderer.refCamera.renderingPath = RenderingPath.Forward; internalReflectionRenderer.refCamera.RenderWithShader(internalDepthShader, ""); } QualitySettings.shadowDistance = shadowDistance; } public void LateUpdate() { castersActive = false; Camera[] array = new Camera[gameReflectors.Keys.Count]; gameReflectors.Keys.CopyTo(array, 0); for (int i = 0; i < array.Length; i++) { if (array[i] != null) { Vector3 forward = array[i].transform.forward; Vector3 up = array[i].transform.up; Vector3 position = array[i].transform.position; Vector3 direction = base.transform.InverseTransformDirection(forward); Vector3 direction2 = base.transform.InverseTransformDirection(up); Vector3 position2 = base.transform.InverseTransformPoint(position); direction.y *= -1f; direction2.y *= -1f; position2.y *= -1f; forward = base.transform.TransformDirection(direction); up = base.transform.TransformDirection(direction2); position = base.transform.TransformPoint(position2); if (!gameReflectors[array[i]].refCamera) { continue; } gameReflectors[array[i]].refCamera.transform.position = position; gameReflectors[array[i]].refCamera.transform.LookAt(position + forward, up); if (!Settings.useCustomComponents) { continue; } List list = new List(Settings.customComponentNames); Component[] components = base.gameObject.GetComponents(typeof(Component)); foreach (Component component in components) { if (!list.Contains(component.GetType().Name)) { continue; } if (!gameReflectors[array[i]].refCamera.GetComponent(component.GetType())) { Component obj = gameReflectors[array[i]].refCamera.gameObject.AddComponent(component.GetType()); FieldInfo[] fields = component.GetType().GetFields(); foreach (FieldInfo fieldInfo in fields) { fieldInfo.SetValue(obj, fieldInfo.GetValue(component)); } } else if (Settings.autoSynchComponents) { Component obj2 = gameReflectors[array[i]].refCamera.gameObject.AddComponent(component.GetType()); FieldInfo[] fields = component.GetType().GetFields(); foreach (FieldInfo fieldInfo2 in fields) { fieldInfo2.SetValue(obj2, fieldInfo2.GetValue(component)); } } } } else { gameReflectors.Remove(array[i]); } } } public void OnDisable() { foreach (RenderTexture item in rTex) { RenderTexture.ReleaseTemporary(item); } if (SRPMode) { RenderPipelineManager.beginFrameRendering -= SRPRenderReflection; RenderPipelineManager.beginFrameRendering -= CleanupTextures; } else { Camera.onPreCull = (Camera.CameraCallback)Delegate.Remove(Camera.onPreCull, new Camera.CameraCallback(RenderReflection)); } Camera[] array = Resources.FindObjectsOfTypeAll(); foreach (Camera camera in array) { if (camera.name.Contains("PLANAR3_")) { if ((bool)camera.targetTexture && rTex.Contains(camera.targetTexture)) { RenderTexture.ReleaseTemporary(camera.targetTexture); } UnityEngine.Object.DestroyImmediate(camera.gameObject); } } } private Vector4 CameraSpacePlane(Camera forCamera, Vector3 planeCenter, Vector3 planeNormal) { Matrix4x4 worldToCameraMatrix = forCamera.worldToCameraMatrix; Vector3 lhs = worldToCameraMatrix.MultiplyPoint(planeCenter); Vector3 rhs = worldToCameraMatrix.MultiplyVector(planeNormal).normalized * 1f; return new Vector4(rhs.x, rhs.y, rhs.z, 0f - Vector3.Dot(lhs, rhs)); } } }