Skip to content

Commit ceb9a6c

Browse files
committed
Probe Volumes: Reflection Probe Normalization by Probe Volumes added. In order to enable, NormalizeReflectionProbeWithProbeVolume needs to be enabled in the Frame Settings for the Camera, and scenes need to be rebaked. Options exist on the ProbeVolumeController volume override. (#27)
Co-authored-by: pastasfuture <[email protected]>
1 parent 983e6d8 commit ceb9a6c

18 files changed

+793
-97
lines changed
Lines changed: 213 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,213 @@
1+
#if UNITY_EDITOR
2+
using System;
3+
using System.Collections.Generic;
4+
using UnityEngine.Profiling;
5+
using UnityEngine.SceneManagement;
6+
using Unity.Collections;
7+
using UnityEditor;
8+
9+
namespace UnityEngine.Rendering.HighDefinition
10+
{
11+
/// <summary>
12+
/// A manager to enqueue extra probe rendering outside of probe volumes.
13+
/// </summary>
14+
public class AdditionalGIBakeRequestsManager
15+
{
16+
// The baking ID for the extra requests
17+
// TODO: Need to ensure this never conflicts with bake IDs from others interacting with the API.
18+
// In our project, this is ProbeVolumes.
19+
internal static readonly int s_BakingID = 912345678;
20+
21+
private static AdditionalGIBakeRequestsManager s_Instance = new AdditionalGIBakeRequestsManager();
22+
/// <summary>
23+
/// Get the manager that governs the additional light probe rendering requests.
24+
/// </summary>
25+
public static AdditionalGIBakeRequestsManager instance { get { return s_Instance; } }
26+
27+
private AdditionalGIBakeRequestsManager()
28+
{
29+
SubscribeOnBakeStarted();
30+
}
31+
32+
~AdditionalGIBakeRequestsManager()
33+
{
34+
UnsubscribeOnBakeStarted();
35+
}
36+
37+
private static List<SphericalHarmonicsL2> m_SHCoefficients = new List<SphericalHarmonicsL2>();
38+
private static List<Vector3> m_RequestPositions = new List<Vector3>();
39+
private static int m_FreelistHead = -1;
40+
41+
private static readonly Vector2 s_FreelistSentinel = new Vector2(float.MaxValue, float.MaxValue);
42+
43+
/// <summary>
44+
/// Enqueue a request for probe rendering at the specified location.
45+
/// </summary>
46+
/// <param name ="capturePosition"> The position at which a probe is baked.</param>
47+
/// <returns>An ID that can be used to retrieve the data once it has been computed</returns>
48+
public int EnqueueRequest(Vector3 capturePosition)
49+
{
50+
Debug.Assert(ComputeCapturePositionIsValid(capturePosition));
51+
52+
if (m_FreelistHead >= 0)
53+
{
54+
int requestID = m_FreelistHead;
55+
Debug.Assert(requestID < m_RequestPositions.Count);
56+
m_FreelistHead = ComputeFreelistNext(m_RequestPositions[requestID]);
57+
m_RequestPositions[requestID] = capturePosition;
58+
m_SHCoefficients[requestID] = new SphericalHarmonicsL2();
59+
return requestID;
60+
}
61+
else
62+
{
63+
int requestID = m_RequestPositions.Count;
64+
m_RequestPositions.Add(capturePosition);
65+
m_SHCoefficients.Add(new SphericalHarmonicsL2());
66+
return requestID;
67+
}
68+
}
69+
70+
/// <summary>
71+
/// Enqueue a request for probe rendering at the specified location.
72+
/// </summary>
73+
/// <param name ="requestID"> An ID that can be used to retrieve the data once it has been computed</param>
74+
/// <returns>An ID that can be used to retrieve the data once it has been computed</returns>
75+
public void DequeueRequest(int requestID)
76+
{
77+
Debug.Assert(requestID >= 0 && requestID < m_RequestPositions.Count);
78+
79+
m_RequestPositions[requestID] = new Vector3(s_FreelistSentinel.x, s_FreelistSentinel.y, m_FreelistHead);
80+
m_SHCoefficients[requestID] = new SphericalHarmonicsL2();
81+
m_FreelistHead = requestID;
82+
}
83+
84+
private bool ComputeCapturePositionIsValid(Vector3 capturePosition)
85+
{
86+
return !((capturePosition.x == s_FreelistSentinel.x) && (capturePosition.y == s_FreelistSentinel.y));
87+
}
88+
89+
private int ComputeFreelistNext(Vector3 capturePosition)
90+
{
91+
Debug.Assert(ComputeRequestIsFree(capturePosition));
92+
93+
int freelistNext = (int)capturePosition.z;
94+
Debug.Assert(freelistNext >= -1 && freelistNext < m_RequestPositions.Count);
95+
return freelistNext;
96+
}
97+
98+
private bool ComputeRequestIsFree(int requestID)
99+
{
100+
Debug.Assert(requestID >= 0 && requestID < m_RequestPositions.Count);
101+
Vector3 requestPosition = m_RequestPositions[requestID];
102+
return ComputeRequestIsFree(requestPosition);
103+
}
104+
105+
private bool ComputeRequestIsFree(Vector3 capturePosition)
106+
{
107+
return (capturePosition.x == s_FreelistSentinel.x) && (capturePosition.y == s_FreelistSentinel.y);
108+
}
109+
110+
/// <summary>
111+
/// Retrieve the result of a capture request, it will return false if the request has not been fulfilled yet or the request ID is invalid.
112+
/// </summary>
113+
/// <param name ="requestID"> The request ID that has been given by the manager through a previous EnqueueRequest.</param>
114+
/// <param name ="sh"> The output SH coefficients that have been computed.</param>
115+
/// <returns>Whether the request for light probe rendering has been fulfilled and sh is valid.</returns>
116+
public bool RetrieveProbeSH(int requestID, out SphericalHarmonicsL2 sh)
117+
{
118+
if (requestID >= 0 && requestID < m_SHCoefficients.Count
119+
&& ComputeCapturePositionIsValid(m_RequestPositions[requestID]))
120+
{
121+
sh = m_SHCoefficients[requestID];
122+
return true;
123+
}
124+
else
125+
{
126+
sh = new SphericalHarmonicsL2();
127+
return false;
128+
}
129+
}
130+
131+
/// <summary>
132+
/// Update the capture location for the probe request.
133+
/// </summary>
134+
/// <param name ="requestID"> The request ID that has been given by the manager through a previous EnqueueRequest.</param>
135+
/// <param name ="newPositionnewPosition"> The position at which a probe is baked.</param>
136+
public int UpdatePositionForRequest(int requestID, Vector3 newPosition)
137+
{
138+
if (requestID >= 0 && requestID < m_RequestPositions.Count)
139+
{
140+
Debug.Assert(ComputeCapturePositionIsValid(m_RequestPositions[requestID]));
141+
m_RequestPositions[requestID] = newPosition;
142+
m_SHCoefficients[requestID] = new SphericalHarmonicsL2();
143+
return requestID;
144+
}
145+
else
146+
{
147+
return EnqueueRequest(newPosition);
148+
}
149+
}
150+
151+
private void SubscribeOnBakeStarted()
152+
{
153+
UnsubscribeOnBakeStarted();
154+
Lightmapping.bakeStarted += AddRequestsToLightmapper;
155+
}
156+
157+
private void UnsubscribeOnBakeStarted()
158+
{
159+
Lightmapping.bakeStarted -= AddRequestsToLightmapper;
160+
RemoveRequestsFromLightmapper();
161+
}
162+
163+
internal void AddRequestsToLightmapper()
164+
{
165+
UnityEditor.Experimental.Lightmapping.SetAdditionalBakedProbes(s_BakingID, m_RequestPositions.ToArray());
166+
167+
Lightmapping.bakeCompleted -= OnAdditionalProbesBakeCompleted;
168+
Lightmapping.bakeCompleted += OnAdditionalProbesBakeCompleted;
169+
}
170+
171+
private void RemoveRequestsFromLightmapper()
172+
{
173+
UnityEditor.Experimental.Lightmapping.SetAdditionalBakedProbes(s_BakingID, null);
174+
}
175+
176+
private void OnAdditionalProbesBakeCompleted()
177+
{
178+
Lightmapping.bakeCompleted -= OnAdditionalProbesBakeCompleted;
179+
180+
var sh = new NativeArray<SphericalHarmonicsL2>(m_RequestPositions.Count, Allocator.Temp, NativeArrayOptions.UninitializedMemory);
181+
var validity = new NativeArray<float>(m_RequestPositions.Count, Allocator.Temp, NativeArrayOptions.UninitializedMemory);
182+
var bakedProbeOctahedralDepth = new NativeArray<float>(m_RequestPositions.Count * 64, Allocator.Temp, NativeArrayOptions.UninitializedMemory);
183+
184+
UnityEditor.Experimental.Lightmapping.GetAdditionalBakedProbes(s_BakingID, sh, validity, bakedProbeOctahedralDepth);
185+
186+
SetSHCoefficients(sh);
187+
PushSHCoefficientsToReflectionProbes();
188+
189+
sh.Dispose();
190+
validity.Dispose();
191+
bakedProbeOctahedralDepth.Dispose();
192+
}
193+
194+
private void SetSHCoefficients(NativeArray<SphericalHarmonicsL2> sh)
195+
{
196+
Debug.Assert(sh.Length == m_SHCoefficients.Count);
197+
for (int i = 0; i < sh.Length; ++i)
198+
{
199+
m_SHCoefficients[i] = sh[i];
200+
}
201+
}
202+
203+
private void PushSHCoefficientsToReflectionProbes()
204+
{
205+
List<HDProbe> hdProbes = HDProbe.GetInstances();
206+
foreach (var hdProbe in hdProbes)
207+
{
208+
hdProbe.TryUpdateLuminanceSHL2ForNormalization();
209+
}
210+
}
211+
}
212+
}
213+
#endif

com.unity.render-pipelines.high-definition/Runtime/Lighting/AdditionalGIBakeRequestsManager.cs.meta

Lines changed: 11 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

com.unity.render-pipelines.high-definition/Runtime/Lighting/LightDefinition.cs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -267,6 +267,14 @@ struct EnvLightData
267267
public float distanceBasedRoughness;
268268
// Sampling properties
269269
public int envIndex;
270+
271+
// The luma SH for irradiance at probe location.
272+
public Vector4 L0L1;
273+
public Vector4 L2_1; // First 4 coeffs of L2 {-2, -1, 0, 1}
274+
public float L2_2; // Last L2 coeff {2}
275+
// Whether the probe is normalized by probe volume content.
276+
public int normalizeWithProbeVolumes;
277+
public Vector2 padding;
270278
};
271279

