static void OnBakeStarted() { if (!ProbeReferenceVolume.instance.isInitialized) { return; } var refVolAuthList = GameObject.FindObjectsOfType <ProbeReferenceVolumeAuthoring>(); if (refVolAuthList.Length == 0) { return; } FindWorldBounds(); refVolAuthList = GameObject.FindObjectsOfType <ProbeReferenceVolumeAuthoring>(); m_BakingReferenceVolumeAuthoring = GetCardinalAuthoringComponent(refVolAuthList); if (m_BakingReferenceVolumeAuthoring == null) { Debug.Log("Scene(s) have multiple inconsistent ProbeReferenceVolumeAuthoring components. Please ensure they use identical profiles and transforms before baking."); return; } RunPlacement(); }
// NOTE: This is somewhat hacky and is going to likely be slow (or at least slower than it could). // It is only a first iteration of the concept that won't be as impactful on memory as other options. internal static void RevertDilation() { var refVolAuthList = GameObject.FindObjectsOfType <ProbeReferenceVolumeAuthoring>(); m_BakingReferenceVolumeAuthoring = GetCardinalAuthoringComponent(refVolAuthList); m_BakingReferenceVolumeAuthoring = GetCardinalAuthoringComponent(refVolAuthList); if (m_BakingReferenceVolumeAuthoring == null) { return; } var dilationSettings = m_BakingReferenceVolumeAuthoring.GetDilationSettings(); foreach (var cell in ProbeReferenceVolume.instance.cells.Values) { for (int i = 0; i < cell.validity.Length; ++i) { if (dilationSettings.dilationDistance > 0.0f && cell.validity[i] > dilationSettings.dilationValidityThreshold) { for (int k = 0; k < 9; ++k) { cell.sh[i][0, k] = 0.0f; cell.sh[i][1, k] = 0.0f; cell.sh[i][2, k] = 0.0f; } } } } }
static void OnAdditionalProbesBakeCompleted() { UnityEditor.Experimental.Lightmapping.additionalBakedProbesCompleted -= OnAdditionalProbesBakeCompleted; var bakingCells = m_BakingBatch.cells; var numCells = bakingCells.Count; int numUniqueProbes = m_BakingBatch.uniqueProbeCount; var sh = new NativeArray <SphericalHarmonicsL2>(numUniqueProbes, Allocator.Temp, NativeArrayOptions.UninitializedMemory); var validity = new NativeArray <float>(numUniqueProbes, Allocator.Temp, NativeArrayOptions.UninitializedMemory); var bakedProbeOctahedralDepth = new NativeArray <float>(numUniqueProbes * 64, Allocator.Temp, NativeArrayOptions.UninitializedMemory); UnityEditor.Experimental.Lightmapping.GetAdditionalBakedProbes(m_BakingBatch.index, sh, validity, bakedProbeOctahedralDepth); // Fetch results of all cells for (int c = 0; c < numCells; ++c) { var cell = bakingCells[c].cell; if (cell.probePositions == null) { continue; } int numProbes = cell.probePositions.Length; Debug.Assert(numProbes > 0); cell.sh = new SphericalHarmonicsL2[numProbes]; cell.validity = new float[numProbes]; for (int i = 0; i < numProbes; ++i) { int j = bakingCells[c].probeIndices[i]; SphericalHarmonicsL2 shv = sh[j]; // Compress the range of all coefficients but the DC component to [0..1] // Upper bounds taken from http://ppsloan.org/publications/Sig20_Advances.pptx // Divide each coefficient by DC*f to get to [-1,1] where f is from slide 33 for (int rgb = 0; rgb < 3; ++rgb) { var l0 = sh[j][rgb, 0]; if (l0 == 0.0f) { continue; } // TODO: We're working on irradiance instead of radiance coefficients // Add safety margin 2 to avoid out-of-bounds values float l1scale = 2.0f; // Should be: 3/(2*sqrt(3)) * 2, but rounding to 2 to issues we are observing. float l2scale = 3.5777088f; // 4/sqrt(5) * 2 // L_1^m shv[rgb, 1] = sh[j][rgb, 1] / (l0 * l1scale * 2.0f) + 0.5f; shv[rgb, 2] = sh[j][rgb, 2] / (l0 * l1scale * 2.0f) + 0.5f; shv[rgb, 3] = sh[j][rgb, 3] / (l0 * l1scale * 2.0f) + 0.5f; // L_2^-2 shv[rgb, 4] = sh[j][rgb, 4] / (l0 * l2scale * 2.0f) + 0.5f; shv[rgb, 5] = sh[j][rgb, 5] / (l0 * l2scale * 2.0f) + 0.5f; shv[rgb, 6] = sh[j][rgb, 6] / (l0 * l2scale * 2.0f) + 0.5f; shv[rgb, 7] = sh[j][rgb, 7] / (l0 * l2scale * 2.0f) + 0.5f; shv[rgb, 8] = sh[j][rgb, 8] / (l0 * l2scale * 2.0f) + 0.5f; for (int coeff = 1; coeff < 9; ++coeff) { Debug.Assert(shv[rgb, coeff] >= 0.0f && shv[rgb, coeff] <= 1.0f); } } SphericalHarmonicsL2Utils.SetL0(ref cell.sh[i], new Vector3(shv[0, 0], shv[1, 0], shv[2, 0])); SphericalHarmonicsL2Utils.SetL1R(ref cell.sh[i], new Vector3(shv[0, 3], shv[0, 1], shv[0, 2])); SphericalHarmonicsL2Utils.SetL1G(ref cell.sh[i], new Vector3(shv[1, 3], shv[1, 1], shv[1, 2])); SphericalHarmonicsL2Utils.SetL1B(ref cell.sh[i], new Vector3(shv[2, 3], shv[2, 1], shv[2, 2])); SphericalHarmonicsL2Utils.SetCoefficient(ref cell.sh[i], 4, new Vector3(shv[0, 4], shv[1, 4], shv[2, 4])); SphericalHarmonicsL2Utils.SetCoefficient(ref cell.sh[i], 5, new Vector3(shv[0, 5], shv[1, 5], shv[2, 5])); SphericalHarmonicsL2Utils.SetCoefficient(ref cell.sh[i], 6, new Vector3(shv[0, 6], shv[1, 6], shv[2, 6])); SphericalHarmonicsL2Utils.SetCoefficient(ref cell.sh[i], 7, new Vector3(shv[0, 7], shv[1, 7], shv[2, 7])); SphericalHarmonicsL2Utils.SetCoefficient(ref cell.sh[i], 8, new Vector3(shv[0, 8], shv[1, 8], shv[2, 8])); cell.validity[i] = validity[j]; } DilateInvalidProbes(cell.probePositions, cell.bricks, cell.sh, cell.validity, m_BakingReferenceVolumeAuthoring.GetDilationSettings()); ProbeReferenceVolume.instance.cells[cell.index] = cell; } m_BakingBatchIndex = 0; // Reset index UnityEditor.Experimental.Lightmapping.SetAdditionalBakedProbes(m_BakingBatch.index, null); // Map from each scene to an existing reference volume var scene2RefVol = new Dictionary <Scene, ProbeReferenceVolumeAuthoring>(); foreach (var refVol in GameObject.FindObjectsOfType <ProbeReferenceVolumeAuthoring>()) { if (refVol.enabled) { scene2RefVol[refVol.gameObject.scene] = refVol; } } // Map from each reference volume to its asset var refVol2Asset = new Dictionary <ProbeReferenceVolumeAuthoring, ProbeVolumeAsset>(); foreach (var refVol in scene2RefVol.Values) { refVol2Asset[refVol] = ProbeVolumeAsset.CreateAsset(refVol.gameObject.scene); } // Put cells into the respective assets foreach (var cell in ProbeReferenceVolume.instance.cells.Values) { foreach (var scene in m_BakingBatch.cellIndex2SceneReferences[cell.index]) { // This scene has a reference volume authoring component in it? ProbeReferenceVolumeAuthoring refVol = null; if (scene2RefVol.TryGetValue(scene, out refVol)) { var asset = refVol2Asset[refVol]; asset.cells.Add(cell); if (hasFoundBounds) { Vector3Int cellsInDir; int cellSizeInMeters = Mathf.CeilToInt((float)refVol.profile.cellSizeInBricks * refVol.profile.brickSize); CellCountInDirections(out cellsInDir, cellSizeInMeters); asset.maxCellIndex.x = cellsInDir.x * (int)refVol.profile.cellSizeInBricks; asset.maxCellIndex.y = cellsInDir.y * (int)refVol.profile.cellSizeInBricks; asset.maxCellIndex.z = cellsInDir.z * (int)refVol.profile.cellSizeInBricks; } else { foreach (var p in cell.probePositions) { float x = Mathf.Abs((float)p.x + refVol.transform.position.x) / refVol.profile.brickSize; float y = Mathf.Abs((float)p.y + refVol.transform.position.y) / refVol.profile.brickSize; float z = Mathf.Abs((float)p.z + refVol.transform.position.z) / refVol.profile.brickSize; asset.maxCellIndex.x = Mathf.Max(asset.maxCellIndex.x, (int)(x * 2)); asset.maxCellIndex.y = Mathf.Max(asset.maxCellIndex.y, (int)(y * 2)); asset.maxCellIndex.z = Mathf.Max(asset.maxCellIndex.z, (int)(z * 2)); } } } } } // Connect the assets to their components foreach (var pair in refVol2Asset) { var refVol = pair.Key; var asset = pair.Value; refVol.volumeAsset = asset; if (UnityEditor.Lightmapping.giWorkflowMode != UnityEditor.Lightmapping.GIWorkflowMode.Iterative) { UnityEditor.EditorUtility.SetDirty(refVol); UnityEditor.EditorUtility.SetDirty(refVol.volumeAsset); } } var probeVolumes = GameObject.FindObjectsOfType <ProbeVolume>(); foreach (var probeVolume in probeVolumes) { probeVolume.OnBakeCompleted(); } UnityEditor.AssetDatabase.SaveAssets(); UnityEditor.AssetDatabase.Refresh(); ProbeReferenceVolume.instance.clearAssetsOnVolumeClear = false; foreach (var refVol in refVol2Asset.Keys) { if (refVol.enabled && refVol.gameObject.activeSelf) { refVol.QueueAssetLoading(); } } }
public override void OnInspectorGUI() { var renderPipelineAsset = GraphicsSettings.renderPipelineAsset; if (renderPipelineAsset != null && renderPipelineAsset.GetType().Name == "HDRenderPipelineAsset") { serializedObject.Update(); if (!ProbeReferenceVolume.instance.isInitialized) { EditorGUILayout.HelpBox("The probe volumes feature is disabled. The feature needs to be enabled in the HDRP Settings and on the used HDRP asset.", MessageType.Warning, wide: true); return; } var probeReferenceVolumes = FindObjectsOfType <ProbeReferenceVolumeAuthoring>(); bool mismatchedProfile = false; if (probeReferenceVolumes.Length > 1) { foreach (var o1 in probeReferenceVolumes) { foreach (var o2 in probeReferenceVolumes) { if (!o1.profile.IsEquivalent(o2.profile)) { mismatchedProfile = true; } } } if (mismatchedProfile) { EditorGUILayout.HelpBox("Multiple Probe Reference Volume components are loaded, but they have different profiles. " + "This is unsupported, please make sure all loaded Probe Reference Volume have the same profile or profiles with equal values.", MessageType.Error, wide: true); } } EditorGUI.BeginChangeCheck(); // The layout system breaks alignment when mixing inspector fields with custom layout'd // fields, do the layout manually instead int buttonWidth = 60; float indentOffset = EditorGUI.indentLevel * 15f; var lineRect = EditorGUILayout.GetControlRect(); var labelRect = new Rect(lineRect.x, lineRect.y, EditorGUIUtility.labelWidth - indentOffset, lineRect.height); var fieldRect = new Rect(labelRect.xMax, lineRect.y, lineRect.width - labelRect.width - buttonWidth, lineRect.height); var buttonNewRect = new Rect(fieldRect.xMax, lineRect.y, buttonWidth, lineRect.height); GUIContent guiContent = EditorGUIUtility.TrTextContent("Profile", "A reference to a profile asset."); EditorGUI.PrefixLabel(labelRect, guiContent); using (var scope = new EditorGUI.ChangeCheckScope()) { EditorGUI.BeginProperty(fieldRect, GUIContent.none, m_Profile); m_Profile.objectReferenceValue = (ProbeReferenceVolumeProfile)EditorGUI.ObjectField(fieldRect, m_Profile.objectReferenceValue, typeof(ProbeReferenceVolumeProfile), false); EditorGUI.EndProperty(); } if (GUI.Button(buttonNewRect, EditorGUIUtility.TrTextContent("New", "Create a new profile."), EditorStyles.miniButton)) { // By default, try to put assets in a folder next to the currently active // scene file. If the user isn't a scene, put them in root instead. var targetName = actualTarget.name; var scene = actualTarget.gameObject.scene; var asset = ProbeReferenceVolumeAuthoring.CreateReferenceVolumeProfile(scene, targetName); m_Profile.objectReferenceValue = asset; } m_VolumeAsset.objectReferenceValue = EditorGUILayout.ObjectField(s_DataAssetLabel, m_VolumeAsset.objectReferenceValue, typeof(ProbeVolumeAsset), false); DilationGroupEnabled = EditorGUILayout.BeginFoldoutHeaderGroup(DilationGroupEnabled, "Dilation"); if (DilationGroupEnabled) { m_Dilate.boolValue = EditorGUILayout.Toggle("Dilate", m_Dilate.boolValue); EditorGUI.BeginDisabledGroup(!m_Dilate.boolValue); m_MaxDilationSamples.intValue = EditorGUILayout.IntField("Max Dilation Samples", m_MaxDilationSamples.intValue); m_MaxDilationSampleDistance.floatValue = EditorGUILayout.FloatField("Max Dilation Sample Distance", m_MaxDilationSampleDistance.floatValue); DilationValidityThresholdInverted = EditorGUILayout.Slider("Dilation Validity Threshold", DilationValidityThresholdInverted, 0f, 1f); m_GreedyDilation.boolValue = EditorGUILayout.Toggle("Greedy Dilation", m_GreedyDilation.boolValue); EditorGUI.EndDisabledGroup(); } EditorGUILayout.EndFoldoutHeaderGroup(); if (EditorGUI.EndChangeCheck()) { Constrain(); serializedObject.ApplyModifiedProperties(); } } else { EditorGUILayout.HelpBox("Probe Volume is not a supported feature by this SRP.", MessageType.Error, wide: true); } }
// Can definitively be optimized later on. // Also note that all the bookkeeping of all the reference volumes will likely need to change when we move to // proper UX. internal static void PerformDilation() { HashSet <ProbeReferenceVolumeAuthoring> refVols = new HashSet <ProbeReferenceVolumeAuthoring>(); Dictionary <int, List <string> > cell2Assets = new Dictionary <int, List <string> >(); var refVolAuthList = GameObject.FindObjectsOfType <ProbeReferenceVolumeAuthoring>(); m_BakingReferenceVolumeAuthoring = GetCardinalAuthoringComponent(refVolAuthList); if (m_BakingReferenceVolumeAuthoring == null) { return; } foreach (var refVol in refVolAuthList) { if (m_BakingReferenceVolumeAuthoring == null) { m_BakingReferenceVolumeAuthoring = refVol; } if (refVol.enabled) { refVols.Add(refVol); } } foreach (var refVol in refVols) { if (refVol.volumeAsset != null) { string assetPath = refVol.volumeAsset.GetSerializedFullPath(); foreach (var cell in refVol.volumeAsset.cells) { if (!cell2Assets.ContainsKey(cell.index)) { cell2Assets.Add(cell.index, new List <string>()); } cell2Assets[cell.index].Add(assetPath); } } } var dilationSettings = m_BakingReferenceVolumeAuthoring.GetDilationSettings(); if (dilationSettings.dilationDistance > 0.0f) { // TODO: This loop is very naive, can be optimized, but let's first verify if we indeed want this or not. for (int iterations = 0; iterations < dilationSettings.dilationIterations; ++iterations) { // Make sure all is loaded before performing dilation. ProbeReferenceVolume.instance.PerformPendingOperations(loadAllCells: true); // Dilate all cells List <ProbeReferenceVolume.Cell> dilatedCells = new List <ProbeReferenceVolume.Cell>(ProbeReferenceVolume.instance.cells.Values.Count); foreach (var cell in ProbeReferenceVolume.instance.cells.Values) { PerformDilation(cell, dilationSettings); dilatedCells.Add(cell); } foreach (var refVol in refVols) { if (refVol != null && refVol.volumeAsset != null) { ProbeReferenceVolume.instance.AddPendingAssetRemoval(refVol.volumeAsset); } } // Make sure unloading happens. ProbeReferenceVolume.instance.PerformPendingOperations(); Dictionary <string, bool> assetCleared = new Dictionary <string, bool>(); // Put back cells foreach (var cell in dilatedCells) { foreach (var refVol in refVols) { if (refVol.volumeAsset == null) { continue; } var asset = refVol.volumeAsset; var assetPath = asset.GetSerializedFullPath(); bool valueFound = false; if (!assetCleared.TryGetValue(assetPath, out valueFound)) { asset.cells.Clear(); assetCleared.Add(asset.GetSerializedFullPath(), true); UnityEditor.EditorUtility.SetDirty(asset); } if (cell2Assets[cell.index].Contains(assetPath)) { asset.cells.Add(cell); } } } UnityEditor.AssetDatabase.SaveAssets(); UnityEditor.AssetDatabase.Refresh(); foreach (var refVol in refVols) { if (refVol.enabled && refVol.gameObject.activeSelf) { refVol.QueueAssetLoading(); } } } } }