Skip to content

Commit 7c55d76

Browse files
committed
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]>
1 parent 1099395 commit 7c55d76

File tree

9 files changed

+382
-129
lines changed

9 files changed

+382
-129
lines changed
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+
_ProbeVolumeAtlasBias("_ProbeVolumeAtlasBias", 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 _ProbeVolumeAtlasBias;
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 + _ProbeVolumeAtlasBias);
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

com.unity.render-pipelines.high-definition/Runtime/Lighting/ProbeVolume/DynamicGI/ProbeVolumeDynamicGIEditor.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ internal void ConstructNeighborData(Vector3[] probePositions, ref ProbeVolumeAss
104104
ClearContent();
105105
}
106106

107-
internal void DebugDrawNeighborhood(ProbeVolumeHandle probeVolume)
107+
internal void DebugDrawNeighborhood(ProbeVolumeHandle probeVolume, Camera camera)
108108
{
109109
if (probeVolume.HasNeighbors()
110110
&& probeVolume.GetProbeVolumeEngineDataIndex() >= 0)
@@ -140,7 +140,7 @@ internal void DebugDrawNeighborhood(ProbeVolumeHandle probeVolume)
140140
bounds.center = Vector3.zero;
141141
bounds.Expand(10000000.0f);
142142

143-
Graphics.DrawProcedural(material, bounds, MeshTopology.Triangles, numVerticesPerAxis, probeVolume.propagationBuffers.neighborHits.count, null, null, ShadowCastingMode.Off, false);
143+
Graphics.DrawProcedural(material, bounds, MeshTopology.Triangles, numVerticesPerAxis, probeVolume.propagationBuffers.neighborHits.count, camera, null, ShadowCastingMode.Off, false);
144144
}
145145
}
146146
}

0 commit comments

Comments
 (0)