namespace PlanarReflections4 { /* * PIDI - Planar Reflections™ 4 - Copyright© 2017-2021 * PIDI - Planar Reflections is a trademark and copyrighted property of Jorge Pinal Negrete. * You cannot sell, redistribute, share nor make public this code, modified or not, in part nor in whole, through any * means on any platform except with the purpose of contacting the developers to request support and only when taking * all pertinent measures to avoid its release to the public and / or any unrelated third parties. * Modifications are allowed only for internal use within the limits of your Unity based projects and cannot be shared, * published, redistributed nor made available to any third parties unrelated to Irreverent Software by any means. * * For more information, contact us at support@irreverent-software.com * */ using UnityEngine.Rendering; using UnityEngine.Rendering.Universal; using System.Collections; using System.Collections.Generic; using UnityEngine; #if UNITY_POST_PROCESSING_STACK_V2 using UnityEngine.Rendering.PostProcessing; #endif [System.Serializable] public class PlanarReflectionSettings { public float nearClipPlane = 0.03f; public float farClipPlane = 1000; public LayerMask reflectLayers = 1; [Range( 0, 1 )] public float customLODBias = 1.0f; public int maxLODLevel; public bool renderShadows = true; public bool usePostFX = true; public LayerMask postFXVolumeMask; public bool accurateMatrix = true; public string camerasTag; public bool screenBasedResolution = true; public Vector2 explicitResolution = new Vector2( 512, 512 ); [Range( 0.1f, 2.0f )] public float outputResolutionMultiplier = 1.0f; [Range( 0, 60 )] public int reflectionFramerate = 0; public bool useMipMaps = true; public bool useAntialiasing = false; public bool clearToColor = false; public Color backgroundColor = Color.blue; public bool renderDepth = false; public bool renderFog; public int fogRendererIndex = 1; public bool forceHDR = true; public bool updateOnCastOnly = true; public bool framerateByDistance = true; public float framerateThreshold = 20; public Material customSkybox; #if UPDATE_PLANAR3 #region LEGACY_API /// /// This is a legacy method and will be deprecated soon. Please use nearClipPlane instead. ///
Controls the near clip plane value of the virtual camera that renders the reflection. When using accurate matrices it is ignored and the reflective surface's plane is used instead.
///
public float nearClipDistance { get { return nearClipPlane; } set { nearClipPlane = value; } } public float farClipDistance { get { return farClipPlane; } set { farClipPlane = value; } } public bool customShadowDistance { get { return renderShadows; } } public float shadowDistance { get { return 50f; } } public int targetFramerate { get { return reflectionFramerate; } set { reflectionFramerate = value; } } public bool useDepth { get { return renderDepth; } set { renderDepth = value; } } public string CamerasTag { get { return camerasTag; } set { camerasTag = value; } } public bool trackCamerasWithTag { get { return !string.IsNullOrEmpty( camerasTag ); } } public PlanarReflections3.ReflectionClipMode reflectionClipMode { get { return accurateMatrix ? PlanarReflections3.ReflectionClipMode.AccurateClipping : PlanarReflections3.ReflectionClipMode.SimpleApproximation; } set { accurateMatrix = value == PlanarReflections3.ReflectionClipMode.AccurateClipping; } } public PlanarReflections3.ResolutionMode resolutionMode { get { return screenBasedResolution ? PlanarReflections3.ResolutionMode.ScreenBased : PlanarReflections3.ResolutionMode.ExplicitValue; } set { screenBasedResolution = value == PlanarReflections3.ResolutionMode.ScreenBased; } } public bool useCustomClearFlags { get { return true; } } public int clearFlags { get { return clearToColor ? 1 : 0; } set { clearToColor = value == 1; } } public bool forceFloatOutput { get { return forceHDR; } set { forceHDR = value; } } #endregion #endif #if UPDATE_PLANAR3 public static implicit operator PlanarReflectionSettings( PlanarReflections3.ReflectionSettings source ) { var newSettings = new PlanarReflectionSettings(); newSettings.nearClipPlane = source.nearClipDistance; newSettings.farClipPlane = source.farClipDistance; newSettings.reflectLayers = source.reflectLayers; newSettings.renderShadows = !source.customShadowDistance || source.shadowDistance > 0.1f; newSettings.reflectionFramerate = source.targetFramerate; newSettings.usePostFX = source.usePostFX; newSettings.explicitResolution = source.explicitResolution; newSettings.renderDepth = source.useDepth; newSettings.useMipMaps = source.useMipMaps; newSettings.useAntialiasing = source.useAntialiasing; newSettings.clearToColor = source.useCustomClearFlags && source.clearFlags == 1; newSettings.camerasTag = source.trackCamerasWithTag ? source.CamerasTag : ""; newSettings.outputResolutionMultiplier = source.resolutionDownscale; newSettings.forceHDR = source.forceFloatOutput; newSettings.backgroundColor = source.backgroundColor; newSettings.accurateMatrix = source.reflectionClipMode == PlanarReflections3.ReflectionClipMode.AccurateClipping; newSettings.screenBasedResolution = source.resolutionMode == PlanarReflections3.ResolutionMode.ScreenBased; return newSettings; } #endif } [System.Serializable] public class ReflectionData { protected Camera _reflectionCam; public Camera ReflectionCamera { get { return _reflectionCam; } } public UniversalAdditionalCameraData UniversalData; public RenderTexture _reflectionTex; public RenderTexture _reflectionDepth; public RenderTexture _reflectionFog; public Vector2Int screenRes; public int recursionLevel = 0; public float timer; public void ForceSetCamera( Camera cam ) { _reflectionCam = cam; UniversalData = _reflectionCam.GetUniversalAdditionalCameraData(); } public ReflectionData( Camera cam, PlanarReflectionSettings settings ) { _reflectionCam = cam; _reflectionTex = RenderTexture.GetTemporary( 1, 1 ); _reflectionDepth = RenderTexture.GetTemporary( 1, 1 ); _reflectionFog = RenderTexture.GetTemporary( 1, 1 ); screenRes = new Vector2Int( Screen.width, Screen.height ); UniversalData = _reflectionCam.GetUniversalAdditionalCameraData(); RegenerateTextures( settings ); } public void RegenerateTextures( PlanarReflectionSettings settings ) { RenderTexture.ReleaseTemporary( _reflectionTex ); RenderTexture.ReleaseTemporary( _reflectionDepth ); RenderTexture.ReleaseTemporary( _reflectionFog ); var rd = new RenderTextureDescriptor( Mathf.RoundToInt( settings.outputResolutionMultiplier * ( settings.screenBasedResolution ? Screen.width : settings.explicitResolution.x ) ), Mathf.RoundToInt( settings.outputResolutionMultiplier * ( settings.screenBasedResolution ? Screen.height : settings.explicitResolution.y ) ) ); rd.useMipMap = settings.useMipMaps; rd.msaaSamples = 1; rd.colorFormat = settings.forceHDR ? RenderTextureFormat.DefaultHDR : RenderTextureFormat.Default; rd.depthBufferBits = 16; rd.autoGenerateMips = true; rd.volumeDepth = 1; rd.vrUsage = VRTextureUsage.None; rd.dimension = UnityEngine.Rendering.TextureDimension.Tex2D; rd.mipCount = 6; _reflectionTex = RenderTexture.GetTemporary( rd ); _reflectionTex.filterMode = FilterMode.Bilinear; if ( settings.renderDepth ) { rd.colorFormat = RenderTextureFormat.Depth; _reflectionDepth = RenderTexture.GetTemporary( rd ); _reflectionDepth.filterMode = FilterMode.Bilinear; _reflectionDepth.name = "_REFDEPTHP4"; } else { RenderTexture.ReleaseTemporary( _reflectionDepth ); } if ( settings.renderFog ) { rd.colorFormat = RenderTextureFormat.Default; _reflectionFog = RenderTexture.GetTemporary( rd ); _reflectionFog.filterMode = FilterMode.Bilinear; _reflectionFog.name = "_REFFOGP4"; } else { RenderTexture.ReleaseTemporary( _reflectionFog ); } screenRes = new Vector2Int( Screen.width, Screen.height ); _reflectionTex.name = "_REFTEXP4"; } } [HelpURL( "https://irreverent-software.com/docs/pidi-planar-reflections-4/getting-started/installation/" )] [ExecuteAlways] public class PlanarReflectionRenderer : MonoBehaviour { #if UNITY_POST_PROCESSING_STACK_V2 public PostProcessResources internalPostFXResources; #endif #if UNITY_EDITOR public Mesh defaultReflectorMesh; public Material defaultReflectorMaterial; public bool showPreviewReflector; public string Version { get { return "4.1.0"; } } #endif [SerializeField] protected PlanarReflectionSettings _settings = new PlanarReflectionSettings(); public PlanarReflectionSettings Settings { get { return _settings; } } public RenderTexture externalReflectionTex; public RenderTexture externalReflectionDepth; protected Dictionary _reflectionData = new Dictionary(); private List _reflectionCameras = new List( new Camera[1] ); private Camera[] updateCams = new Camera[0]; #if UNITY_EDITOR protected ReflectionData _sceneReflection; private MaterialPropertyBlock _sceneReflectorMatBlock; #endif public Texture GetReflection( Camera cam ) { if ( cam.cameraType == CameraType.SceneView ) { #if UNITY_EDITOR if ( externalReflectionTex ) { return _sceneReflection != null && _sceneReflection._reflectionTex != null ? (Texture)externalReflectionTex : Texture2D.blackTexture; } return _sceneReflection != null && _sceneReflection._reflectionTex != null ? (Texture)_sceneReflection._reflectionTex : Texture2D.blackTexture; #else return Texture2D.blackTexture; #endif } else { if ( externalReflectionTex ) { return _reflectionData.ContainsKey( cam ) ? (Texture)externalReflectionTex : Texture2D.blackTexture; } if ( _reflectionData.ContainsKey( cam ) ) { return _reflectionData[cam]._reflectionTex; } else { return Texture2D.blackTexture; } } } public Texture GetReflectionDepth( Camera cam ) { if ( cam.cameraType == CameraType.SceneView ) { #if UNITY_EDITOR return _sceneReflection != null && _sceneReflection._reflectionDepth != null ? (Texture)_sceneReflection._reflectionDepth : Texture2D.whiteTexture; #else return Texture2D.blackTexture; #endif } else { if ( _reflectionData.ContainsKey( cam ) ) { return _reflectionData[cam]._reflectionDepth; } else { return Texture2D.blackTexture; } } } public Texture GetReflectionFog( Camera cam ) { if ( cam.cameraType == CameraType.SceneView ) { #if UNITY_EDITOR return _sceneReflection != null && _sceneReflection._reflectionFog != null ? (Texture)_sceneReflection._reflectionFog : Texture2D.blackTexture; #else return Texture2D.blackTexture; #endif } else { if ( _reflectionData.ContainsKey( cam ) ) { return _reflectionData[cam]._reflectionFog; } else { return Texture2D.blackTexture; } } } #if UNITY_EDITOR [UnityEditor.MenuItem( "GameObject/Effects/Planar Reflections 4/Create Reflections Renderer", priority = -99 )] public static void CreateReflectionsRendererObject() { var reflector = new GameObject( "Reflection Renderer", typeof( PlanarReflectionRenderer ) ); reflector.transform.position = Vector3.zero; reflector.transform.rotation = Quaternion.identity; } [UnityEditor.Callbacks.DidReloadScripts] private static void OnScriptsReloaded() { var cams = Resources.FindObjectsOfTypeAll(); foreach ( Camera cam in cams ) { if ( cam.name.Contains( "_URPReflectionCamera" ) ) { #if !UNITY_2018_3_OR_NEWER DestroyImmediate( cam.targetTexture ); #else RenderTexture.ReleaseTemporary( cam.targetTexture ); #endif cam.targetTexture = null; DestroyImmediate( cam.gameObject ); } } } #endif public void OnEnable() { _reflectionCameras = new List( new Camera[1] ); #if UNITY_EDITOR #if UNITY_2018_1_OR_NEWER UnityEditor.EditorApplication.QueuePlayerLoopUpdate(); UnityEditor.SceneView.RepaintAll(); #endif UnityEditor.Undo.undoRedoPerformed += ApplySettings; if ( UnityEditor.AssetDatabase.FindAssets( "Planar4Logo_Gizmos" ).Length < 1 ) { var sceneIcon = UnityEditor.AssetDatabase.LoadAssetAtPath( UnityEditor.AssetDatabase.GUIDToAssetPath( UnityEditor.AssetDatabase.FindAssets( "l: Pidi_PlanarGizmos" )[0] ) ); if ( !UnityEditor.AssetDatabase.IsValidFolder( "Assets/Gizmos" ) ) UnityEditor.AssetDatabase.CreateFolder( "Assets", "Gizmos" ); var t = new Texture2D( sceneIcon.width, sceneIcon.height ); t.SetPixels( sceneIcon.GetPixels() ); System.IO.File.WriteAllBytes( Application.dataPath + "/Gizmos/Planar4Logo_Gizmos.png", t.EncodeToPNG() ); UnityEditor.AssetDatabase.Refresh(); var importer = (UnityEditor.TextureImporter)UnityEditor.AssetImporter.GetAtPath( "Assets/Gizmos/Planar4Logo_Gizmos.png" ); importer.isReadable = true; importer.textureType = UnityEditor.TextureImporterType.GUI; importer.SaveAndReimport(); UnityEditor.AssetDatabase.Refresh(); } #endif #if UPDATE_PLANAR3 if ( GetComponent() ) { _settings = GetComponent().Settings; } #endif //Camera.onPreCull += RenderReflection; RenderPipelineManager.beginCameraRendering -= RenderURPReflection; RenderPipelineManager.beginCameraRendering += RenderURPReflection; } public void OnDisable() { #if UNITY_EDITOR UnityEditor.Undo.undoRedoPerformed -= ApplySettings; #endif UnityEngine.Rendering.RenderPipelineManager.beginCameraRendering -= RenderURPReflection; foreach ( KeyValuePair pair in _reflectionData ) { RenderTexture.ReleaseTemporary( pair.Value._reflectionTex ); RenderTexture.ReleaseTemporary( pair.Value._reflectionDepth ); RenderTexture.ReleaseTemporary( pair.Value._reflectionFog ); } var cams = _reflectionCameras.ToArray(); for ( int i = 0; i < cams.Length; i++ ) { if ( cams[i] ) { DestroyImmediate( cams[i].gameObject ); } } _reflectionCameras.Clear(); _reflectionCameras.Add( null ); _reflectionData.Clear(); #if UNITY_EDITOR _sceneReflection = default; #endif } public void ApplySettings() { foreach ( KeyValuePair pair in _reflectionData ) { _reflectionData[pair.Key].RegenerateTextures( _settings ); } #if UNITY_EDITOR if ( _sceneReflection != null ) _sceneReflection.RegenerateTextures( _settings ); #endif } public void RenderURPReflection( ScriptableRenderContext context, Camera cam ) { if ( !_reflectionData.ContainsKey( cam ) #if UNITY_EDITOR || _sceneReflection == null || !_sceneReflection.ReflectionCamera #endif ) { #if UNITY_EDITOR if ( cam.cameraType == CameraType.SceneView ) { if ( _sceneReflection == null ) { var rCam = new GameObject( "_URPSceneReflectionCamera", typeof( Camera ), typeof( Skybox ), typeof( UniversalAdditionalCameraData ) ); _reflectionCameras[0] = rCam.GetComponent(); rCam.hideFlags = HideFlags.HideAndDontSave; rCam.GetComponent().enabled = false; rCam.GetComponent().cameraType = CameraType.Game; #if UNITY_POST_PROCESSING_STACK_V2 rCam.AddComponent(); rCam.GetComponent().Init( internalPostFXResources ); rCam.GetComponent().enabled = false; #endif _sceneReflection = new ReflectionData( _reflectionCameras[0], _settings ); } else { if ( !_sceneReflection.ReflectionCamera ) { var rCam = new GameObject( "_URPSceneReflectionCamera", typeof( Camera ), typeof( Skybox ), typeof( UniversalAdditionalCameraData ) ); _reflectionCameras[0] = rCam.GetComponent(); #if UNITY_POST_PROCESSING_STACK_V2 rCam.AddComponent(); rCam.GetComponent().Init( internalPostFXResources ); rCam.GetComponent().enabled = false; #endif rCam.hideFlags = HideFlags.HideAndDontSave; rCam.GetComponent().enabled = false; rCam.GetComponent().cameraType = CameraType.Game; } _sceneReflection.ForceSetCamera( _reflectionCameras[0] ); } //var rCam = new GameObject( "_URPReflectionCamera", typeof( Camera ), typeof( Skybox ), typeof(UniversalAdditionalCameraData) ); } else #endif if ( cam.cameraType == CameraType.Game && !_reflectionData.ContainsKey( cam ) && !cam.gameObject.name.Contains( "ReflectionCamera" ) && cam.gameObject.hideFlags == HideFlags.None ) { var rCam = new GameObject( "_URPGameReflectionCamera", typeof( Camera ), typeof( Skybox ), typeof( UniversalAdditionalCameraData ) ); _reflectionCameras.Add( rCam.GetComponent() ); rCam.hideFlags = HideFlags.HideAndDontSave; rCam.GetComponent().enabled = false; rCam.GetComponent().cameraType = CameraType.Game; _reflectionData.Add( cam, new ReflectionData( rCam.GetComponent(), _settings ) ); } #if UNITY_EDITOR if ( cam.cameraType == CameraType.SceneView ) { if ( _sceneReflection == null ) { _sceneReflection = new ReflectionData( _reflectionCameras[0], _settings ); } else { _sceneReflection.ForceSetCamera( _reflectionCameras[0] ); } } else # endif if ( cam.cameraType == CameraType.Game && !_reflectionData.ContainsKey( cam ) && !cam.gameObject.name.Contains( "ReflectionCamera" ) && cam.gameObject.hideFlags == HideFlags.None ) { _reflectionData.Add( cam, new ReflectionData( _reflectionCameras[0], _settings ) ); } else if ( cam.gameObject.hideFlags != HideFlags.None ) { return; } } Plane reflectionPlane = new Plane( transform.up, transform.position ); if ( Mathf.Abs( Vector3.Dot( transform.up, cam.transform.forward ) ) < 0.01f && ( cam.orthographic || reflectionPlane.GetDistanceToPoint( cam.transform.position ) < 0.025f ) ) { return; } #if UNITY_EDITOR var isSceneCam = cam.cameraType == CameraType.SceneView; if ( !_reflectionData.ContainsKey( cam ) && !isSceneCam ) { return; } if ( isSceneCam ) { if ( Screen.width != _sceneReflection.screenRes.x || Screen.height != _sceneReflection.screenRes.y ) { _sceneReflection.RegenerateTextures( _settings ); } } else { if ( Screen.width != _reflectionData[cam].screenRes.x || Screen.height != _reflectionData[cam].screenRes.y ) { _reflectionData[cam].RegenerateTextures( _settings ); } } var currentData = isSceneCam ? _sceneReflection : _reflectionData[cam]; #else if ( Screen.width != _reflectionData[cam].screenRes.x || Screen.height != _reflectionData[cam].screenRes.y ) { _reflectionData[cam].RegenerateTextures( _settings ); } var currentData = _reflectionData[cam]; #endif var refCamera = currentData.ReflectionCamera; refCamera.CopyFrom( cam ); refCamera.allowHDR = cam.allowHDR; refCamera.allowMSAA = false; refCamera.useOcclusionCulling = false; refCamera.cullingMask = _settings.reflectLayers; refCamera.clearFlags = _settings.clearToColor ? CameraClearFlags.SolidColor : CameraClearFlags.Skybox; refCamera.backgroundColor = _settings.backgroundColor; refCamera.cameraType = CameraType.Game; refCamera.renderingPath = cam.renderingPath; refCamera.useOcclusionCulling = false; refCamera.targetTexture = externalReflectionTex ? externalReflectionTex : currentData._reflectionTex; Skybox skyComp; if ( refCamera.TryGetComponent( out skyComp ) ) { skyComp.material = _settings.customSkybox; } #if UNITY_POST_PROCESSING_STACK_V2 var uData = refCamera.GetUniversalAdditionalCameraData(); PostProcessLayer postFX; if ( refCamera.TryGetComponent( out postFX ) ) { postFX.enabled = _settings.usePostFX; postFX.volumeLayer = _settings.postFXVolumeMask; } #else var uData = refCamera.GetUniversalAdditionalCameraData(); #endif Vector3 worldSpaceViewDir = cam.transform.forward; Vector3 worldSpaceViewUp = cam.transform.up; Vector3 worldSpaceCamPos = cam.transform.position; Vector3 planeSpaceViewDir = transform.InverseTransformDirection( worldSpaceViewDir ); Vector3 planeSpaceViewUp = transform.InverseTransformDirection( worldSpaceViewUp ); Vector3 planeSpaceCamPos = transform.InverseTransformPoint( worldSpaceCamPos ); planeSpaceViewDir.y *= -1.0f; planeSpaceViewUp.y *= -1.0f; planeSpaceCamPos.y *= -1.0f; worldSpaceViewDir = transform.TransformDirection( planeSpaceViewDir ); worldSpaceViewUp = transform.TransformDirection( planeSpaceViewUp ); worldSpaceCamPos = transform.TransformPoint( planeSpaceCamPos ); refCamera.transform.position = worldSpaceCamPos; refCamera.transform.LookAt( worldSpaceCamPos + worldSpaceViewDir, worldSpaceViewUp ); refCamera.nearClipPlane = _settings.nearClipPlane; refCamera.farClipPlane = _settings.farClipPlane; refCamera.rect = new Rect( 0, 0, 1, 1 ); refCamera.aspect = cam.aspect; if ( _settings.accurateMatrix ) { refCamera.projectionMatrix = refCamera.CalculateObliqueMatrix( CameraSpacePlane( refCamera, transform.position, transform.up ) ); } var tempLOD = QualitySettings.lodBias; var maxLod = QualitySettings.maximumLODLevel; QualitySettings.lodBias *= _settings.customLODBias; QualitySettings.maximumLODLevel = _settings.maxLODLevel; uData.renderShadows = _settings.renderShadows; uData.volumeLayerMask = _settings.postFXVolumeMask; uData.antialiasing = _settings.useAntialiasing ? AntialiasingMode.FastApproximateAntialiasing : AntialiasingMode.None; if ( #if UNITY_EDITOR !Application.isPlaying || #endif ( Time.realtimeSinceStartup > currentData.timer ) ) { if ( _settings.renderFog ) { refCamera.targetTexture = currentData._reflectionFog; #if UNITY_POST_PROCESSING_STACK_V2 if (postFX) postFX.enabled = false; #else uData.renderPostProcessing = false; #endif uData.SetRenderer( _settings.fogRendererIndex ); UniversalRenderPipeline.RenderSingleCamera( context, refCamera ); uData.SetRenderer( 0 ); } if ( _settings.renderDepth ) { refCamera.targetTexture = currentData._reflectionDepth; refCamera.depthTextureMode = DepthTextureMode.Depth; #if UNITY_POST_PROCESSING_STACK_V2 if (postFX) postFX.enabled = false; #else uData.renderPostProcessing = false; #endif uData.requiresColorOption = CameraOverrideOption.Off; uData.renderShadows = false; UniversalRenderPipeline.RenderSingleCamera( context, refCamera ); #if UNITY_EDITOR if ( isSceneCam ) { uData.renderPostProcessing = false; } else { #endif #if UNITY_POST_PROCESSING_STACK_V2 if (!postFX) #endif uData.renderPostProcessing = _settings.usePostFX; #if UNITY_EDITOR } #endif uData.volumeLayerMask = _settings.postFXVolumeMask; } refCamera.targetTexture = currentData._reflectionTex; #if UNITY_EDITOR if ( isSceneCam ) { uData.renderPostProcessing = false; } else { #endif uData.renderPostProcessing = _settings.usePostFX; #if UNITY_EDITOR } #endif uData.volumeLayerMask = _settings.postFXVolumeMask; uData.requiresDepthOption = CameraOverrideOption.Off; uData.requiresColorOption = CameraOverrideOption.Off; if ( _settings.usePostFX #if UNITY_EDITOR && !isSceneCam #endif ) { refCamera.enabled = true; } else { refCamera.enabled = false; UniversalRenderPipeline.RenderSingleCamera( context, refCamera ); } } QualitySettings.maximumLODLevel = maxLod; QualitySettings.lodBias = tempLOD; if ( #if UNITY_EDITOR Application.isPlaying && #endif Time.realtimeSinceStartup > currentData.timer && _settings.reflectionFramerate > 0 ) { currentData.timer = Time.realtimeSinceStartup + ( 1.0f / _settings.reflectionFramerate ); } #if UNITY_EDITOR if ( cam.cameraType == CameraType.SceneView && showPreviewReflector ) DrawReflectorMesh( cam, currentData ); #endif } private void LateUpdate() { if ( _settings.usePostFX ) { if ( updateCams.Length != _reflectionData.Keys.Count ) { updateCams = new Camera[_reflectionData.Keys.Count]; } _reflectionData.Keys.CopyTo( updateCams, 0 ); for ( int c = 0; c < updateCams.Length; c++ ) { if ( updateCams[c] != null ) { Vector3 worldSpaceViewDir = updateCams[c].transform.forward; Vector3 worldSpaceViewUp = updateCams[c].transform.up; Vector3 worldSpaceCamPos = updateCams[c].transform.position; Vector3 planeSpaceViewDir = transform.InverseTransformDirection( worldSpaceViewDir ); Vector3 planeSpaceViewUp = transform.InverseTransformDirection( worldSpaceViewUp ); Vector3 planeSpaceCamPos = transform.InverseTransformPoint( worldSpaceCamPos ); planeSpaceViewDir.y *= -1.0f; planeSpaceViewUp.y *= -1.0f; planeSpaceCamPos.y *= -1.0f; worldSpaceViewDir = transform.TransformDirection( planeSpaceViewDir ); worldSpaceViewUp = transform.TransformDirection( planeSpaceViewUp ); worldSpaceCamPos = transform.TransformPoint( planeSpaceCamPos ); if ( _reflectionData[updateCams[c]].ReflectionCamera ) { _reflectionData[updateCams[c]].ReflectionCamera.transform.position = worldSpaceCamPos; _reflectionData[updateCams[c]].ReflectionCamera.transform.LookAt( worldSpaceCamPos + worldSpaceViewDir, worldSpaceViewUp ); } } } } } private Vector4 CameraSpacePlane( Camera forCamera, Vector3 planeCenter, Vector3 planeNormal ) { Vector3 offsetPos = planeCenter; Matrix4x4 mtx = forCamera.worldToCameraMatrix; Vector3 cPos = mtx.MultiplyPoint( offsetPos ); Vector3 cNormal = mtx.MultiplyVector( planeNormal ).normalized * 1; return new Vector4( cNormal.x, cNormal.y, cNormal.z, -Vector3.Dot( cPos, cNormal ) ); } #if UNITY_EDITOR private void DrawReflectorMesh( Camera sceneCam, ReflectionData data ) { var matrix = new Matrix4x4(); matrix.SetTRS( transform.position, transform.rotation, Vector3.one * 10 ); if ( _sceneReflectorMatBlock == null ) { _sceneReflectorMatBlock = new MaterialPropertyBlock(); } _sceneReflectorMatBlock.SetTexture( "_ReflectionTex", data._reflectionTex ? (Texture)data._reflectionTex : (Texture)Texture2D.blackTexture ); Graphics.DrawMesh( defaultReflectorMesh, matrix, defaultReflectorMaterial, 0, sceneCam, 0, _sceneReflectorMatBlock ); } public void OnDrawGizmos() { Gizmos.matrix = Matrix4x4.TRS( transform.position, transform.rotation, Vector3.one ); Gizmos.DrawIcon( transform.position + transform.rotation * Vector3.up, "Planar4Logo_Gizmos.png" ); Gizmos.color = Color.clear; Gizmos.DrawCube( Vector3.zero, new Vector3( 1, 0.01f, 1 ) * 10 ); Gizmos.color = Color.cyan; Gizmos.DrawWireCube( Vector3.zero, new Vector3( 1, 0, 1 ) * 10 ); Gizmos.matrix = Matrix4x4.TRS( Vector3.zero, Quaternion.identity, Vector3.one ); } public void OnDrawGizmosSelected() { } #endif } }