ProbeVolumeList PrepareVisibleProbeVolumeList(ScriptableRenderContext renderContext, HDCamera hdCamera, CommandBuffer cmd) { ProbeVolumeList probeVolumes = new ProbeVolumeList(); if (ShaderConfig.s_ProbeVolumesEvaluationMode == ProbeVolumesEvaluationModes.Disabled) { return(probeVolumes); } if (!hdCamera.frameSettings.IsEnabled(FrameSettingsField.ProbeVolume)) { return(probeVolumes); } var settings = hdCamera.volumeStack.GetComponent <ProbeVolumeController>(); bool octahedralDepthOcclusionFilterIsEnabled = settings.leakMitigationMode.value == LeakMitigationMode.OctahedralDepthOcclusionFilter; using (new ProfilingScope(cmd, ProfilingSampler.Get(HDProfileId.PrepareProbeVolumeList))) { ClearProbeVolumeAtlasIfRequested(cmd); Vector3 camPosition = hdCamera.camera.transform.position; Vector3 camOffset = Vector3.zero;// World-origin-relative if (ShaderConfig.s_CameraRelativeRendering != 0) { camOffset = camPosition; // Camera-relative } m_VisibleProbeVolumeBounds.Clear(); m_VisibleProbeVolumeData.Clear(); // Collect all visible finite volume data, and upload it to the GPU. List <ProbeVolume> volumes = ProbeVolumeManager.manager.volumes; int probeVolumesCount = Math.Min(volumes.Count, k_MaxVisibleProbeVolumeCount); int sortCount = 0; // Sort probe volumes smallest from smallest to largest volume. // Same as is done with reflection probes. // See LightLoop.cs::PrepareLightsForGPU() for original example of this. for (int probeVolumesIndex = 0; (probeVolumesIndex < volumes.Count) && (sortCount < probeVolumesCount); probeVolumesIndex++) { ProbeVolume volume = volumes[probeVolumesIndex]; #if UNITY_EDITOR if (!volume.IsAssetCompatible()) { continue; } #endif if (ShaderConfig.s_ProbeVolumesAdditiveBlending == 0 && volume.parameters.volumeBlendMode != VolumeBlendMode.Normal) { // Non-normal blend mode volumes are not supported. Skip. continue; } float probeVolumeDepthFromCameraWS = Vector3.Dot(hdCamera.camera.transform.forward, volume.transform.position - camPosition); if (probeVolumeDepthFromCameraWS >= volume.parameters.distanceFadeEnd) { // Probe volume is completely faded out from distance fade optimization. // Do not bother adding it to the list, it would evaluate to zero weight. continue; } // TODO: cache these? var obb = new OrientedBBox(Matrix4x4.TRS(volume.transform.position, volume.transform.rotation, volume.parameters.size)); // Handle camera-relative rendering. obb.center -= camOffset; // Frustum cull on the CPU for now. TODO: do it on the GPU. if (GeometryUtils.Overlap(obb, hdCamera.frustum, hdCamera.frustum.planes.Length, hdCamera.frustum.corners.Length)) { var logVolume = CalculateProbeVolumeLogVolume(volume.parameters.size); m_ProbeVolumeSortKeys[sortCount++] = PackProbeVolumeSortKey(volume.parameters.volumeBlendMode, logVolume, probeVolumesIndex); } } CoreUnsafeUtils.QuickSort(m_ProbeVolumeSortKeys, 0, sortCount - 1); // Call our own quicksort instead of Array.Sort(sortKeys, 0, sortCount) so we don't allocate memory (note the SortCount-1 that is different from original call). for (int sortIndex = 0; sortIndex < sortCount; ++sortIndex) { // In 1. we have already classify and sorted the probe volume, we need to use this sorted order here uint sortKey = m_ProbeVolumeSortKeys[sortIndex]; int probeVolumesIndex; UnpackProbeVolumeSortKey(sortKey, out probeVolumesIndex); ProbeVolume volume = volumes[probeVolumesIndex]; // TODO: cache these? var obb = new OrientedBBox(Matrix4x4.TRS(volume.transform.position, volume.transform.rotation, volume.parameters.size)); // Handle camera-relative rendering. obb.center -= camOffset; // TODO: cache these? var data = volume.parameters.ConvertToEngineData(); // Note: The system is not aware of slice packing in Z. // Need to modify scale and bias terms just before uploading to GPU. // TODO: Should we make it aware earlier up the chain? data.scale.z = data.scale.z / (float)m_ProbeVolumeAtlasSHRTDepthSliceCount; data.bias.z = data.bias.z / (float)m_ProbeVolumeAtlasSHRTDepthSliceCount; m_VisibleProbeVolumeBounds.Add(obb); m_VisibleProbeVolumeData.Add(data); } s_VisibleProbeVolumeBoundsBuffer.SetData(m_VisibleProbeVolumeBounds); s_VisibleProbeVolumeDataBuffer.SetData(m_VisibleProbeVolumeData); // Fill the struct with pointers in order to share the data with the light loop. probeVolumes.bounds = m_VisibleProbeVolumeBounds; probeVolumes.data = m_VisibleProbeVolumeData; // For now, only upload one volume per frame. // This is done: // 1) To timeslice upload cost across N frames for N volumes. // 2) To avoid creating a sync point between compute buffer upload and each volume upload. const int volumeUploadedToAtlasSHCapacity = 1; int volumeUploadedToAtlasOctahedralDepthCapacity = octahedralDepthOcclusionFilterIsEnabled ? 1 : 0; int volumeUploadedToAtlasSHCount = 0; int volumeUploadedToAtlasOctahedralDepthCount = 0; for (int sortIndex = 0; sortIndex < sortCount; ++sortIndex) { uint sortKey = m_ProbeVolumeSortKeys[sortIndex]; int probeVolumesIndex; UnpackProbeVolumeSortKey(sortKey, out probeVolumesIndex); ProbeVolume volume = volumes[probeVolumesIndex]; if (volumeUploadedToAtlasSHCount < volumeUploadedToAtlasSHCapacity) { bool volumeWasUploaded = EnsureProbeVolumeInAtlas(renderContext, cmd, volume); if (volumeWasUploaded) { ++volumeUploadedToAtlasSHCount; } } if (volumeUploadedToAtlasOctahedralDepthCount < volumeUploadedToAtlasOctahedralDepthCapacity) { bool volumeWasUploaded = EnsureProbeVolumeInAtlasOctahedralDepth(renderContext, cmd, volume); if (volumeWasUploaded) { ++volumeUploadedToAtlasOctahedralDepthCount; } } if (volumeUploadedToAtlasSHCount == volumeUploadedToAtlasSHCapacity && volumeUploadedToAtlasOctahedralDepthCount == volumeUploadedToAtlasOctahedralDepthCapacity) { // Met our capacity this frame. Early out. break; } } return(probeVolumes); } }
DensityVolumeList PrepareVisibleDensityVolumeList(HDCamera hdCamera, CommandBuffer cmd, float time) { DensityVolumeList densityVolumes = new DensityVolumeList(); if (!hdCamera.frameSettings.IsEnabled(FrameSettingsField.Volumetrics)) { return(densityVolumes); } var visualEnvironment = VolumeManager.instance.stack.GetComponent <VisualEnvironment>(); if (visualEnvironment.fogType.value != FogType.Volumetric) { return(densityVolumes); } using (new ProfilingSample(cmd, "Prepare Visible Density Volume List")) { Vector3 camPosition = hdCamera.camera.transform.position; Vector3 camOffset = Vector3.zero;// World-origin-relative if (ShaderConfig.s_CameraRelativeRendering != 0) { camOffset = camPosition; // Camera-relative } m_VisibleVolumeBounds.Clear(); m_VisibleVolumeData.Clear(); // Collect all visible finite volume data, and upload it to the GPU. var volumes = DensityVolumeManager.manager.PrepareDensityVolumeData(cmd, hdCamera.camera, time); for (int i = 0; i < Math.Min(volumes.Count, k_MaxVisibleVolumeCount); i++) { DensityVolume volume = volumes[i]; // TODO: cache these? var obb = new OrientedBBox(Matrix4x4.TRS(volume.transform.position, volume.transform.rotation, volume.parameters.size)); // Handle camera-relative rendering. obb.center -= camOffset; // Frustum cull on the CPU for now. TODO: do it on the GPU. // TODO: account for custom near and far planes of the V-Buffer's frustum. // It's typically much shorter (along the Z axis) than the camera's frustum. if (GeometryUtils.Overlap(obb, hdCamera.frustum, 6, 8)) { // TODO: cache these? var data = volume.parameters.ConvertToEngineData(); m_VisibleVolumeBounds.Add(obb); m_VisibleVolumeData.Add(data); } } m_VisibleVolumeBoundsBuffer.SetData(m_VisibleVolumeBounds); m_VisibleVolumeDataBuffer.SetData(m_VisibleVolumeData); // Fill the struct with pointers in order to share the data with the light loop. densityVolumes.bounds = m_VisibleVolumeBounds; densityVolumes.density = m_VisibleVolumeData; return(densityVolumes); } }