public static void GetSphericalHarmonicsL1FromIndex(ref SphericalHarmonicsL1 sh, ref ProbeVolumePayload payload, int indexProbe) { int strideSHL01 = GetDataSHL01Stride(); int indexDataBaseSHL01 = indexProbe * strideSHL01; int indexDataEndSHL01 = indexDataBaseSHL01 + strideSHL01; Debug.Assert(payload.dataSHL01 != null); Debug.Assert(payload.dataSHL01.Length >= indexDataEndSHL01); // Constant (DC terms): sh.shAr.w = payload.dataSHL01[indexDataBaseSHL01 + 0]; // shAr.w sh.shAg.w = payload.dataSHL01[indexDataBaseSHL01 + 1]; // shAg.w sh.shAb.w = payload.dataSHL01[indexDataBaseSHL01 + 2]; // shAb.w // Linear: (used by L1 and L2) // Swizzle the coefficients to be in { x, y, z } order. sh.shAr.x = payload.dataSHL01[indexDataBaseSHL01 + 3]; // shAr.x sh.shAr.y = payload.dataSHL01[indexDataBaseSHL01 + 4]; // shAr.y sh.shAr.z = payload.dataSHL01[indexDataBaseSHL01 + 5]; // shAr.z sh.shAg.x = payload.dataSHL01[indexDataBaseSHL01 + 6]; // shAg.x sh.shAg.y = payload.dataSHL01[indexDataBaseSHL01 + 7]; // shAg.y sh.shAg.z = payload.dataSHL01[indexDataBaseSHL01 + 8]; // shAg.z sh.shAb.x = payload.dataSHL01[indexDataBaseSHL01 + 9]; // shAb.x sh.shAb.y = payload.dataSHL01[indexDataBaseSHL01 + 10]; // shAb.y sh.shAb.z = payload.dataSHL01[indexDataBaseSHL01 + 11]; // shAb.z }
private static void DilateInvalidProbes(Vector3[] probePositions, List <Brick> bricks, SphericalHarmonicsL1[] sh, float[] validity, ProbeDilationSettings dilationSettings) { // For each brick List <DilationProbe> culledProbes = new List <DilationProbe>(); List <DilationProbe> nearProbes = new List <DilationProbe>(dilationSettings.maxDilationSamples); for (int brickIdx = 0; brickIdx < bricks.Count; brickIdx++) { // Find probes that are in bricks nearby CullDilationProbes(brickIdx, bricks, validity, dilationSettings, culledProbes); // Iterate probes in current brick for (int probeOffset = 0; probeOffset < 64; probeOffset++) { int probeIdx = brickIdx * 64 + probeOffset; // Skip valid probes if (validity[probeIdx] <= dilationSettings.dilationValidityThreshold) { continue; } // Find distance weighted probes nearest to current probe FindNearProbes(probeIdx, probePositions, dilationSettings, culledProbes, nearProbes, out float invDistSum); // Set invalid probe to weighted average of found neighboring probes var shAverage = new SphericalHarmonicsL1(); for (int nearProbeIdx = 0; nearProbeIdx < nearProbes.Count; nearProbeIdx++) { var nearProbe = nearProbes[nearProbeIdx]; float weight = nearProbe.dist / invDistSum; var target = sh[nearProbe.idx]; shAverage.shAr += target.shAr * weight; shAverage.shAg += target.shAg * weight; shAverage.shAb += target.shAb * weight; } sh[probeIdx] = shAverage; validity[probeIdx] = validity[probeIdx]; } } }
private static void OnAdditionalProbesBakeCompleted() { UnityEditor.Experimental.Lightmapping.additionalBakedProbesCompleted -= OnAdditionalProbesBakeCompleted; var numCells = bakingCells.Count; // Fetch results of all cells for (int c = 0; c < numCells; ++c) { var cell = bakingCells[c]; if (cell.probePositions == null) { continue; } int numProbes = cell.probePositions.Length; Debug.Assert(numProbes > 0); var sh = new NativeArray <SphericalHarmonicsL2>(numProbes, Allocator.Temp, NativeArrayOptions.UninitializedMemory); var validity = new NativeArray <float>(numProbes, Allocator.Temp, NativeArrayOptions.UninitializedMemory); var bakedProbeOctahedralDepth = new NativeArray <float>(numProbes * 64, Allocator.Temp, NativeArrayOptions.UninitializedMemory); UnityEditor.Experimental.Lightmapping.GetAdditionalBakedProbes(cell.index, sh, validity, bakedProbeOctahedralDepth); cell.sh = new SphericalHarmonicsL1[numProbes]; cell.validity = new float[numProbes]; for (int i = 0; i < numProbes; ++i) { Vector4[] channels = new Vector4[3]; // compare to SphericalHarmonicsL2::GetShaderConstantsFromNormalizedSH channels[0] = new Vector4(sh[i][0, 3], sh[i][0, 1], sh[i][0, 2], sh[i][0, 0]); channels[1] = new Vector4(sh[i][1, 3], sh[i][1, 1], sh[i][1, 2], sh[i][1, 0]); channels[2] = new Vector4(sh[i][2, 3], sh[i][2, 1], sh[i][2, 2], sh[i][2, 0]); // It can be shown that |L1_i| <= |2*L0| // Precomputed Global Illumination in Frostbite by Yuriy O'Donnell. // https://media.contentapi.ea.com/content/dam/eacom/frostbite/files/gdc2018-precomputedgiobalilluminationinfrostbite.pdf // // So divide by L0 brings us to [-2, 2], // divide by 4 brings us to [-0.5, 0.5], // and plus by 0.5 brings us to [0, 1]. for (int channel = 0; channel < 3; ++channel) { var l0 = channels[channel][3]; if (l0 != 0.0f) { for (int axis = 0; axis < 3; ++axis) { channels[channel][axis] = channels[channel][axis] / (l0 * 4.0f) + 0.5f; Debug.Assert(channels[channel][axis] >= 0.0f && channels[channel][axis] <= 1.0f); } } } SphericalHarmonicsL1 sh1 = new SphericalHarmonicsL1(); sh1.shAr = channels[0]; sh1.shAg = channels[1]; sh1.shAb = channels[2]; cell.sh[i] = sh1; cell.validity[i] = validity[i]; } // Reset index UnityEditor.Experimental.Lightmapping.SetAdditionalBakedProbes(cell.index, null); DilateInvalidProbes(cell.probePositions, cell.bricks, cell.sh, cell.validity, bakingReferenceVolumeAuthoring.GetDilationSettings()); ProbeReferenceVolume.instance.cells[cell.index] = cell; } // 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 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); } } } // 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); } } UnityEditor.AssetDatabase.SaveAssets(); UnityEditor.AssetDatabase.Refresh(); foreach (var refVol in refVol2Asset.Keys) { if (refVol.enabled && refVol.gameObject.activeSelf) { refVol.QueueAssetLoading(); } } }
public static void SetSphericalHarmonicsL1FromIndex(ref ProbeVolumePayload payload, SphericalHarmonicsL1 sh, int indexProbe) { int strideSHL01 = GetDataSHL01Stride(); int indexDataBaseSHL01 = indexProbe * strideSHL01; int indexDataEndSHL01 = indexDataBaseSHL01 + strideSHL01; Debug.Assert(payload.dataSHL01 != null); Debug.Assert(payload.dataSHL01.Length >= indexDataEndSHL01); int strideSHL2 = GetDataSHL2Stride(); int indexDataBaseSHL2 = indexProbe * strideSHL2; int indexDataEndSHL2 = indexDataBaseSHL2 + strideSHL2; Debug.Assert(payload.dataSHL2 != null); Debug.Assert(payload.dataSHL2.Length >= indexDataEndSHL2); // Constant (DC terms): payload.dataSHL01[indexDataBaseSHL01 + 0] = sh.shAr.w; payload.dataSHL01[indexDataBaseSHL01 + 1] = sh.shAg.w; payload.dataSHL01[indexDataBaseSHL01 + 2] = sh.shAb.w; // Linear: (used by L1 and L2) // Swizzle the coefficients to be in { x, y, z } order. payload.dataSHL01[indexDataBaseSHL01 + 3] = sh.shAr.x; payload.dataSHL01[indexDataBaseSHL01 + 4] = sh.shAr.y; payload.dataSHL01[indexDataBaseSHL01 + 5] = sh.shAr.z; payload.dataSHL01[indexDataBaseSHL01 + 6] = sh.shAg.x; payload.dataSHL01[indexDataBaseSHL01 + 7] = sh.shAg.y; payload.dataSHL01[indexDataBaseSHL01 + 8] = sh.shAg.z; payload.dataSHL01[indexDataBaseSHL01 + 9] = sh.shAb.x; payload.dataSHL01[indexDataBaseSHL01 + 10] = sh.shAb.y; payload.dataSHL01[indexDataBaseSHL01 + 11] = sh.shAb.z; // Quadratic: (used by L2) payload.dataSHL2[indexDataBaseSHL2 + 0] = 0.0f; // shBr.x payload.dataSHL2[indexDataBaseSHL2 + 1] = 0.0f; // shBr.y payload.dataSHL2[indexDataBaseSHL2 + 2] = 0.0f; // shBr.z payload.dataSHL2[indexDataBaseSHL2 + 3] = 0.0f; // shBr.w payload.dataSHL2[indexDataBaseSHL2 + 4] = 0.0f; // shBg.x payload.dataSHL2[indexDataBaseSHL2 + 5] = 0.0f; // shBg.y payload.dataSHL2[indexDataBaseSHL2 + 6] = 0.0f; // shBg.z payload.dataSHL2[indexDataBaseSHL2 + 7] = 0.0f; // shBg.w payload.dataSHL2[indexDataBaseSHL2 + 8] = 0.0f; // shBb.x payload.dataSHL2[indexDataBaseSHL2 + 9] = 0.0f; // shBb.y payload.dataSHL2[indexDataBaseSHL2 + 10] = 0.0f; // shBb.z payload.dataSHL2[indexDataBaseSHL2 + 11] = 0.0f; // shBb.w payload.dataSHL2[indexDataBaseSHL2 + 12] = 0.0f; // shCr.x payload.dataSHL2[indexDataBaseSHL2 + 13] = 0.0f; // shCr.y payload.dataSHL2[indexDataBaseSHL2 + 14] = 0.0f; // shCr.z }
internal void OnProbesBakeCompleted() { if (this.gameObject == null || !this.gameObject.activeInHierarchy) { return; } int numProbes = parameters.resolutionX * parameters.resolutionY * parameters.resolutionZ; SphericalHarmonicsL1[] data = new SphericalHarmonicsL1[numProbes]; float[] dataValidity = new float[numProbes]; float[] dataOctahedralDepth = new float[numProbes * 8 * 8]; var sh = new NativeArray <SphericalHarmonicsL2>(numProbes, Allocator.Temp, NativeArrayOptions.UninitializedMemory); var validity = new NativeArray <float>(numProbes, Allocator.Temp, NativeArrayOptions.UninitializedMemory); var octahedralDepth = new NativeArray <float>(numProbes * 8 * 8, Allocator.Temp, NativeArrayOptions.UninitializedMemory); if (UnityEditor.Experimental.Lightmapping.GetAdditionalBakedProbes(GetID(), sh, validity, octahedralDepth)) { // TODO: Remove this data copy. for (int i = 0, iLen = data.Length; i < iLen; ++i) { data[i].shAr = new Vector4(sh[i][0, 3], sh[i][0, 1], sh[i][0, 2], sh[i][0, 0] - sh[i][0, 6]); data[i].shAg = new Vector4(sh[i][1, 3], sh[i][1, 1], sh[i][1, 2], sh[i][1, 0] - sh[i][1, 6]); data[i].shAb = new Vector4(sh[i][2, 3], sh[i][2, 1], sh[i][2, 2], sh[i][2, 0] - sh[i][2, 6]); dataValidity[i] = validity[i]; for (int j = 0; j < 64; ++j) { dataOctahedralDepth[i * 64 + j] = octahedralDepth[i * 64 + j]; } } if (!probeVolumeAsset || GetID() != probeVolumeAsset.instanceID) { probeVolumeAsset = ProbeVolumeAsset.CreateAsset(GetID()); } probeVolumeAsset.instanceID = GetID(); probeVolumeAsset.dataSH = data; probeVolumeAsset.dataValidity = dataValidity; probeVolumeAsset.dataOctahedralDepth = dataOctahedralDepth; probeVolumeAsset.resolutionX = parameters.resolutionX; probeVolumeAsset.resolutionY = parameters.resolutionY; probeVolumeAsset.resolutionZ = parameters.resolutionZ; if (UnityEditor.Lightmapping.giWorkflowMode != UnityEditor.Lightmapping.GIWorkflowMode.Iterative) { UnityEditor.EditorUtility.SetDirty(probeVolumeAsset); } UnityEditor.AssetDatabase.Refresh(); dataUpdated = true; } sh.Dispose(); validity.Dispose(); octahedralDepth.Dispose(); }