using System; using System.Collections.Generic; using UnityEngine.Experimental.Rendering; using UnityEngine.Rendering.RenderGraphModule; using UnityEngine.VFX; namespace UnityEngine.Rendering.HighDefinition { [GenerateHLSL] internal enum RayTracingRendererFlag { Opaque = 0x01, CastShadowTransparent = 0x02, CastShadowOpaque = 0x04, CastShadow = CastShadowOpaque | CastShadowTransparent, AmbientOcclusion = 0x08, Reflection = 0x10, GlobalIllumination = 0x20, RecursiveRendering = 0x40, PathTracing = 0x80, All = Opaque | CastShadow | AmbientOcclusion | Reflection | GlobalIllumination | RecursiveRendering | PathTracing, } /// /// Flags returned when trying to add a renderer into the ray tracing acceleration structure. /// public enum AccelerationStructureStatus { /// Initial flag state. Clear = 0x0, /// Flag that indicates that the renderer was successfully added to the ray tracing acceleration structure. Added = 0x1, /// Flag that indicates that the renderer was excluded from the ray tracing acceleration structure. Excluded = 0x02, /// Flag that indicates that the renderer was added to the ray tracing acceleration structure, but it had transparent and opaque sub-meshes. TransparencyIssue = 0x04, /// Flag that indicates that the renderer was not included into the ray tracing acceleration structure because of a missing material NullMaterial = 0x08, /// Flag that indicates that the renderer was not included into the ray tracing acceleration structure because of a missing mesh MissingMesh = 0x10 } public partial class HDRenderPipeline { // Data used for runtime evaluation static readonly string m_RTASDebugRTKernel = "RTASDebug"; HDRTASManager m_RTASManager; HDRaytracingLightCluster m_RayTracingLightCluster; bool m_ValidRayTracingState = false; bool m_ValidRayTracingCluster = false; bool m_ValidRayTracingClusterCulling = false; bool m_RayTracedShadowsRequired = false; bool m_RayTracedContactShadowsRequired = false; // Denoisers HDTemporalFilter m_TemporalFilter; HDDiffuseDenoiser m_DiffuseDenoiser; HDDiffuseShadowDenoiser m_DiffuseShadowDenoiser; ReBlurDenoiser m_ReBlurDenoiser; // Ray-count manager data RayCountManager m_RayCountManager; // Static variables used for the dirtiness and manual rtas management const int maxNumSubMeshes = 32; static RayTracingSubMeshFlags[] subMeshFlagArray = new RayTracingSubMeshFlags[maxNumSubMeshes]; static uint[] vfxSystemMasks = new uint[maxNumSubMeshes]; static List materialArray = new List(maxNumSubMeshes); static Dictionary m_MaterialCRCs = new Dictionary(); // Global shader variables ray tracing lightloop constant buffer ShaderVariablesRaytracingLightLoop m_ShaderVariablesRaytracingLightLoopCB = new ShaderVariablesRaytracingLightLoop(); internal bool GetMaterialDirtiness(HDCamera hdCamera) { RayTracingSettings rtSettings = hdCamera.volumeStack.GetComponent(); #if UNITY_EDITOR if (rtSettings.buildMode.value == RTASBuildMode.Automatic || hdCamera.camera.cameraType == CameraType.SceneView) #else if (rtSettings.buildMode.value == RTASBuildMode.Automatic) #endif { return m_RTASManager.materialsDirty; } else { return hdCamera.materialsDirty; } } internal void ResetMaterialDirtiness(HDCamera hdCamera) { RayTracingSettings rtSettings = hdCamera.volumeStack.GetComponent(); #if UNITY_EDITOR if (rtSettings.buildMode.value == RTASBuildMode.Automatic || hdCamera.camera.cameraType == CameraType.SceneView) #else if (rtSettings.buildMode.value == RTASBuildMode.Automatic) #endif { m_RTASManager.materialsDirty = false; } else { hdCamera.materialsDirty = false; } } internal bool GetTransformDirtiness(HDCamera hdCamera) { RayTracingSettings rtSettings = hdCamera.volumeStack.GetComponent(); #if UNITY_EDITOR if (rtSettings.buildMode.value == RTASBuildMode.Automatic || hdCamera.camera.cameraType == CameraType.SceneView) #else if (rtSettings.buildMode.value == RTASBuildMode.Automatic) #endif { return m_RTASManager.transformsDirty; } else { return hdCamera.transformsDirty; } } internal void ResetTransformDirtiness(HDCamera hdCamera) { RayTracingSettings rtSettings = hdCamera.volumeStack.GetComponent(); #if UNITY_EDITOR if (rtSettings.buildMode.value == RTASBuildMode.Automatic || hdCamera.camera.cameraType == CameraType.SceneView) #else if (rtSettings.buildMode.value == RTASBuildMode.Automatic) #endif { m_RTASManager.transformsDirty = false; } else { hdCamera.transformsDirty = false; } } internal void InitRayTracingManager() { // Init the ray count manager m_RayCountManager = new RayCountManager(); m_RayCountManager.Init(rayTracingResources); // Initialize the light cluster m_RayTracingLightCluster = new HDRaytracingLightCluster(); m_RayTracingLightCluster.Initialize(this); // Initialize the RTAS manager m_RTASManager = new HDRTASManager(); m_RTASManager.Initialize(); } internal void ReleaseRayTracingManager() { if (m_RTASManager != null) m_RTASManager.ReleaseResources(); if (m_RayTracingLightCluster != null) m_RayTracingLightCluster.ReleaseResources(); if (m_RayCountManager != null) m_RayCountManager.Release(); if (m_TemporalFilter != null) m_TemporalFilter.Release(); if (m_DiffuseShadowDenoiser != null) m_DiffuseShadowDenoiser.Release(); if (m_DiffuseDenoiser != null) m_DiffuseDenoiser.Release(); if (m_ReBlurDenoiser != null) { m_ReBlurDenoiser.Release(); m_ReBlurDenoiser = null; } } static bool IsValidRayTracedMaterial(Material currentMaterial) { if (currentMaterial == null || currentMaterial.shader == null) return false; // For the time being, we only consider non-decal HDRP materials as valid return currentMaterial.GetTag("RenderPipeline", false) == "HDRenderPipeline" && !DecalSystem.IsDecalMaterial(currentMaterial); } static bool IsTransparentMaterial(Material currentMaterial) { return currentMaterial.IsKeywordEnabled("_SURFACE_TYPE_TRANSPARENT") || (HDRenderQueue.k_RenderQueue_Transparent.lowerBound <= currentMaterial.renderQueue && HDRenderQueue.k_RenderQueue_Transparent.upperBound >= currentMaterial.renderQueue); } static bool IsAlphaTestedMaterial(Material currentMaterial) { return currentMaterial.IsKeywordEnabled("_ALPHATEST_ON") || (HDRenderQueue.k_RenderQueue_OpaqueAlphaTest.lowerBound <= currentMaterial.renderQueue && HDRenderQueue.k_RenderQueue_OpaqueAlphaTest.upperBound >= currentMaterial.renderQueue); } private static bool UpdateMaterialCRC(int matInstanceId, int matCRC) { int matPrevCRC; if (m_MaterialCRCs.TryGetValue(matInstanceId, out matPrevCRC)) { m_MaterialCRCs[matInstanceId] = matCRC; return (matCRC != matPrevCRC); } else { m_MaterialCRCs.Add(matInstanceId, matCRC); return true; } } /// /// Function that adds a renderer to a ray tracing acceleration structure. /// /// Ray Tracing Acceleration structure the renderer should be added to. /// The renderer that should be added to the RTAS. /// Structure defining the enabled ray tracing and path tracing effects for a camera. /// Flag that indicates if the renderer's transform has changed. /// Flag that indicates if any of the renderer's materials have changed. /// public static AccelerationStructureStatus AddInstanceToRAS(RayTracingAccelerationStructure targetRTAS, Renderer currentRenderer, HDEffectsParameters effectsParameters, ref bool transformDirty, ref bool materialsDirty) { if (currentRenderer is VFXRenderer vfxRenderer) return AddVFXInstanceToRAS(targetRTAS, vfxRenderer, effectsParameters, ref transformDirty, ref materialsDirty); return AddRegularInstanceToRAS(targetRTAS, currentRenderer, effectsParameters, ref transformDirty, ref materialsDirty); } private static AccelerationStructureStatus AddRegularInstanceToRAS(RayTracingAccelerationStructure targetRTAS, Renderer currentRenderer, HDEffectsParameters effectsParameters, ref bool transformDirty, ref bool materialsDirty) { // For every sub-mesh/sub-material let's build the right flags int numSubMeshes = 1; if (currentRenderer.GetType() == typeof(SkinnedMeshRenderer)) { SkinnedMeshRenderer skinnedMesh = (SkinnedMeshRenderer)currentRenderer; if (skinnedMesh.sharedMesh == null) return AccelerationStructureStatus.MissingMesh; currentRenderer.GetSharedMaterials(materialArray); numSubMeshes = skinnedMesh.sharedMesh.subMeshCount; } else { currentRenderer.TryGetComponent(out MeshFilter meshFilter); if (meshFilter == null || meshFilter.sharedMesh == null) return AccelerationStructureStatus.MissingMesh; currentRenderer.GetSharedMaterials(materialArray); numSubMeshes = meshFilter.sharedMesh.subMeshCount; } // If the material array is null, we are done if (materialArray == null) return AccelerationStructureStatus.NullMaterial; // Let's clamp the number of sub-meshes to avoid throwing an unwanted error numSubMeshes = Mathf.Min(numSubMeshes, maxNumSubMeshes); bool doubleSided = false; bool materialIsOnlyTransparent = true; bool hasTransparentSubMaterial = false; for (int subGeomIdx = 0; subGeomIdx < numSubMeshes; ++subGeomIdx) { // Initially we consider the potential mesh as invalid bool validMesh = false; if (materialArray.Count > subGeomIdx) { // Grab the material for the current sub-mesh Material currentMaterial = materialArray[subGeomIdx]; // Make sure that the material is HDRP's and non-decal if (IsValidRayTracedMaterial(currentMaterial)) { // Mesh is valid given that all requirements are ok validMesh = true; // Evaluate what kind of materials we are dealing with bool alphaTested = IsAlphaTestedMaterial(currentMaterial); bool transparentMaterial = IsTransparentMaterial(currentMaterial); // Aggregate the transparency info materialIsOnlyTransparent &= transparentMaterial; hasTransparentSubMaterial |= transparentMaterial; ComputeSubMeshFlag(subGeomIdx, transparentMaterial, alphaTested, currentMaterial, ref doubleSided); // Check if the material has changed since last time we were here if (!materialsDirty) { materialsDirty |= UpdateMaterialCRC(currentMaterial.GetInstanceID(), currentMaterial.ComputeCRC()); } } } // If the mesh was not valid, exclude it (without affecting sidedness) if (!validMesh) subMeshFlagArray[subGeomIdx] = RayTracingSubMeshFlags.Disabled; } // If the material is considered opaque, every sub-mesh has to be enabled and with unique any hit calls if (!materialIsOnlyTransparent && hasTransparentSubMaterial) for (int meshIdx = 0; meshIdx < numSubMeshes; ++meshIdx) subMeshFlagArray[meshIdx] = RayTracingSubMeshFlags.Enabled | RayTracingSubMeshFlags.UniqueAnyHitCalls; // We need to build the instance flag for this renderer uint instanceFlag = ComputeInstanceFlag(currentRenderer, currentRenderer.shadowCastingMode, effectsParameters, hasTransparentSubMaterial, materialIsOnlyTransparent); // If the object was not referenced if (instanceFlag == 0) return AccelerationStructureStatus.Added; targetRTAS.AddInstance(currentRenderer, subMeshFlags: subMeshFlagArray, enableTriangleCulling: !doubleSided, mask: instanceFlag); // Indicates that a transform has changed in our scene (mesh or light) transformDirty |= currentRenderer.transform.hasChanged; currentRenderer.transform.hasChanged = false; // return the status return (!materialIsOnlyTransparent && hasTransparentSubMaterial) ? AccelerationStructureStatus.TransparencyIssue : AccelerationStructureStatus.Added; } private static AccelerationStructureStatus AddVFXInstanceToRAS(RayTracingAccelerationStructure targetRTAS, VFXRenderer currentRenderer, HDEffectsParameters effectsParameters, ref bool transformDirty, ref bool materialsDirty) { // If we should exclude Visual effects, skip right now if (!effectsParameters.includeVFX) return AccelerationStructureStatus.Excluded; currentRenderer.GetSharedMaterials(materialArray); int numSubGeom = materialArray.Count; // If the material array is null, we are done if (materialArray == null) return AccelerationStructureStatus.NullMaterial; // Let's clamp the number of sub-meshes to avoid throwing an unwanted error numSubGeom = Mathf.Min(numSubGeom, maxNumSubMeshes); bool materialIsOnlyTransparent = true; bool hasTransparentSubMaterial = false; int compactedSubGeomIndex = 0; // For VFX, we expect no holes in the system mask array bool hasAnyValidInstance = false; for (int subGeomIdx = 0; subGeomIdx < numSubGeom; ++subGeomIdx) { if (materialArray.Count > subGeomIdx) { // Grab the material for the current sub-mesh Material currentMaterial = materialArray[subGeomIdx]; // Make sure that the material is HDRP's and non-decal if (IsValidRayTracedMaterial(currentMaterial)) { // Evaluate what kind of materials we are dealing with bool transparentMaterial = IsTransparentMaterial(currentMaterial); // Aggregate the transparency info materialIsOnlyTransparent &= transparentMaterial; hasTransparentSubMaterial |= transparentMaterial; ShadowCastingMode shadowCastingMode = currentMaterial.FindPass("ShadowCaster") != -1 ? ShadowCastingMode.On : ShadowCastingMode.Off; uint instanceFlag = ComputeInstanceFlag(currentRenderer, shadowCastingMode, effectsParameters, transparentMaterial, transparentMaterial); hasAnyValidInstance |= (instanceFlag != 0); vfxSystemMasks[compactedSubGeomIndex] = instanceFlag; compactedSubGeomIndex++; // Check if the material has changed since last time we were here if (!materialsDirty) { materialsDirty |= UpdateMaterialCRC(currentMaterial.GetInstanceID(), currentMaterial.ComputeCRC()); } } } } // If the object was not referenced if (!hasAnyValidInstance) return AccelerationStructureStatus.Added; // Add it to the acceleration structure targetRTAS.AddVFXInstances(currentRenderer, vfxSystemMasks); // Indicates that a transform has changed in our scene (mesh or light) transformDirty |= currentRenderer.transform.hasChanged; currentRenderer.transform.hasChanged = false; // return the status return (!materialIsOnlyTransparent && hasTransparentSubMaterial) ? AccelerationStructureStatus.TransparencyIssue : AccelerationStructureStatus.Added; } private static void ComputeSubMeshFlag(int meshIdx, bool transparentMaterial, bool alphaTested, Material currentMaterial, ref bool doubleSided) { // Mark the thing as valid subMeshFlagArray[meshIdx] = RayTracingSubMeshFlags.Enabled; // Append the additional flags depending on what kind of sub mesh this is if (!transparentMaterial && !alphaTested) subMeshFlagArray[meshIdx] |= RayTracingSubMeshFlags.ClosestHitOnly; else if (transparentMaterial) subMeshFlagArray[meshIdx] |= RayTracingSubMeshFlags.UniqueAnyHitCalls; // Check if we want to enable double-sidedness for the mesh // (note that a mix of single and double-sided materials will result in a double-sided mesh in the AS) doubleSided |= currentMaterial.doubleSidedGI || currentMaterial.IsKeywordEnabled("_DOUBLESIDED_ON"); } private static uint ComputeInstanceFlag(Renderer currentRenderer, ShadowCastingMode shadowCastingMode, HDEffectsParameters effectsParameters, bool hasTransparentSubMaterial, bool materialIsOnlyTransparent) { uint instanceFlag = 0x00; // Get the layer of this object int objectLayerValue = 1 << currentRenderer.gameObject.layer; // We disregard the ray traced shadows option when in Path Tracing bool rayTracedShadow = effectsParameters.shadows && !effectsParameters.pathTracing; // Deactivate Path Tracing if the object does not belong to the path traced layer(s) bool pathTracing = effectsParameters.pathTracing && (bool)((effectsParameters.ptLayerMask & objectLayerValue) != 0); // Propagate the opacity mask only if all sub materials are opaque bool isOpaque = !hasTransparentSubMaterial; if (isOpaque) { instanceFlag |= (uint)(RayTracingRendererFlag.Opaque); } if (rayTracedShadow || pathTracing) { if (hasTransparentSubMaterial) { // Raise the shadow casting flag if needed instanceFlag |= ((shadowCastingMode != ShadowCastingMode.Off) ? (uint)(RayTracingRendererFlag.CastShadowTransparent) : 0x00); } else { // Raise the shadow casting flag if needed instanceFlag |= ((shadowCastingMode != ShadowCastingMode.Off) ? (uint)(RayTracingRendererFlag.CastShadowOpaque) : 0x00); } } // We consider a mesh visible by reflection, gi, etc if it is not in the shadow only mode. bool meshIsVisible = shadowCastingMode != ShadowCastingMode.ShadowsOnly; if (effectsParameters.ambientOcclusion && !materialIsOnlyTransparent && meshIsVisible) { // Raise the Ambient Occlusion flag if needed instanceFlag |= ((effectsParameters.aoLayerMask & objectLayerValue) != 0) ? (uint)(RayTracingRendererFlag.AmbientOcclusion) : 0x00; } if (effectsParameters.reflections && !materialIsOnlyTransparent && meshIsVisible) { // Raise the Screen Space Reflection flag if needed instanceFlag |= ((effectsParameters.reflLayerMask & objectLayerValue) != 0) ? (uint)(RayTracingRendererFlag.Reflection) : 0x00; } if (effectsParameters.globalIllumination && !materialIsOnlyTransparent && meshIsVisible) { // Raise the Global Illumination flag if needed instanceFlag |= ((effectsParameters.giLayerMask & objectLayerValue) != 0) ? (uint)(RayTracingRendererFlag.GlobalIllumination) : 0x00; } if (effectsParameters.recursiveRendering && meshIsVisible) { // Raise the Recursive Rendering flag if needed instanceFlag |= ((effectsParameters.recursiveLayerMask & objectLayerValue) != 0) ? (uint)(RayTracingRendererFlag.RecursiveRendering) : 0x00; } if (effectsParameters.pathTracing && meshIsVisible) { // Raise the Path Tracing flag if needed instanceFlag |= (uint)(RayTracingRendererFlag.PathTracing); } return instanceFlag; } void GatherLightInformationForRayTracing(HDCamera hdCamera, ref bool transformDirty) { // fetch all the lights in the scene HDLightRenderDatabase lightEntities = HDLightRenderDatabase.instance; for (int lightIdx = 0; lightIdx < lightEntities.lightCount; ++lightIdx) { HDLightRenderEntity lightRenderEntity = lightEntities.lightEntities[lightIdx]; HDAdditionalLightData hdLight = lightEntities.hdAdditionalLightData[lightIdx]; if (hdLight != null && hdLight.enabled && hdLight != HDUtils.s_DefaultHDAdditionalLightData) { Light light = hdLight.gameObject.GetComponent(); // If the light is null or disabled, skip it if (light == null || !light.enabled) continue; // If the light is flagged as baked and has been effectively been baked, skip it, except if we are path tracing bool isPathTracingEnabled = hdCamera.IsPathTracingEnabled(); if (!isPathTracingEnabled && light.bakingOutput.lightmapBakeType == LightmapBakeType.Baked && light.bakingOutput.isBaked) continue; // If this light should not be included when ray tracing is active on the camera, skip it, except if we are path tracing if (!isPathTracingEnabled && hdCamera.frameSettings.IsEnabled(FrameSettingsField.RayTracing) && !hdLight.includeForRayTracing) continue; // If path tracing is enabled and the light should not be included in path tracing, skip it if(isPathTracingEnabled && hdCamera.frameSettings.IsEnabled(FrameSettingsField.RayTracing) && !hdLight.includeForPathTracing) continue; // Flag that needs to be overriden by the light and tells us if the light will need the RTAS bool hasRayTracedShadows = false; // Indicates that a transform has changed in our scene (mesh or light) transformDirty |= hdLight.transform.hasChanged; hdLight.transform.hasChanged = false; switch (hdLight.legacyLight.type) { case LightType.Directional: { hasRayTracedShadows = hdLight.ShadowsEnabled() && hdLight.useScreenSpaceShadows && hdLight.useRayTracedShadows; } break; case LightType.Point: case LightType.Spot: case LightType.Pyramid: case LightType.Box: { hasRayTracedShadows = hdLight.ShadowsEnabled() && hdLight.useRayTracedShadows; } break; case LightType.Rectangle: { hasRayTracedShadows = hdLight.ShadowsEnabled() && hdLight.useRayTracedShadows; } break; case LightType.Tube: { hasRayTracedShadows = hdLight.ShadowsEnabled() && hdLight.useRayTracedShadows; } break; case LightType.Disc: { hasRayTracedShadows = hdLight.ShadowsEnabled() && hdLight.useRayTracedShadows; } break; } // Check if there is a ray traced shadow in the scene m_RayTracedShadowsRequired |= hasRayTracedShadows; m_RayTracedContactShadowsRequired |= (hdLight.useContactShadow.@override && hdLight.rayTraceContactShadow); } } } /// /// Function that returns the ray tracing and path tracing effects that are enabled for a given camera. /// /// The input camera /// Flag that defines if at least one light has ray traced shadows. /// Flag that defines if at least one light has ray traced contact shadows /// HDEffectsParameters type. public static HDEffectsParameters EvaluateEffectsParameters(HDCamera hdCamera, bool rayTracedShadows, bool rayTracedContactShadows) { HDEffectsParameters parameters = new HDEffectsParameters(); // Aggregate the shadow requirements parameters.shadows = hdCamera.frameSettings.IsEnabled(FrameSettingsField.ScreenSpaceShadows) && (rayTracedShadows || rayTracedContactShadows); // Aggregate the ambient occlusion parameters var aoSettings = hdCamera.volumeStack.GetComponent(); parameters.ambientOcclusion = aoSettings.rayTracing.value && hdCamera.frameSettings.IsEnabled(FrameSettingsField.SSAO); parameters.aoLayerMask = aoSettings.layerMask.value; // Aggregate the reflections parameters ScreenSpaceReflection reflSettings = hdCamera.volumeStack.GetComponent(); bool opaqueReflections = hdCamera.frameSettings.IsEnabled(FrameSettingsField.SSR) && reflSettings.enabled.value; bool transparentReflections = hdCamera.frameSettings.IsEnabled(FrameSettingsField.TransparentSSR) && reflSettings.enabledTransparent.value; parameters.reflections = ScreenSpaceReflection.RayTracingActive(reflSettings) && (opaqueReflections || transparentReflections); parameters.reflLayerMask = reflSettings.layerMask.value; // Aggregate the global illumination parameters GlobalIllumination giSettings = hdCamera.volumeStack.GetComponent(); parameters.globalIllumination = giSettings.enable.value && GlobalIllumination.RayTracingActive(giSettings) && hdCamera.frameSettings.IsEnabled(FrameSettingsField.SSGI); parameters.giLayerMask = giSettings.layerMask.value; // Aggregate the global illumination parameters RecursiveRendering recursiveSettings = hdCamera.volumeStack.GetComponent(); parameters.recursiveRendering = recursiveSettings.enable.value; parameters.recursiveLayerMask = recursiveSettings.layerMask.value; // Aggregate the sub surface parameters SubSurfaceScattering sssSettings = hdCamera.volumeStack.GetComponent(); parameters.subSurface = sssSettings.rayTracing.value && hdCamera.frameSettings.IsEnabled(FrameSettingsField.SubsurfaceScattering); // Aggregate the path parameters PathTracing pathTracingSettings = hdCamera.volumeStack.GetComponent(); parameters.pathTracing = pathTracingSettings.enable.value; parameters.ptLayerMask = pathTracingSettings.layerMask.value; // Aggregate the VFX parameters parameters.includeVFX = hdCamera.frameSettings.IsEnabled(FrameSettingsField.RaytracingVFX); // We need to check if at least one effect will require the acceleration structure parameters.rayTracingRequired = parameters.ambientOcclusion || parameters.reflections || parameters.globalIllumination || parameters.recursiveRendering || parameters.subSurface || parameters.pathTracing || parameters.shadows; // Return the result return parameters; } internal void BuildRayTracingAccelerationStructure(HDCamera hdCamera) { // Resets the rtas manager m_RTASManager.Reset(); // Reset all the flags m_ValidRayTracingState = false; m_ValidRayTracingCluster = false; m_ValidRayTracingClusterCulling = false; m_RayTracedShadowsRequired = false; m_RayTracedContactShadowsRequired = false; // If the camera does not have a ray tracing frame setting or it is a preview camera (due to the fact that the sphere does not exist as a game object we can't create the RTAS) we do not want to build a RTAS if (!hdCamera.frameSettings.IsEnabled(FrameSettingsField.RayTracing)) return; if (m_VFXRayTracingSupported && hdCamera.frameSettings.IsEnabled(FrameSettingsField.RaytracingVFX)) VFXManager.RequestRtasAabbConstruction(); GatherLightInformationForRayTracing(hdCamera, ref m_RTASManager.transformsDirty); // Evaluate the parameters of the effects HDEffectsParameters effectParameters = EvaluateEffectsParameters(hdCamera, m_RayTracedShadowsRequired, m_RayTracedContactShadowsRequired); if (!effectParameters.rayTracingRequired) return; // Grab the ray tracing settings RayTracingSettings rtSettings = hdCamera.volumeStack.GetComponent(); #if UNITY_EDITOR if (rtSettings.buildMode.value == RTASBuildMode.Automatic || hdCamera.camera.cameraType == CameraType.SceneView) #else if (rtSettings.buildMode.value == RTASBuildMode.Automatic) #endif { // Cull the scene for the RTAS RayTracingInstanceCullingResults cullingResults = m_RTASManager.Cull(hdCamera, effectParameters); // Update the material dirtiness for the PT if (effectParameters.pathTracing) { m_RTASManager.transformsDirty |= cullingResults.transformsChanged; for (int i = 0; i < cullingResults.materialsCRC.Length; i++) { RayTracingInstanceMaterialCRC matCRC = cullingResults.materialsCRC[i]; m_RTASManager.materialsDirty |= UpdateMaterialCRC(matCRC.instanceID, matCRC.crc); } } // Build the ray tracing acceleration structure m_RTASManager.Build(hdCamera); // tag the structures as valid m_ValidRayTracingState = true; } else { // If the user fed a non null ray tracing acceleration structure, then we are all set. if (hdCamera.rayTracingAccelerationStructure != null) m_ValidRayTracingState = true; } } class RTASDebugPassData { // Camera data public int actualWidth; public int actualHeight; public int viewCount; // Evaluation parameters public int debugMode; public uint layerMask; public Matrix4x4 pixelCoordToViewDirWS; // Other parameters public RayTracingShader debugRTASRT; public RayTracingAccelerationStructure rayTracingAccelerationStructure; // Output public TextureHandle outputTexture; } static uint LayerFromRTASDebugView(RTASDebugView debugView, HDCamera hdCamera) { switch (debugView) { case RTASDebugView.Shadows: { return (uint)RayTracingRendererFlag.CastShadow; } case RTASDebugView.AmbientOcclusion: { return (uint)RayTracingRendererFlag.AmbientOcclusion; } case RTASDebugView.GlobalIllumination: { return (uint)RayTracingRendererFlag.GlobalIllumination; } case RTASDebugView.Reflections: { return (uint)RayTracingRendererFlag.Reflection; } case RTASDebugView.RecursiveRayTracing: { return (uint)RayTracingRendererFlag.RecursiveRendering; } case RTASDebugView.PathTracing: { return (uint)RayTracingRendererFlag.PathTracing; } default: { return (uint)RayTracingRendererFlag.All; } } } internal void EvaluateRTASDebugView(RenderGraph renderGraph, HDCamera hdCamera) { // If the ray tracing state is not valid, we cannot evaluate the debug view if (!m_ValidRayTracingState) return; using (var builder = renderGraph.AddRenderPass("Debug view of the RTAS", out var passData, ProfilingSampler.Get(HDProfileId.RaytracingBuildAccelerationStructureDebug))) { builder.EnableAsyncCompute(false); // Camera data passData.actualWidth = hdCamera.actualWidth; passData.actualHeight = hdCamera.actualHeight; passData.viewCount = hdCamera.viewCount; // Evaluation parameters passData.debugMode = (int)m_CurrentDebugDisplaySettings.data.rtasDebugMode; passData.layerMask = LayerFromRTASDebugView(m_CurrentDebugDisplaySettings.data.rtasDebugView, hdCamera); passData.pixelCoordToViewDirWS = hdCamera.mainViewConstants.pixelCoordToViewDirWS; // Other parameters passData.debugRTASRT = rayTracingResources.debugRTASRT; passData.rayTracingAccelerationStructure = RequestAccelerationStructure(hdCamera); // Depending of if we will have to denoise (or not), we need to allocate the final format, or a bigger texture passData.outputTexture = builder.WriteTexture(renderGraph.CreateTexture(new TextureDesc(Vector2.one, true, true) { colorFormat = GraphicsFormat.R16G16B16A16_SFloat, enableRandomWrite = true, name = "RTAS Debug" })); builder.SetRenderFunc( (RTASDebugPassData data, RenderGraphContext ctx) => { // Define the shader pass to use for the reflection pass ctx.cmd.SetRayTracingShaderPass(data.debugRTASRT, "DebugDXR"); // Set the acceleration structure for the pass ctx.cmd.SetRayTracingAccelerationStructure(data.debugRTASRT, HDShaderIDs._RaytracingAccelerationStructureName, data.rayTracingAccelerationStructure); // Layer mask ctx.cmd.SetRayTracingIntParam(data.debugRTASRT, "_DebugMode", data.debugMode); ctx.cmd.SetRayTracingIntParam(data.debugRTASRT, "_LayerMask", (int)data.layerMask); ctx.cmd.SetRayTracingMatrixParam(data.debugRTASRT, HDShaderIDs._PixelCoordToViewDirWS, data.pixelCoordToViewDirWS); // Set the output texture ctx.cmd.SetRayTracingTextureParam(data.debugRTASRT, "_OutputDebugBuffer", data.outputTexture); // Evaluate the debug view ctx.cmd.DispatchRays(data.debugRTASRT, m_RTASDebugRTKernel, (uint)data.actualWidth, (uint)data.actualHeight, (uint)data.viewCount); }); // Use the debug texture to do the full screen debug PushFullScreenDebugTexture(renderGraph, passData.outputTexture, FullScreenDebugMode.RayTracingAccelerationStructure); } } internal static int RayTracingFrameIndex(HDCamera hdCamera) { return hdCamera.ActiveRayTracingAccumulation() ? (int)hdCamera.GetCameraFrameCount() % 8 : 0; } internal int RayTracingFrameIndex(HDCamera hdCamera, int targetFrameCount = 8) { return hdCamera.ActiveRayTracingAccumulation() ? (int)hdCamera.GetCameraFrameCount() % targetFrameCount : 0; } internal bool RayTracingLightClusterRequired(HDCamera hdCamera) { ScreenSpaceReflection reflSettings = hdCamera.volumeStack.GetComponent(); GlobalIllumination giSettings = hdCamera.volumeStack.GetComponent(); RecursiveRendering recursiveSettings = hdCamera.volumeStack.GetComponent(); PathTracing pathTracingSettings = hdCamera.volumeStack.GetComponent(); SubSurfaceScattering subSurface = hdCamera.volumeStack.GetComponent(); return (m_ValidRayTracingState && (ScreenSpaceReflection.RayTracingActive(reflSettings) || GlobalIllumination.RayTracingActive(giSettings) || recursiveSettings.enable.value || pathTracingSettings.enable.value || subSurface.rayTracing.value)); } internal void CullForRayTracing(CommandBuffer cmd, HDCamera hdCamera) { if (m_ValidRayTracingState && RayTracingLightClusterRequired(hdCamera)) { m_RayTracingLightCluster.CullForRayTracing(hdCamera, m_WorldLights, m_WorldLightsVolumes); m_ValidRayTracingClusterCulling = true; } } internal void ReserveRayTracingCookieAtlasSlots() { if (m_ValidRayTracingState && m_ValidRayTracingClusterCulling) { m_RayTracingLightCluster.ReserveCookieAtlasSlots(m_WorldLights); } } internal void BuildRayTracingLightData(CommandBuffer cmd, HDCamera hdCamera) { if (m_ValidRayTracingState && m_ValidRayTracingClusterCulling) { m_ValidRayTracingCluster = true; UpdateShaderVariablesRaytracingLightLoopCB(hdCamera, cmd); m_RayTracingLightCluster.BuildLightClusterBuffer(cmd, hdCamera, m_WorldLightsVolumes); } } static internal float EvaluateHistoryValidity(HDCamera hdCamera) { // We need to check if something invalidated the history buffers float historyValidity = (((int)hdCamera.GetCameraFrameCount() > 1) && hdCamera.ActiveRayTracingAccumulation()) ? 1.0f : 0.0f; return historyValidity; } internal bool RayTracingHalfResAllowed() { return DynamicResolutionHandler.instance.GetCurrentScale() >= (currentPlatformRenderPipelineSettings.dynamicResolutionSettings.rayTracingHalfResThreshold / 100.0f); } internal static Vector4 EvaluateRayTracingHistorySizeAndScale(HDCamera hdCamera, RTHandle buffer) { return new Vector4(hdCamera.historyRTHandleProperties.previousViewportSize.x, hdCamera.historyRTHandleProperties.previousViewportSize.y, (float)hdCamera.historyRTHandleProperties.previousViewportSize.x / buffer.rt.width, (float)hdCamera.historyRTHandleProperties.previousViewportSize.y / buffer.rt.height); } void UpdateShaderVariablesRaytracingLightLoopCB(HDCamera hdCamera, CommandBuffer cmd) { m_ShaderVariablesRaytracingLightLoopCB._MinClusterPos = m_RayTracingLightCluster.GetMinClusterPos(); m_ShaderVariablesRaytracingLightLoopCB._LightPerCellCount = (uint)m_RayTracingLightCluster.GetLightPerCellCount(); m_ShaderVariablesRaytracingLightLoopCB._MaxClusterPos = m_RayTracingLightCluster.GetMaxClusterPos(); ConstantBuffer.PushGlobal(cmd, m_ShaderVariablesRaytracingLightLoopCB, HDShaderIDs._ShaderVariablesRaytracingLightLoop); } internal bool RayTracedContactShadowsRequired() { return m_RayTracedContactShadowsRequired; } internal RayTracingAccelerationStructure RequestAccelerationStructure(HDCamera hdCamera) { if (m_ValidRayTracingState) { RayTracingSettings rtSettings = hdCamera.volumeStack.GetComponent(); #if UNITY_EDITOR if (rtSettings.buildMode.value == RTASBuildMode.Automatic || hdCamera.camera.cameraType == CameraType.SceneView) #else if (rtSettings.buildMode.value == RTASBuildMode.Automatic) #endif return m_RTASManager.rtas; else return hdCamera.rayTracingAccelerationStructure; } return null; } internal HDRaytracingLightCluster RequestLightCluster() { if (m_ValidRayTracingCluster) { return m_RayTracingLightCluster; } return null; } // Ray Tracing is supported if the asset setting supports it and the platform supports it static internal bool PipelineSupportsRayTracing(RenderPipelineSettings rpSetting) => rpSetting.supportRayTracing && currentSystemSupportsRayTracing; static internal bool currentSystemSupportsRayTracing => SystemInfo.supportsRayTracing #if UNITY_EDITOR && (UnityEditor.EditorUserBuildSettings.activeBuildTarget == UnityEditor.BuildTarget.StandaloneWindows64 || UnityEditor.EditorUserBuildSettings.activeBuildTarget == UnityEditor.BuildTarget.StandaloneWindows || UnityEditor.EditorUserBuildSettings.activeBuildTarget == UnityEditor.BuildTarget.GameCoreXboxSeries || UnityEditor.EditorUserBuildSettings.activeBuildTarget == UnityEditor.BuildTarget.PS5); #else ; #endif internal BlueNoise GetBlueNoiseManager() { return m_BlueNoise; } internal HDTemporalFilter GetTemporalFilter() { if (m_TemporalFilter == null) { m_TemporalFilter = new HDTemporalFilter(); m_TemporalFilter.Init(this); } return m_TemporalFilter; } internal HDDiffuseDenoiser GetDiffuseDenoiser() { if (m_DiffuseDenoiser == null) { m_DiffuseDenoiser = new HDDiffuseDenoiser(); m_DiffuseDenoiser.Init(this); } return m_DiffuseDenoiser; } internal ReBlurDenoiser GetReBlurDenoiser() { if (m_ReBlurDenoiser == null) { m_ReBlurDenoiser = new ReBlurDenoiser(); m_ReBlurDenoiser.Init(rayTracingResources); } return m_ReBlurDenoiser; } internal HDDiffuseShadowDenoiser GetDiffuseShadowDenoiser() { if (m_DiffuseShadowDenoiser == null) { m_DiffuseShadowDenoiser = new HDDiffuseShadowDenoiser(); m_DiffuseShadowDenoiser.Init(rayTracingResources); } return m_DiffuseShadowDenoiser; } internal bool GetRayTracingState() { return m_ValidRayTracingState; } internal bool GetRayTracingClusterState() { return m_ValidRayTracingCluster; } static internal float GetPixelSpreadTangent(float fov, int width, int height) { return Mathf.Tan(fov * Mathf.Deg2Rad * 0.5f) * 2.0f / Mathf.Min(width, height); } static internal float GetPixelSpreadAngle(float fov, int width, int height) { return Mathf.Atan(GetPixelSpreadTangent(fov, width, height)); } internal TextureHandle EvaluateHistoryValidationBuffer(RenderGraph renderGraph, HDCamera hdCamera, TextureHandle depthBuffer, TextureHandle normalBuffer, TextureHandle motionVectorsBuffer) { // Grab the temporal filter HDTemporalFilter temporalFilter = GetTemporalFilter(); // If the temporal filter is valid use it, otherwise return a white texture if (temporalFilter != null) { float historyValidity = EvaluateHistoryValidity(hdCamera); return temporalFilter.HistoryValidity(renderGraph, hdCamera, historyValidity, depthBuffer, normalBuffer, motionVectorsBuffer); } else return renderGraph.defaultResources.whiteTexture; } } }