Skip to content

Commit 068a86f

Browse files
committed
Probe Volumes:
Created a proper Probe Volume probe rendering shader. All probes in a selected probe volume are rendered in a single DrawProcedural call, 2 triangle per probe (screen space quads). Allows me to get rid of a lot of nasty mesh setup logic that was previously happening. The new Draw Probes debug shader currently has two additional options: Draw Validity, and Highlight Ringing (#14) Co-authored-by: pastasfuture <[email protected]> Probe Volumes: Use GlobalObjectId.targetObjectId (#16) * Probe Volumes: Use GlobalObjectId.targetObjectId to uniquely identify Probe Volumes, and Probe Volume Asset ownership, rather than GetInstanceID() - which was not guarenteed to be persistent. This PR cleans up the related logic, allowing us to guarentee non-colliding bake ids, avoids hanging on to asset references during duplication of probe volumes, and, in the case where you have intentionally referenced a probe volume asset on a probe volume that does not own it, avoids baking that asset, which would previously stomp source data. * Probe Volumes: GlobalUniqueID: Use a custom stack-only + packed representation of the full GlobalObjectId, rather than only using GlobalObjectID.targetObjectId to avoid collisions when different scene probe volumes happen to have the same targetObjectId. This also allows full backwards asset lookup in the future if necessary, which should help debugging + build tools. Co-authored-by: pastasfuture <[email protected]> Probe Volumes: Make all bake requests go through the AdditionalGIBakeRequestsManager. Track the generationIndex in the least significant 8 bits of the lightmapperBakeID, and talk to the lightmapper with this composite ID. This ensures that anytime we call SetAdditionalBakeProbes we generate a completely unique bake request from the perspective of the lightmapper, ensuring that we will always bake this data (because the lightmapper will hash these IDs to determine if it has any work to do). Previously, if we set an ID with new data, or if we cleared an ID then set it with new data, the lightmapper wouldnt track these data changes, and so it would skip baking these actually new bake requests. (#17) Co-authored-by: pastasfuture <[email protected]> Probe Volumes: Fix Debug Colors display mode - shader line got erroneously commented out in a PR a few months back (#18) Co-authored-by: pastasfuture <[email protected]> Probe Volumes: Fix exposure in Probe Volume debug modes (ensure no exposure is applied (#19) Co-authored-by: pastasfuture <[email protected]> Probe Volumes: Maintain user specified absolute world space positive and negative fade while resizing bounds handle. This makes Probe Volumes resize behavior match behavior of Density Volumes and Reflection Probes. (#20) Co-authored-by: pastasfuture <[email protected]> Probe Volumes: Fix PROBE_VOLUMES_SAMPLING_MODE defines in GetReflectionPRobeNormalizationFactor (#23) Co-authored-by: pastasfuture <[email protected]>
1 parent 121c875 commit 068a86f

19 files changed

+842
-248
lines changed

com.unity.render-pipelines.high-definition/Editor/Lighting/ProbeVolume/ProbeVolumeEditor.cs

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,12 @@ protected void OnSceneGUI()
144144
shapeBox.center = Quaternion.Inverse(probeVolume.transform.rotation) * probeVolume.transform.position;
145145
shapeBox.size = probeVolume.parameters.size;
146146

147+
// Maintain fade distance in world space units during resize.
148+
// Otherwise, resize will cause the fade distance to scale based on the new shapeBox size,
149+
// which makes it hard for artists to tune.
150+
Vector3 positiveFadeAbsolutePrevious = Vector3.Scale(probeVolume.parameters.positiveFade, probeVolume.parameters.size);
151+
Vector3 negativeFadeAbsolutePrevious = Vector3.Scale(probeVolume.parameters.negativeFade, probeVolume.parameters.size);
152+
147153
shapeBox.monoHandle = !probeVolume.parameters.advancedFade;
148154
EditorGUI.BeginChangeCheck();
149155
shapeBox.DrawHandle();
@@ -152,9 +158,21 @@ protected void OnSceneGUI()
152158
Undo.RecordObjects(new Object[] { probeVolume, probeVolume.transform }, "Change Probe Volume Bounding Box");
153159

154160
probeVolume.parameters.size = shapeBox.size;
155-
156161
Vector3 delta = probeVolume.transform.rotation * shapeBox.center - probeVolume.transform.position;
157-
probeVolume.transform.position += delta; ;
162+
probeVolume.transform.position += delta;
163+
164+
positiveFadeAbsolutePrevious = Vector3.Min(positiveFadeAbsolutePrevious, probeVolume.parameters.size);
165+
negativeFadeAbsolutePrevious = Vector3.Min(negativeFadeAbsolutePrevious, probeVolume.parameters.size - positiveFadeAbsolutePrevious);
166+
Vector3 sizeInverse = new Vector3(
167+
Mathf.Abs(probeVolume.parameters.size.x) > 1e-5f ? (1.0f / probeVolume.parameters.size.x) : 0.0f,
168+
Mathf.Abs(probeVolume.parameters.size.y) > 1e-5f ? (1.0f / probeVolume.parameters.size.y) : 0.0f,
169+
Mathf.Abs(probeVolume.parameters.size.z) > 1e-5f ? (1.0f / probeVolume.parameters.size.z) : 0.0f
170+
);
171+
probeVolume.parameters.positiveFade = Vector3.Scale(positiveFadeAbsolutePrevious, sizeInverse);
172+
probeVolume.parameters.negativeFade = Vector3.Scale(negativeFadeAbsolutePrevious, sizeInverse);
173+
174+
blendBox.center = CenterBlendLocalPosition(probeVolume);
175+
blendBox.size = BlendSize(probeVolume);
158176
}
159177
}
160178
break;
Lines changed: 222 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,222 @@
1+
Shader "Hidden/Debug/ProbeVolumeSHPreview"
2+
{
3+
Properties
4+
{
5+
_Exposure("_Exposure", Range(-10.0,10.0)) = 0.0
6+
_ProbeVolumeResolution("_ProbeVolumeResolution", Vector) = (0, 0, 0, 0)
7+
_ProbeVolumeProbeDisplayRadiusWS("_ProbeVolumeProbeDisplayRadiusWS", Float) = 1.0
8+
_ProbeVolumeAtlasBiasTexels("_ProbeVolumeAtlasBiasTexels", Vector) = (0, 0, 0, 0)
9+
_ProbeVolumeIsResidentInAtlas("_ProbeVolumeIsResidentInAtlas", Float) = 0.0
10+
_ProbeVolumeHighlightNegativeRinging("_ProbeVolumeHighlightNegativeRinging", Float) = 0.0
11+
_ProbeVolumeDrawValidity("_ProbeVolumeDrawValidity", Float) = 0.0
12+
}
13+
14+
SubShader
15+
{
16+
Tags{ "RenderPipeline" = "HDRenderPipeline" "RenderType" = "Opaque" "Queue" = "Transparent" }
17+
ZWrite On
18+
Cull Front
19+
20+
Pass
21+
{
22+
Name "ForwardUnlit"
23+
Tags{ "LightMode" = "Forward" }
24+
25+
HLSLPROGRAM
26+
27+
#pragma editor_sync_compilation
28+
29+
#pragma vertex vert
30+
#pragma fragment frag
31+
32+
#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Common.hlsl"
33+
#include "Packages/com.unity.render-pipelines.high-definition/Runtime/ShaderLibrary/ShaderVariables.hlsl"
34+
#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Color.hlsl"
35+
#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/EntityLighting.hlsl"
36+
37+
#if SHADEROPTIONS_PROBE_VOLUMES_EVALUATION_MODE != PROBEVOLUMESEVALUATIONMODES_DISABLED
38+
#include "Packages/com.unity.render-pipelines.high-definition/Runtime/Lighting/ProbeVolume/ProbeVolumeShaderVariables.hlsl"
39+
#include "Packages/com.unity.render-pipelines.high-definition/Runtime/Lighting/ProbeVolume/ProbeVolumeAtlas.hlsl"
40+
#endif
41+
42+
struct appdata
43+
{
44+
uint vertexID : SV_VertexID;
45+
};
46+
47+
struct v2f
48+
{
49+
float4 positionCS : SV_POSITION;
50+
float2 uv : TEXCOORD0;
51+
float3 probeIndex3D : TEXCOORD1;
52+
};
53+
54+
float _Exposure;
55+
56+
float3 _ProbeVolumeResolution;
57+
float4x4 _ProbeIndex3DToPositionWSMatrix;
58+
float _ProbeVolumeProbeDisplayRadiusWS;
59+
float3 _ProbeVolumeAtlasBiasTexels;
60+
int _ProbeVolumeIsResidentInAtlas;
61+
int _ProbeVolumeHighlightNegativeRinging;
62+
int _ProbeVolumeDrawValidity;
63+
64+
uint3 ComputeWriteIndexFromReadIndex(uint readIndex, float3 resolution)
65+
{
66+
// _ProbeVolumeAtlasReadBuffer[z * resolutionY * resolutionX + y * resolutionX + x]
67+
// TODO: Could implement as floating point operations, which is likely faster.
68+
// Would need to verify precision.
69+
uint x = readIndex % (uint)resolution.x;
70+
uint y = (readIndex / (uint)resolution.x) % (uint)resolution.y;
71+
uint z = readIndex / ((uint)resolution.y * (uint)resolution.x);
72+
73+
return uint3(x, y, z);
74+
}
75+
76+
v2f vert(appdata v)
77+
{
78+
v2f o;
79+
80+
uint probeIndex1D = v.vertexID / 6u;
81+
uint probeTriangleIndex = (v.vertexID / 3u) & 1u;
82+
uint probeVertexIndex = v.vertexID - probeIndex1D * 6u - probeTriangleIndex * 3u;
83+
84+
float2 vertexPositionOS = (probeTriangleIndex == 1u)
85+
? float2((probeVertexIndex & 1u), saturate(probeVertexIndex))
86+
: float2(saturate(probeVertexIndex), saturate((float)probeVertexIndex - 1.0));
87+
o.uv = vertexPositionOS;
88+
vertexPositionOS = vertexPositionOS * 2.0 - 1.0;
89+
vertexPositionOS *= _ProbeVolumeProbeDisplayRadiusWS;
90+
91+
o.probeIndex3D = ComputeWriteIndexFromReadIndex(probeIndex1D, _ProbeVolumeResolution);
92+
float3 probeOriginWS = mul(_ProbeIndex3DToPositionWSMatrix, float4(o.probeIndex3D, 1.0)).xyz;
93+
float3 probeOriginRWS = GetCameraRelativePositionWS(probeOriginWS);
94+
95+
float3 cameraRightWS = mul(float4(1.0, 0.0, 0.0, 0.0), UNITY_MATRIX_V).xyz;
96+
float3 cameraUpWS = mul(float4(0.0, 1.0, 0.0, 0.0), UNITY_MATRIX_V).xyz;
97+
98+
float3 positionRWS = (cameraRightWS * vertexPositionOS.x + cameraUpWS * vertexPositionOS.y) + probeOriginRWS;
99+
o.positionCS = TransformWorldToHClip(positionRWS);
100+
101+
return o;
102+
}
103+
104+
void ClipProbeSphere(float2 uv)
105+
{
106+
float2 positionProbeCard = uv * 2.0 - 1.0;
107+
clip(dot(positionProbeCard, positionProbeCard) < 1.0 ? 1.0 : -1.0);
108+
}
109+
110+
float3 ComputeProbeNormalWSFromCameraFacingOrtho(float2 uv)
111+
{
112+
// Reconstruct a surface normal vector for our virtual probe sphere using the knowledge that
113+
// our card is camera aligned, and we can project from the 2D disc coordinate to 3D sphere surface coordinate.
114+
// This will not take into account perspective - but as a method to preview probe SH data, this limitation fine visually.
115+
float2 normalOSXY = uv * 2.0 - 1.0;
116+
float normalOSZ = (1.0 - dot(normalOSXY, normalOSXY));
117+
118+
float3 normalWS = mul(float4(normalOSXY, normalOSZ, 0.0), UNITY_MATRIX_V).xyz;
119+
return normalWS;
120+
}
121+
122+
float3 SampleProbeOutgoingRadiance(int3 probeIndexAtlas3D, float3 normalWS)
123+
{
124+
float3 outgoingRadiance = 0.0;
125+
126+
#if SHADEROPTIONS_PROBE_VOLUMES_EVALUATION_MODE != PROBEVOLUMESEVALUATIONMODES_DISABLED
127+
128+
#if SHADEROPTIONS_PROBE_VOLUMES_ENCODING_MODE == PROBEVOLUMESENCODINGMODES_SPHERICAL_HARMONICS_L1
129+
ProbeVolumeSphericalHarmonicsL1 coefficients;
130+
ZERO_INITIALIZE(ProbeVolumeSphericalHarmonicsL1, coefficients);
131+
ProbeVolumeLoadAccumulateSphericalHarmonicsL1(probeIndexAtlas3D, 1.0f, coefficients);
132+
ProbeVolumeSwizzleAndNormalizeSphericalHarmonicsL1(coefficients);
133+
outgoingRadiance = SHEvalLinearL0L1(normalWS, coefficients.data[0], coefficients.data[1], coefficients.data[2]);
134+
135+
#elif SHADEROPTIONS_PROBE_VOLUMES_ENCODING_MODE == PROBEVOLUMESENCODINGMODES_SPHERICAL_HARMONICS_L2
136+
ProbeVolumeSphericalHarmonicsL2 coefficients;
137+
ZERO_INITIALIZE(ProbeVolumeSphericalHarmonicsL2, coefficients);
138+
ProbeVolumeLoadAccumulateSphericalHarmonicsL2(probeIndexAtlas3D, 1.0f, coefficients);
139+
ProbeVolumeSwizzleAndNormalizeSphericalHarmonicsL2(coefficients);
140+
outgoingRadiance = SampleSH9(coefficients.data, normalWS);
141+
#endif
142+
143+
#endif
144+
return outgoingRadiance;
145+
}
146+
147+
float3 SampleProbeValidity(int3 probeIndexAtlas3D)
148+
{
149+
float3 color = 0.0;
150+
151+
#if SHADEROPTIONS_PROBE_VOLUMES_EVALUATION_MODE != PROBEVOLUMESEVALUATIONMODES_DISABLED
152+
float validity = ProbeVolumeLoadValidity(probeIndexAtlas3D);
153+
color = lerp(float3(1, 0, 0), float3(0, 1, 0), validity);
154+
#endif
155+
156+
return color;
157+
}
158+
159+
float ComputeBlink(float time, float frequency)
160+
{
161+
return sin(time * 2.0 * PI * frequency - 0.5 * PI) * 0.5 + 0.5;
162+
}
163+
164+
struct ProbeSampleData
165+
{
166+
float3 color;
167+
bool needsExposure;
168+
};
169+
170+
ProbeSampleData SampleProbeData(int3 probeIndexAtlas3D, float3 normalWS)
171+
{
172+
ProbeSampleData probeSampleData;
173+
ZERO_INITIALIZE(ProbeSampleData, probeSampleData);
174+
175+
if (_ProbeVolumeDrawValidity)
176+
{
177+
probeSampleData.color = SampleProbeValidity(probeIndexAtlas3D);
178+
probeSampleData.needsExposure = false;
179+
}
180+
else
181+
{
182+
float3 outgoingRadiance = SampleProbeOutgoingRadiance(probeIndexAtlas3D, normalWS);
183+
184+
outgoingRadiance = _ProbeVolumeHighlightNegativeRinging
185+
? (any(outgoingRadiance < 0.0) ? float3(ComputeBlink(_Time.y, 1.0) * 0.75 + 0.25, 0.0, 0.0) : outgoingRadiance)
186+
: outgoingRadiance;
187+
188+
outgoingRadiance = max(0.0, outgoingRadiance);
189+
probeSampleData.color = outgoingRadiance;
190+
probeSampleData.needsExposure = true;
191+
}
192+
193+
return probeSampleData;
194+
}
195+
196+
float4 frag(v2f i) : SV_Target
197+
{
198+
ClipProbeSphere(i.uv);
199+
float3 normalWS = ComputeProbeNormalWSFromCameraFacingOrtho(i.uv);
200+
201+
ProbeSampleData probeSampleData;
202+
ZERO_INITIALIZE(ProbeSampleData, probeSampleData);
203+
204+
if (_ProbeVolumeIsResidentInAtlas)
205+
{
206+
// Due to probeIndex3D getting stored as a float and interpolated, we need to round before converting to int.
207+
// Otherwise our texel coordinate will oscillate between probes randomly (based on precision).
208+
int3 probeIndexAtlas3D = (int3)(i.probeIndex3D + 0.5 + _ProbeVolumeAtlasBiasTexels);
209+
210+
probeSampleData = SampleProbeData(probeIndexAtlas3D, normalWS);
211+
}
212+
213+
probeSampleData.color *= probeSampleData.needsExposure
214+
? (exp2(_Exposure) * GetCurrentExposureMultiplier())
215+
: 1.0;
216+
217+
return float4(probeSampleData.color, 1.0);
218+
}
219+
ENDHLSL
220+
}
221+
}
222+
}

