A game about forced loneliness, made by TACStudios
1#ifndef PROBEVOLUMEDEBUG_BASE_HLSL
2#define PROBEVOLUMEDEBUG_BASE_HLSL
3
4// TEMPORARY WORKAROUND
5// Unfortunately we don't have a cross pipeline way to pass per frame constant.
6// One of the reason is that we don't want to force a certain Constant Buffer layout on users (read: SRP writer) for shared data.
7// This means that usually SRP Core functions (see SpaceTransforms.hlsl for example) use common names that are NOT declared in the Core package but rather in each pipelines.
8// The consequence is that when writing core shaders that need those variables, we currently have to copy their declaration and set them manually from core C# code to the relevant shaders.
9
10// Here is current the subset of variables and functions required by APV debug.
11// Copying them here means that these shaders don't support either XR or Camera Relative rendering (at least until those concept become fully cross pipeline)
12CBUFFER_START(ShaderVariablesProbeVolumeDebug)
13float4x4 unity_MatrixVP; // Sent by builtin
14float4x4 unity_MatrixInvV; // Sent by builtin
15float4x4 unity_ObjectToWorld; // Sent by builtin
16float4x4 unity_MatrixV; // Sent by builtin
17float4 _ScreenSize;
18float3 _WorldSpaceCameraPos; // Sent by builtin
19CBUFFER_END
20
21TEXTURE2D(_ExposureTexture);
22
23#define UNITY_MATRIX_VP unity_MatrixVP
24#define UNITY_MATRIX_V unity_MatrixV
25#define UNITY_MATRIX_I_V unity_MatrixInvV
26#define UNITY_MATRIX_M unity_ObjectToWorld
27
28float3 GetCurrentViewPosition()
29{
30 return UNITY_MATRIX_I_V._14_24_34;
31}
32
33float GetCurrentExposureMultiplier()
34{
35 return LOAD_TEXTURE2D(_ExposureTexture, int2(0, 0)).x;
36}
37
38#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/UnityInstancing.hlsl"
39#include "Packages/com.unity.render-pipelines.core/Runtime/Lighting/ProbeVolume/DecodeSH.hlsl"
40#include "Packages/com.unity.render-pipelines.core/Runtime/Lighting/ProbeVolume/ProbeVolume.hlsl"
41#include "Packages/com.unity.render-pipelines.core/Runtime/Lighting/ProbeVolume/ProbeReferenceVolume.Debug.cs.hlsl"
42
43
44uniform int _ShadingMode;
45uniform float _ExposureCompensation;
46uniform float _ProbeSize;
47uniform float4 _Color;
48uniform int _SubdivLevel;
49uniform float _CullDistance;
50uniform int _MaxAllowedSubdiv;
51uniform int _MinAllowedSubdiv;
52uniform float _ValidityThreshold;
53uniform uint _RenderingLayerMask;
54uniform float _OffsetSize;
55
56// Sampling Position Debug
57uniform bool _DebugProbeVolumeSampling = false;
58StructuredBuffer<float4> _positionNormalBuffer;
59uniform float4 _DebugArrowColor; // arrow color for position and normal debug
60uniform float4 _DebugLocator01Color; // locator color for final sampling position debug
61uniform float4 _DebugLocator02Color; // locator color for normal and view bias sampling position debug
62uniform float4 _DebugEmptyProbeData; // probe color for missing data
63uniform bool _ForceDebugNormalViewBias; // additional locator to debug Normal Bias and View Bias without AntiLeak Reduction Mode
64uniform bool _DebugSamplingNoise = false;
65uniform sampler2D _NumbersTex;
66
67UNITY_INSTANCING_BUFFER_START(Props)
68 UNITY_DEFINE_INSTANCED_PROP(float, _Validity)
69 UNITY_DEFINE_INSTANCED_PROP(float, _RenderingLayer)
70 UNITY_DEFINE_INSTANCED_PROP(float, _DilationThreshold)
71 UNITY_DEFINE_INSTANCED_PROP(float, _TouchupedByVolume)
72 UNITY_DEFINE_INSTANCED_PROP(float4, _IndexInAtlas)
73 UNITY_DEFINE_INSTANCED_PROP(float4, _Offset)
74 UNITY_DEFINE_INSTANCED_PROP(float, _RelativeSize)
75UNITY_INSTANCING_BUFFER_END(Props)
76
77struct appdata
78{
79 float4 vertex : POSITION;
80 float3 normal : NORMAL;
81 float4 color : COLOR0;
82 float2 texCoord : TEXCOORD0;
83 UNITY_VERTEX_INPUT_INSTANCE_ID
84};
85
86struct v2f
87{
88 float4 vertex : SV_POSITION;
89 float3 normal : TEXCOORD1;
90 float4 color : COLOR0;
91 float2 texCoord : TEXCOORD0;
92 float2 samplingFactor_ValidityWeight : TEXCOORD2; // stores sampling factor (for Probe Sampling Debug view) and validity weight
93 float2 centerCoordSS : TEXCOORD3;
94 UNITY_VERTEX_INPUT_INSTANCE_ID
95};
96
97void DoCull(inout v2f o)
98{
99 ZERO_INITIALIZE(v2f, o);
100 o.samplingFactor_ValidityWeight = float2(0.0f, 1.0f);
101}
102
103// snappedProbePosition_WS : worldspace position of main probe (a corner of the 8 probes cube)
104// samplingPosition_WS : worldspace sampling position after applying 'NormalBias' and 'ViewBias' and 'ValidityAndNormalBased Leak Reduction'
105// normalizedOffset : normalized offset between sampling position and snappedProbePosition
106void FindSamplingData(float3 posWS, float3 normalWS, uint renderingLayer, out float3 snappedProbePosition_WS, out float3 samplingPositionNoAntiLeak_WS, out float probeDistance, out float3 normalizedOffset, out float validityWeights[8])
107{
108 float3 cameraPosition_WS = _WorldSpaceCameraPos;
109 float3 viewDir_WS = normalize(cameraPosition_WS - posWS);
110
111 if (_DebugSamplingNoise)
112 {
113 float2 posNDC = ComputeNormalizedDeviceCoordinates(posWS, UNITY_MATRIX_VP);
114 float2 posSS = floor(posNDC.xy * _ScreenSize.xy);
115 posWS = AddNoiseToSamplingPosition(posWS, posSS, viewDir_WS);
116 }
117
118 posWS -= _APVWorldOffset;
119
120 // uvw
121 APVResources apvRes = FillAPVResources();
122 float3 uvw;
123 uint subdiv;
124 float3 biasedPosWS;
125 bool valid = TryToGetPoolUVWAndSubdiv(apvRes, posWS, normalWS, viewDir_WS, uvw, subdiv, biasedPosWS);
126
127 // Validity mask
128 float3 texCoord = uvw * _APVPoolDim - .5f;
129 float3 texFrac = frac(texCoord);
130 uint validityMask = LoadValidityMask(apvRes, renderingLayer, texCoord);
131 for (uint i = 0; i < 8; i++)
132 {
133 int3 probeCoord = GetSampleOffset(i);
134 float validityWeight = ((probeCoord.x == 1) ? texFrac.x : 1.0f - texFrac.x) *
135 ((probeCoord.y == 1) ? texFrac.y : 1.0f - texFrac.y) *
136 ((probeCoord.z == 1) ? texFrac.z : 1.0f - texFrac.z);
137 validityWeights[i] = validityWeight * GetValidityWeight(i, validityMask);
138 }
139
140 // Sample position
141 normalizedOffset = texFrac;
142 if (_APVLeakReductionMode == APVLEAKREDUCTIONMODE_PERFORMANCE)
143 {
144 float3 warped = uvw;
145 WarpUVWLeakReduction(apvRes, renderingLayer, warped);
146 normalizedOffset += (warped - uvw) * _APVPoolDim;
147 }
148
149 // stuff
150 probeDistance = ProbeDistance(subdiv);
151 snappedProbePosition_WS = GetSnappedProbePosition(biasedPosWS, subdiv) + _APVWorldOffset;
152 samplingPositionNoAntiLeak_WS = biasedPosWS + _APVWorldOffset;
153}
154
155// Return probe sampling weight
156float ComputeSamplingFactor(float3 probePosition_WS, float3 snappedProbePosition_WS, float3 normalizedOffset, float probeDistance)
157{
158 float samplingFactor = -1.0f;
159
160 if (distance(snappedProbePosition_WS, probePosition_WS) < 0.0001f)
161 {
162 samplingFactor = (1.0f-normalizedOffset.x) * (1.0f-normalizedOffset.y) * (1.0f-normalizedOffset.z);
163 }
164
165 else if (distance(snappedProbePosition_WS, probePosition_WS + float3(-1.0f, 0.0f, 0.0f)*probeDistance) < 0.0001f)
166 {
167 samplingFactor = (normalizedOffset.x) * (1.0f-normalizedOffset.y) * (1.0f-normalizedOffset.z);
168 }
169
170 else if (distance(snappedProbePosition_WS, probePosition_WS + float3(-1.0f, -1.0f, 0.0f)*probeDistance) < 0.0001f)
171 {
172 samplingFactor = (normalizedOffset.x) * (normalizedOffset.y) * (1.0f-normalizedOffset.z);
173 }
174
175 else if (distance(snappedProbePosition_WS, probePosition_WS + float3(0.0f, -1.0f, 0.0f)*probeDistance) < 0.0001f)
176 {
177 samplingFactor = (1.0f-normalizedOffset.x) * (normalizedOffset.y) * (1.0f-normalizedOffset.z);
178 }
179
180 else if (distance(snappedProbePosition_WS, probePosition_WS + float3(-1.0f, 0.0f, -1.0f)*probeDistance) < 0.0001f)
181 {
182 samplingFactor = (normalizedOffset.x) * (1.0f-normalizedOffset.y) * (normalizedOffset.z);
183 }
184
185 else if (distance(snappedProbePosition_WS, probePosition_WS + float3(0.0f, 0.0f, -1.0f)*probeDistance) < 0.0001f)
186 {
187 samplingFactor = (1.0f-normalizedOffset.x) * (1.0f-normalizedOffset.y) * (normalizedOffset.z);
188 }
189
190 else if (distance(snappedProbePosition_WS, probePosition_WS + float3(-1.0f, -1.0f, -1.0f)*probeDistance) < 0.0001f)
191 {
192 samplingFactor = (normalizedOffset.x) * (normalizedOffset.y) * (normalizedOffset.z);
193 }
194
195 else if (distance(snappedProbePosition_WS, probePosition_WS + float3(0.0f, -1.0f, -1.0f)*probeDistance) < 0.0001f)
196 {
197 samplingFactor = (1.0f-normalizedOffset.x) * (normalizedOffset.y) * (normalizedOffset.z);
198 }
199
200 return samplingFactor;
201}
202
203// Sample a texture with numbers at the right place depending on a number input
204half4 SampleCharacter(int input, float2 texCoord) // Samples _NumbersTex to get given character
205{
206 // texture is divided in 16 parts and contains following characters : '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '.' (+ empty space)
207 float2 uv = float2((texCoord.x + input) / 16.0f, texCoord.y);
208 half4 color = tex2D(_NumbersTex, uv);
209 return color;
210}
211
212// Writes a floating number with two decimals using a texture with numbers.
213// Use to debug probe sampling weights with text
214half4 WriteFractNumber(float input, float2 texCoord)
215{
216 // using 4 characters
217 float i = 4.0f;
218
219 // 0.00X
220 int n3_value = floor(frac(input * 100.0f) * 10.0f);
221 int n2_add = 0;
222 if (n3_value >= 5) {n2_add = 1;}
223
224 // 0.0X
225 int n2_value = floor(frac(input * 10.0f) * 10.0f);
226 n2_value += n2_add;
227 int n1_add = 0;
228 if (n2_value >= 10) {n2_value -= 10; n1_add = 1;}
229
230 // 0.X
231 int n1_value = floor(frac(input) * 10.0f);
232 n1_value += n1_add;
233 int n0_add = 0;
234 if (n1_value >= 10) {n1_value -= 10; n0_add = 1;}
235
236 // X
237 int n0_value = floor(input);
238 n0_value += n0_add;
239
240 float2 n0_uv = float2(clamp(texCoord.x*i - 0.0f, 0.0f, 1.0f), texCoord.y);
241 float2 dot_uv = float2(clamp(texCoord.x*i - 1.0f, 0.0f, 1.0f), texCoord.y);
242 float2 n1_uv = float2(clamp(texCoord.x*i - 2.0f, 0.0f, 1.0f), texCoord.y);
243 float2 n2_uv = float2(clamp(texCoord.x*i - 3.0f, 0.0f, 1.0f), texCoord.y);
244
245 half4 outVal = 0;
246 if (texCoord.x <= 0.25)
247 outVal = SampleCharacter(n0_value, n0_uv);
248 else if (texCoord.x <= 0.50)
249 outVal = SampleCharacter(10, dot_uv);
250 else if (texCoord.x <= 0.75)
251 outVal = SampleCharacter(n1_value, n1_uv);
252 else
253 outVal = SampleCharacter(n2_value, n2_uv);
254
255 return outVal;
256}
257
258
259
260// Finer culling, degenerate the vertices of the debug element if it lies over the max distance.
261// Coarser culling has already happened on CPU.
262bool ShouldCull(inout v2f o)
263{
264 float4 position = float4(UNITY_MATRIX_M._m03_m13_m23, 1);
265 int brickSize = UNITY_ACCESS_INSTANCED_PROP(Props, _IndexInAtlas).w;
266
267 bool shouldCull = false;
268 if (distance(position.xyz + _APVWorldOffset, GetCurrentViewPosition()) > _CullDistance || brickSize > _MaxAllowedSubdiv || brickSize < _MinAllowedSubdiv)
269 {
270 DoCull(o);
271 shouldCull = true;
272 }
273
274 return shouldCull;
275}
276
277float3 EvalL1(float3 L0, float3 L1_R, float3 L1_G, float3 L1_B, float3 N)
278{
279 float3 outLighting = 0;
280 L1_R = DecodeSH(L0.r, L1_R);
281 L1_G = DecodeSH(L0.g, L1_G);
282 L1_B = DecodeSH(L0.b, L1_B);
283 outLighting += SHEvalLinearL1(N, L1_R, L1_G, L1_B);
284
285 return outLighting;
286}
287
288float3 EvalL2(inout float3 L0, float4 L2_R, float4 L2_G, float4 L2_B, float4 L2_C, float3 N)
289{
290 DecodeSH_L2(L0, L2_R, L2_G, L2_B, L2_C);
291
292 return SHEvalLinearL2(N, L2_R, L2_G, L2_B, L2_C);
293}
294
295float3 CalculateDiffuseLighting(v2f i)
296{
297 APVResources apvRes = FillAPVResources();
298 int3 texLoc = UNITY_ACCESS_INSTANCED_PROP(Props, _IndexInAtlas).xyz;
299 float3 normal = normalize(i.normal);
300
301 float3 skyShadingDirection = normal;
302 if (_ShadingMode == DEBUGPROBESHADINGMODE_PROBE_OCCLUSION)
303 {
304 float4 shadowmask = apvRes.ProbeOcclusion[texLoc];
305 return shadowmask.rgb * 0.5 + (shadowmask.aaa * 0.5);
306 }
307 else if (_ShadingMode == DEBUGPROBESHADINGMODE_SKY_DIRECTION)
308 {
309 if (_APVSkyDirectionWeight > 0)
310 {
311 float value = 1.0f / GetCurrentExposureMultiplier();
312
313 uint index = apvRes.SkyShadingDirectionIndices[texLoc].r * 255;
314 if (index != 255)
315 skyShadingDirection = apvRes.SkyPrecomputedDirections[index].rgb;
316 else
317 return float3(value, 0.0f, 0.0f);
318
319 if (dot(normal, skyShadingDirection) > 0.95)
320 return float3(0.0f, value, 0.0f);
321 return float3(0.0f, 0.0f, 0.0f);
322 }
323 else
324 {
325 return _DebugEmptyProbeData.xyz / GetCurrentExposureMultiplier();
326 }
327 }
328 else
329 {
330 float3 skyOcclusion = _DebugEmptyProbeData.xyz;
331 if (_APVSkyOcclusionWeight > 0)
332 {
333 // L0 L1
334 float4 temp = float4(kSHBasis0, kSHBasis1 * normal.x, kSHBasis1 * normal.y, kSHBasis1 * normal.z);
335 skyOcclusion = dot(temp, apvRes.SkyOcclusionL0L1[texLoc].rgba);
336 }
337
338 if (_ShadingMode == DEBUGPROBESHADINGMODE_SKY_OCCLUSION_SH)
339 {
340 return skyOcclusion / GetCurrentExposureMultiplier();
341 }
342 else
343 {
344 float4 L0_L1Rx = apvRes.L0_L1Rx[texLoc].rgba;
345 float3 L0 = L0_L1Rx.xyz;
346
347 if (_ShadingMode == DEBUGPROBESHADINGMODE_SHL0)
348 return L0;
349
350 float L1Rx = L0_L1Rx.w;
351 float4 L1G_L1Ry = apvRes.L1G_L1Ry[texLoc].rgba;
352 float4 L1B_L1Rz = apvRes.L1B_L1Rz[texLoc].rgba;
353
354 float3 bakeDiffuseLighting = EvalL1(L0, float3(L1Rx, L1G_L1Ry.w, L1B_L1Rz.w), L1G_L1Ry.xyz, L1B_L1Rz.xyz, normal);
355 bakeDiffuseLighting += L0;
356
357 if (_ShadingMode == DEBUGPROBESHADINGMODE_SHL0L1)
358 return bakeDiffuseLighting;
359
360 #ifdef PROBE_VOLUMES_L2
361 float4 L2_R = apvRes.L2_0[texLoc].rgba;
362 float4 L2_G = apvRes.L2_1[texLoc].rgba;
363 float4 L2_B = apvRes.L2_2[texLoc].rgba;
364 float4 L2_C = apvRes.L2_3[texLoc].rgba;
365
366 bakeDiffuseLighting += EvalL2(L0, L2_R, L2_G, L2_B, L2_C, normal);
367 #endif
368 if (_APVSkyOcclusionWeight > 0)
369 bakeDiffuseLighting += skyOcclusion * EvaluateAmbientProbe(skyShadingDirection);
370
371 return bakeDiffuseLighting;
372 }
373 }
374}
375
376CBUFFER_START(TouchupVolumeBounds)
377 float4 _TouchupVolumeBounds[16 * 3]; // A BBox is 3 float4
378 uint _AdjustmentVolumeCount;
379CBUFFER_END
380
381bool IsInSelection(float3 position)
382{
383 for (uint i = 0; i < _AdjustmentVolumeCount; i++)
384 {
385 float3 center = _TouchupVolumeBounds[i * 3 + 0].xyz;
386 bool isSphere = _TouchupVolumeBounds[i * 3 + 0].w >= FLT_MAX;
387 if (isSphere)
388 {
389 float radius = _TouchupVolumeBounds[i * 3 + 1].x;
390 if (dot(position - center, position - center) < radius * radius)
391 return true;
392 }
393 else
394 {
395 float3 X = _TouchupVolumeBounds[i * 3 + 1].xyz;
396 float3 Y = _TouchupVolumeBounds[i * 3 + 2].xyz;
397 float3 Z = float3(_TouchupVolumeBounds[i * 3 + 0].w,
398 _TouchupVolumeBounds[i * 3 + 1].w,
399 _TouchupVolumeBounds[i * 3 + 2].w);
400
401 if (abs(dot(position - center, normalize(X))) < length(X) &&
402 abs(dot(position - center, normalize(Y))) < length(Y) &&
403 abs(dot(position - center, normalize(Z))) < length(Z))
404 return true;
405 }
406 }
407
408 return false;
409}
410
411#endif //PROBEVOLUMEDEBUG_BASE_HLSL