using UnityEngine; using System.Collections; namespace VLB { [ExecuteInEditMode] [HelpURL(Consts.HelpUrlDynamicOcclusionDepthBuffer)] public class DynamicOcclusionDepthBuffer : DynamicOcclusionAbstractBase { /// /// The beam can only be occluded by objects located on the layers matching this mask. /// It's very important to set it as restrictive as possible (checking only the layers which are necessary) /// to perform a more efficient process in order to increase the performance. /// It should NOT include the layer on which the beams are generated. /// public LayerMask layerMask = Consts.DynOcclusionLayerMaskDefault; /// /// Whether or not the virtual camera will use occlusion culling during rendering from the beam's POV. /// public bool useOcclusionCulling = Consts.DynOcclusionDepthBufferOcclusionCullingDefault; /// /// Controls how large the depth texture captured by the virtual camera is. /// The lower the resolution, the better the performance, but the less accurate the rendering. /// public int depthMapResolution = Consts.DynOcclusionDepthBufferDepthMapResolutionDefault; /// /// Fade out the beam before the occlusion surface in order to soften the transition. /// public float fadeDistanceToSurface = Consts.DynOcclusionFadeDistanceToSurfaceDefault; protected override string GetShaderKeyword() { return "VLB_OCCLUSION_DEPTH_TEXTURE"; } protected override MaterialManager.DynamicOcclusion GetDynamicOcclusionMode() { return MaterialManager.DynamicOcclusion.DepthTexture; } Camera m_DepthCamera = null; bool m_NeedToUpdateOcclusionNextFrame = false; void ProcessOcclusionInternal() { UpdateDepthCameraPropertiesAccordingToBeam(); m_DepthCamera.Render(); } protected override bool OnProcessOcclusion(ProcessOcclusionSource source) { Debug.Assert(m_Master && m_DepthCamera); if (source == ProcessOcclusionSource.RenderLoop && SRPHelper.IsUsingCustomRenderPipeline()) // Recursive rendering is not supported on SRP m_NeedToUpdateOcclusionNextFrame = true; else ProcessOcclusionInternal(); return true; } void Update() { if (m_NeedToUpdateOcclusionNextFrame && m_Master && m_DepthCamera) { ProcessOcclusionInternal(); m_NeedToUpdateOcclusionNextFrame = false; } } void UpdateDepthCameraPropertiesAccordingToBeam() { Debug.Assert(m_Master); Debug.Assert(m_DepthCamera); float apexDist = m_Master.coneApexOffsetZ; m_DepthCamera.transform.localPosition = m_Master.beamLocalForward * (-apexDist); m_DepthCamera.transform.localRotation = m_Master.beamInternalLocalRotation; var scale = m_Master.lossyScale; if (!Mathf.Approximately(scale.y * scale.z, 0)) { const float kMinNearClipPlane = 0.1f; // should be the same than in shader m_DepthCamera.nearClipPlane = Mathf.Max(apexDist, kMinNearClipPlane) * scale.z; m_DepthCamera.farClipPlane = (m_Master.maxGeometryDistance + apexDist) * scale.z; m_DepthCamera.aspect = scale.x / scale.y; m_DepthCamera.fieldOfView = scale.y * m_Master.coneAngle / scale.z; } } public bool HasLayerMaskIssues() { if(Config.Instance.geometryOverrideLayer) { int layerBit = 1 << Config.Instance.geometryLayerID; return ((layerMask.value & layerBit) == layerBit); } return false; } protected override void OnValidateProperties() { base.OnValidateProperties(); depthMapResolution = Mathf.Clamp(Mathf.NextPowerOfTwo(depthMapResolution), 8, 2048); fadeDistanceToSurface = Mathf.Max(fadeDistanceToSurface, 0f); } void InstantiateOrActivateDepthCamera() { if (m_DepthCamera != null) { m_DepthCamera.gameObject.SetActive(true); // active it in case it has been disabled by OnDisable() } else { m_DepthCamera = new GameObject("Depth Camera").AddComponent(); if (m_DepthCamera && m_Master) { m_DepthCamera.enabled = false; m_DepthCamera.cullingMask = layerMask; m_DepthCamera.clearFlags = CameraClearFlags.Depth; m_DepthCamera.depthTextureMode = DepthTextureMode.Depth; m_DepthCamera.renderingPath = RenderingPath.VertexLit; // faster m_DepthCamera.useOcclusionCulling = useOcclusionCulling; m_DepthCamera.gameObject.hideFlags = Consts.ProceduralObjectsHideFlags; m_DepthCamera.transform.SetParent(transform, false); var rt = new RenderTexture(depthMapResolution, depthMapResolution, 16, RenderTextureFormat.Depth); m_DepthCamera.targetTexture = rt; UpdateDepthCameraPropertiesAccordingToBeam(); #if UNITY_EDITOR UnityEditor.GameObjectUtility.SetStaticEditorFlags(m_DepthCamera.gameObject, m_Master.GetStaticEditorFlagsForSubObjects()); m_DepthCamera.gameObject.SetSameSceneVisibilityStatesThan(m_Master.gameObject); #endif } } } protected override void OnEnablePostValidate() { InstantiateOrActivateDepthCamera(); } protected override void OnDisable() { base.OnDisable(); if (m_DepthCamera) m_DepthCamera.gameObject.SetActive(false); } protected override void Awake() { base.Awake(); #if UNITY_EDITOR MarkMaterialAsDirty(); #endif } protected override void OnDestroy() { base.OnDestroy(); DestroyDepthCamera(); #if UNITY_EDITOR MarkMaterialAsDirty(); #endif } void DestroyDepthCamera() { if (m_DepthCamera) { if (m_DepthCamera.targetTexture) { m_DepthCamera.targetTexture.Release(); DestroyImmediate(m_DepthCamera.targetTexture); m_DepthCamera.targetTexture = null; } DestroyImmediate(m_DepthCamera.gameObject); // Make sure to delete the GAO m_DepthCamera = null; } } protected override void OnModifyMaterialCallback(MaterialModifier.Interface owner) { Debug.Assert(owner != null); owner.SetMaterialProp(ShaderProperties.DynamicOcclusionDepthTexture, m_DepthCamera.targetTexture); owner.SetMaterialProp(ShaderProperties.DynamicOcclusionDepthProps, fadeDistanceToSurface); } #if UNITY_EDITOR bool m_NeedToReinstantiateDepthCamera = false; void MarkMaterialAsDirty() { // when adding/removing this component in editor, we might need to switch from a GPU Instanced material to a custom one, // since this feature doesn't support GPU Instancing if (!Application.isPlaying) m_Master._EditorSetBeamGeomDirty(); } protected override void OnValidate() { base.OnValidate(); m_NeedToReinstantiateDepthCamera = true; } void LateUpdate() { if (!Application.isPlaying) { if (m_NeedToReinstantiateDepthCamera) { DestroyDepthCamera(); InstantiateOrActivateDepthCamera(); m_NeedToReinstantiateDepthCamera = false; } if(m_Master && m_Master.enabled) ProcessOcclusion(ProcessOcclusionSource.EditorUpdate); } } #endif // UNITY_EDITOR } }