com.unity.render-pipelines.high-definition/Editor/Lighting/ProbeVolume/ProbeVolumeSHPreview.shader.meta

Lines changed: 10 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/Editor/Lighting/ProbeVolume/ProbeVolumeUI.Drawer.cs

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -130,18 +130,27 @@ static void Drawer_ToolBar(SerializedProbeVolume serialized, Editor owner)
130130
static void Drawer_PrimarySettings(SerializedProbeVolume serialized, Editor owner)
131131
{
132132
EditorGUILayout.PropertyField(serialized.drawProbes, Styles.s_DrawProbesLabel);
133-
EditorGUILayout.PropertyField(serialized.drawOctahedralDepthRays, Styles.s_DrawOctahedralDepthRays);
134-
if (serialized.drawOctahedralDepthRays.boolValue)
133+
if (serialized.drawProbes.boolValue)
135134
{
136-
EditorGUI.BeginChangeCheck();
137-
EditorGUILayout.DelayedIntField(serialized.drawOctahedralDepthRayIndexX, Styles.s_DrawOctahedralDepthRayIndexX);
138-
EditorGUILayout.DelayedIntField(serialized.drawOctahedralDepthRayIndexY, Styles.s_DrawOctahedralDepthRayIndexY);
139-
EditorGUILayout.DelayedIntField(serialized.drawOctahedralDepthRayIndexZ, Styles.s_DrawOctahedralDepthRayIndexZ);
140-
if (EditorGUI.EndChangeCheck())
135+
EditorGUILayout.PropertyField(serialized.drawValidity, Styles.s_DrawValidityLabel);
136+
if (!serialized.drawValidity.boolValue)
137+
{
138+
EditorGUILayout.PropertyField(serialized.highlightRinging, Styles.s_HighlightRingingLabel);
139+
}
140+
141+
EditorGUILayout.PropertyField(serialized.drawOctahedralDepthRays, Styles.s_DrawOctahedralDepthRays);
142+
if (serialized.drawOctahedralDepthRays.boolValue)
141143
{
142-
serialized.drawOctahedralDepthRayIndexX.intValue = Mathf.Clamp(serialized.drawOctahedralDepthRayIndexX.intValue, 0, serialized.resolutionX.intValue - 1);
143-
serialized.drawOctahedralDepthRayIndexY.intValue = Mathf.Clamp(serialized.drawOctahedralDepthRayIndexY.intValue, 0, serialized.resolutionY.intValue - 1);
144-
serialized.drawOctahedralDepthRayIndexZ.intValue = Mathf.Clamp(serialized.drawOctahedralDepthRayIndexZ.intValue, 0, serialized.resolutionZ.intValue - 1);
144+
EditorGUI.BeginChangeCheck();
145+
EditorGUILayout.DelayedIntField(serialized.drawOctahedralDepthRayIndexX, Styles.s_DrawOctahedralDepthRayIndexX);
146+
EditorGUILayout.DelayedIntField(serialized.drawOctahedralDepthRayIndexY, Styles.s_DrawOctahedralDepthRayIndexY);
147+
EditorGUILayout.DelayedIntField(serialized.drawOctahedralDepthRayIndexZ, Styles.s_DrawOctahedralDepthRayIndexZ);
148+
if (EditorGUI.EndChangeCheck())
149+
{
150+
serialized.drawOctahedralDepthRayIndexX.intValue = Mathf.Clamp(serialized.drawOctahedralDepthRayIndexX.intValue, 0, serialized.resolutionX.intValue - 1);
151+
serialized.drawOctahedralDepthRayIndexY.intValue = Mathf.Clamp(serialized.drawOctahedralDepthRayIndexY.intValue, 0, serialized.resolutionY.intValue - 1);
152+
serialized.drawOctahedralDepthRayIndexZ.intValue = Mathf.Clamp(serialized.drawOctahedralDepthRayIndexZ.intValue, 0, serialized.resolutionZ.intValue - 1);
153+
}
145154
}
146155
}
147156

com.unity.render-pipelines.high-definition/Editor/Lighting/ProbeVolume/ProbeVolumeUI.Skin.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ internal static class Styles
2525
internal static readonly GUIContent s_Size = new GUIContent("Size", "Modify the size of this Probe Volume. This is independent of the Transform's Scale.");
2626
internal static readonly GUIContent s_DebugColorLabel = new GUIContent("Debug Color", "This color is used to visualize per-pixel probe volume weights in the render pipeline debugger.");
2727
internal static readonly GUIContent s_DrawProbesLabel = new GUIContent("Draw Probes", "Enable or disable drawing probes.");
28+
internal static readonly GUIContent s_DrawValidityLabel = new GUIContent("Draw Validity", "Visualize per-probe validity. Green is full validity, no backfaces visible. Red is no validity, max backfaces visible.");
29+
internal static readonly GUIContent s_HighlightRingingLabel = new GUIContent("Highlight Ringing", "Negative values on probes due to ringing will be highlighted and blinked in red.");
2830
internal static readonly GUIContent s_DrawNeighborsLabel = new GUIContent("Draw Neighbors", "Enable or disable drawing probe neighborhood to debug dynamic gi.");
2931
internal static readonly GUIContent s_NeighborsQuadScaleLabel = new GUIContent("Draw Neighbors Scale", "The size of the debug quads when viewing draw neighbors mode");
3032
internal static readonly GUIContent s_DrawOctahedralDepthRays = new GUIContent("Draw Octahedral Depth Rays", "Enable or disable drawing rays to visualize to the octahedral depth data.");

com.unity.render-pipelines.high-definition/Editor/Lighting/ProbeVolume/SerializedProbeVolume.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ class SerializedProbeVolume
77
internal SerializedProperty debugColor;
88
internal SerializedProperty supportDynamicGI;
99
internal SerializedProperty drawProbes;
10+
internal SerializedProperty drawValidity;
11+
internal SerializedProperty highlightRinging;
1012
internal SerializedProperty drawNeighbors;
1113
internal SerializedProperty neighborsQuadScale;
1214
internal SerializedProperty drawOctahedralDepthRays;
@@ -56,6 +58,8 @@ internal SerializedProbeVolume(SerializedObject serializedObject)
5658
debugColor = probeVolumeParams.FindPropertyRelative("debugColor");
5759
supportDynamicGI = probeVolumeParams.FindPropertyRelative("supportDynamicGI");
5860
drawProbes = probeVolumeParams.FindPropertyRelative("drawProbes");
61+
drawValidity = probeVolumeParams.FindPropertyRelative("drawValidity");
62+
highlightRinging = probeVolumeParams.FindPropertyRelative("highlightRinging");
5963
drawNeighbors = probeVolumeParams.FindPropertyRelative("drawNeighbors");
6064
neighborsQuadScale = probeVolumeParams.FindPropertyRelative("neighborsQuadScale");
6165

0 commit comments

Comments
 (0)