272280
[GenerateHLSL]

com.unity.render-pipelines.high-definition/Runtime/Lighting/LightDefinition.cs.hlsl

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,11 @@ struct EnvLightData
160160
float roughReflections;
161161
float distanceBasedRoughness;
162162
int envIndex;
163+
float4 L0L1;
164+
float4 L2_1;
165+
float L2_2;
166+
int normalizeWithProbeVolumes;
167+
float2 padding;
163168
};
164169

165170

com.unity.render-pipelines.high-definition/Runtime/Lighting/LightLoop/LightLoop.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2095,6 +2095,16 @@ internal bool GetEnvLightData(CommandBuffer cmd, HDCamera hdCamera, in Processed
20952095

20962096
envLightData.envIndex = envIndex;
20972097

2098+
envLightData.normalizeWithProbeVolumes = hdCamera.frameSettings.IsEnabled(FrameSettingsField.NormalizeReflectionProbeWithProbeVolume) ? 1 : 0;
2099+
if (envLightData.normalizeWithProbeVolumes > 0)
2100+
{
2101+
if (!probe.GetLuminanceSHL2ForNormalization(out envLightData.L0L1, out envLightData.L2_1, out envLightData.L2_2))
2102+
{
2103+
// We don't have valid data, hence we disable the feature.
2104+
envLightData.normalizeWithProbeVolumes = 0;
2105+
}
2106+
}
2107+
20982108
// Proxy data
20992109
var proxyToWorld = probe.proxyToWorld;
21002110
envLightData.proxyExtents = probe.proxyExtents;

0 commit comments

Comments
 (0)