A game about forced loneliness, made by TACStudios
1#ifndef __PROBEVOLUME_HLSL__ 2#define __PROBEVOLUME_HLSL__ 3 4#if defined(SHADER_API_MOBILE) || defined(SHADER_API_SWITCH) 5//#define USE_APV_TEXTURE_HALF 6#endif // SHADER_API_MOBILE || SHADER_API_SWITCH 7 8#include "Packages/com.unity.render-pipelines.core/Runtime/Lighting/ProbeVolume/ShaderVariablesProbeVolumes.cs.hlsl" 9#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/SphericalHarmonics.hlsl" 10#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Color.hlsl" 11 12// Unpack variables 13#define _APVWorldOffset _Offset_LayerCount.xyz 14#define _APVIndirectionEntryDim _MinLoadedCellInEntries_IndirectionEntryDim.w 15#define _APVRcpIndirectionEntryDim _MaxLoadedCellInEntries_RcpIndirectionEntryDim.w 16#define _APVMinBrickSize _PoolDim_MinBrickSize.w 17#define _APVPoolDim _PoolDim_MinBrickSize.xyz 18#define _APVRcpPoolDim _RcpPoolDim_XY.xyz 19#define _APVRcpPoolDimXY _RcpPoolDim_XY.w 20#define _APVMinEntryPosition _MinEntryPos_Noise.xyz 21#define _APVSamplingNoise _MinEntryPos_Noise.w 22#define _APVEntryCount _EntryCount_X_XY_LeakReduction.xy 23#define _APVLeakReductionMode _EntryCount_X_XY_LeakReduction.z 24#define _APVNormalBias _Biases_NormalizationClamp.x 25#define _APVViewBias _Biases_NormalizationClamp.y 26#define _APVMinLoadedCellInEntries _MinLoadedCellInEntries_IndirectionEntryDim.xyz 27#define _APVMaxLoadedCellInEntries _MaxLoadedCellInEntries_RcpIndirectionEntryDim.xyz 28#define _APVLayerCount (uint)(_Offset_LayerCount.w) 29#define _APVMinReflProbeNormalizationFactor _Biases_NormalizationClamp.z 30#define _APVMaxReflProbeNormalizationFactor _Biases_NormalizationClamp.w 31#define _APVFrameIndex _FrameIndex_Weights.x 32#define _APVWeight _FrameIndex_Weights.y 33#define _APVSkyOcclusionWeight _FrameIndex_Weights.z 34#define _APVSkyDirectionWeight _FrameIndex_Weights.w 35 36#ifndef DECODE_SH 37#include "Packages/com.unity.render-pipelines.core/Runtime/Lighting/ProbeVolume/DecodeSH.hlsl" 38#endif 39 40#ifndef __AMBIENTPROBE_HLSL__ 41float3 EvaluateAmbientProbe(float3 normalWS) 42{ 43 return float3(0, 0, 0); 44} 45#endif 46 47#ifndef UNITY_SHADER_VARIABLES_INCLUDED 48SAMPLER(s_linear_clamp_sampler); 49SAMPLER(s_point_clamp_sampler); 50#endif 51 52#ifdef USE_APV_TEXTURE_HALF 53#define TEXTURE3D_APV TEXTURE3D_HALF 54#else 55#define TEXTURE3D_APV TEXTURE3D_FLOAT 56#endif 57 58struct APVResources 59{ 60 StructuredBuffer<int> index; 61 StructuredBuffer<float3> SkyPrecomputedDirections; 62 63 TEXTURE3D_APV(L0_L1Rx); 64 TEXTURE3D_APV(L1G_L1Ry); 65 TEXTURE3D_APV(L1B_L1Rz); 66 TEXTURE3D_APV(L2_0); 67 TEXTURE3D_APV(L2_1); 68 TEXTURE3D_APV(L2_2); 69 TEXTURE3D_APV(L2_3); 70 TEXTURE3D_FLOAT(Validity); // Validity stores indices and requires full float precision to be decoded properly. 71 72 TEXTURE3D_APV(ProbeOcclusion); 73 74 TEXTURE3D_APV(SkyOcclusionL0L1); 75 TEXTURE3D_FLOAT(SkyShadingDirectionIndices); 76}; 77 78struct APVResourcesRW 79{ 80 RWTexture3D<float4> L0_L1Rx; 81 RWTexture3D<float4> L1G_L1Ry; 82 RWTexture3D<float4> L1B_L1Rz; 83 RWTexture3D<float4> L2_0; 84 RWTexture3D<float4> L2_1; 85 RWTexture3D<float4> L2_2; 86 RWTexture3D<float4> L2_3; 87 RWTexture3D<float4> ProbeOcclusion; 88}; 89 90#ifndef USE_APV_PROBE_OCCLUSION 91// If we are rendering a probe lit renderer, and we have APV enabled, and we are using subtractive or shadowmask mode, we sample occlusion from APV. 92#if !defined(LIGHTMAP_ON) && (defined(PROBE_VOLUMES_L1) || defined(PROBE_VOLUMES_L2)) && (defined(LIGHTMAP_SHADOW_MIXING) || defined(SHADOWS_SHADOWMASK)) 93#define USE_APV_PROBE_OCCLUSION 1 94#endif 95#endif 96 97#define LOAD_APV_RES_L1(res, target) \ 98 res.L0_L1Rx = CALL_MERGE_NAME(target, _L0_L1Rx); \ 99 res.L1G_L1Ry = CALL_MERGE_NAME(target, _L1G_L1Ry); \ 100 res.L1B_L1Rz = CALL_MERGE_NAME(target, _L1B_L1Rz); 101#define LOAD_APV_RES_L2(res, target) \ 102 res.L2_0 = CALL_MERGE_NAME(target, _L2_0); \ 103 res.L2_1 = CALL_MERGE_NAME(target, _L2_1); \ 104 res.L2_2 = CALL_MERGE_NAME(target, _L2_2); \ 105 res.L2_3 = CALL_MERGE_NAME(target, _L2_3); 106#define LOAD_APV_RES_OCCLUSION(res, target) \ 107 res.ProbeOcclusion = CALL_MERGE_NAME(target, _ProbeOcclusion); 108 109#ifndef PROBE_VOLUMES_L2 110 #ifndef USE_APV_PROBE_OCCLUSION 111 # define LOAD_APV_RES(res, target) LOAD_APV_RES_L1(res, target) 112 #else 113 # define LOAD_APV_RES(res, target) LOAD_APV_RES_L1(res, target) \ 114 LOAD_APV_RES_OCCLUSION(res, target) 115 #endif 116#else 117 #ifndef USE_APV_PROBE_OCCLUSION 118 # define LOAD_APV_RES(res, target) \ 119 LOAD_APV_RES_L1(res, target) \ 120 LOAD_APV_RES_L2(res, target) 121 #else 122 # define LOAD_APV_RES(res, target) \ 123 LOAD_APV_RES_L1(res, target) \ 124 LOAD_APV_RES_L2(res, target) \ 125 LOAD_APV_RES_OCCLUSION(res, target) 126 #endif 127#endif 128 129struct APVSample 130{ 131 half3 L0; 132 half3 L1_R; 133 half3 L1_G; 134 half3 L1_B; 135#ifdef PROBE_VOLUMES_L2 136 half4 L2_R; 137 half4 L2_G; 138 half4 L2_B; 139 half3 L2_C; 140#endif // PROBE_VOLUMES_L2 141 142 float4 skyOcclusionL0L1; 143 float3 skyShadingDirection; 144 145#ifdef USE_APV_PROBE_OCCLUSION 146 float4 probeOcclusion; 147#endif 148 149#define APV_SAMPLE_STATUS_INVALID -1 150#define APV_SAMPLE_STATUS_ENCODED 0 151#define APV_SAMPLE_STATUS_DECODED 1 152 153 int status; 154 155 // Note: at the moment this is called at the moment the struct is built, but it is kept as a separate step 156 // as ideally should be called as far as possible from sample to allow for latency hiding. 157 void Decode() 158 { 159 if (status == APV_SAMPLE_STATUS_ENCODED) 160 { 161 L1_R = DecodeSH(L0.r, L1_R); 162 L1_G = DecodeSH(L0.g, L1_G); 163 L1_B = DecodeSH(L0.b, L1_B); 164#ifdef PROBE_VOLUMES_L2 165 DecodeSH_L2(L0, L2_R, L2_G, L2_B, L2_C); 166#endif // PROBE_VOLUMES_L2 167 168 status = APV_SAMPLE_STATUS_DECODED; 169 } 170 } 171 172 void Encode() 173 { 174 if (status == APV_SAMPLE_STATUS_DECODED) 175 { 176 L1_R = EncodeSH(L0.r, L1_R); 177 L1_G = EncodeSH(L0.g, L1_G); 178 L1_B = EncodeSH(L0.b, L1_B); 179#ifdef PROBE_VOLUMES_L2 180 EncodeSH_L2(L0, L2_R, L2_G, L2_B, L2_C); 181#endif // PROBE_VOLUMES_L2 182 183 status = APV_SAMPLE_STATUS_ENCODED; 184 } 185 } 186}; 187 188// Resources required for APV 189StructuredBuffer<int> _APVResIndex; 190StructuredBuffer<uint3> _APVResCellIndices; 191StructuredBuffer<float3> _SkyPrecomputedDirections; 192StructuredBuffer<uint> _AntiLeakData; 193 194TEXTURE3D_APV(_APVResL0_L1Rx); 195 196TEXTURE3D_APV(_APVResL1G_L1Ry); 197TEXTURE3D_APV(_APVResL1B_L1Rz); 198TEXTURE3D_APV(_APVResL2_0); 199TEXTURE3D_APV(_APVResL2_1); 200TEXTURE3D_APV(_APVResL2_2); 201TEXTURE3D_APV(_APVResL2_3); 202 203TEXTURE3D_APV(_APVProbeOcclusion); 204 205TEXTURE3D_APV(_APVResValidity); 206 207TEXTURE3D_APV(_SkyOcclusionTexL0L1); 208TEXTURE3D(_SkyShadingDirectionIndicesTex); 209 210 211// ------------------------------------------------------------- 212// Various weighting functions for occlusion or helper functions. 213// ------------------------------------------------------------- 214float3 AddNoiseToSamplingPosition(float3 posWS, float2 positionSS, float3 direction) 215{ 216#ifdef UNITY_SPACE_TRANSFORMS_INCLUDED 217 float3 right = mul((float3x3)GetViewToWorldMatrix(), float3(1.0, 0.0, 0.0)); 218 float3 top = mul((float3x3)GetViewToWorldMatrix(), float3(0.0, 1.0, 0.0)); 219 float noise01 = InterleavedGradientNoise(positionSS, _APVFrameIndex); 220 float noise02 = frac(noise01 * 100.0); 221 float noise03 = frac(noise01 * 1000.0); 222 direction += top * (noise02 - 0.5) + right * (noise03 - 0.5); 223 return _APVSamplingNoise > 0 ? posWS + noise01 * _APVSamplingNoise * direction : posWS; 224#else 225 return posWS; 226#endif 227} 228 229uint3 GetSampleOffset(uint i) 230{ 231 return uint3(i, i >> 1, i >> 2) & 1; 232} 233 234// The validity mask is sampled once and contains a binary info on whether a probe neighbour (relevant for trilinear) is to be used 235// or not. The entry in the mask uses the same mapping that GetSampleOffset above uses. 236half GetValidityWeight(uint offset, uint validityMask) 237{ 238 uint mask = 1U << offset; 239 return (validityMask & mask) > 0 ? 1 : 0; 240} 241 242float ProbeDistance(uint subdiv) 243{ 244 return pow(3, subdiv) * _APVMinBrickSize / 3.0f; 245} 246 247half ProbeDistanceHalf(uint subdiv) 248{ 249 return pow(half(3), half(subdiv)) * half(_APVMinBrickSize) / 3.0; 250} 251 252float3 GetSnappedProbePosition(float3 posWS, uint subdiv) 253{ 254 float3 distBetweenProbes = ProbeDistance(subdiv); 255 float3 dividedPos = posWS / distBetweenProbes; 256 return (dividedPos - frac(dividedPos)) * distBetweenProbes; 257} 258 259// ------------------------------------------------------------- 260// Indexing functions 261// ------------------------------------------------------------- 262 263bool LoadCellIndexMetaData(uint cellFlatIdx, out uint chunkIndex, out int stepSize, out int3 minRelativeIdx, out uint3 sizeOfValid) 264{ 265 uint3 metaData = _APVResCellIndices[cellFlatIdx]; 266 267 // See ProbeIndexOfIndices.cs for packing 268 chunkIndex = metaData.x & 0x1FFFFFFF; 269 stepSize = round(pow(3, (metaData.x >> 29) & 0x7)); 270 271 minRelativeIdx.x = metaData.y & 0x3FF; 272 minRelativeIdx.y = (metaData.y >> 10) & 0x3FF; 273 minRelativeIdx.z = (metaData.y >> 20) & 0x3FF; 274 275 sizeOfValid.x = metaData.z & 0x3FF; 276 sizeOfValid.y = (metaData.z >> 10) & 0x3FF; 277 sizeOfValid.z = (metaData.z >> 20) & 0x3FF; 278 279 return metaData.x != 0xFFFFFFFF; 280} 281 282uint GetIndexData(APVResources apvRes, float3 posWS) 283{ 284 float3 entryPos = floor(posWS * _APVRcpIndirectionEntryDim); 285 float3 topLeftEntryWS = entryPos * _APVIndirectionEntryDim; 286 287 bool isALoadedCell = all(entryPos >= _APVMinLoadedCellInEntries) && all(entryPos <= _APVMaxLoadedCellInEntries); 288 289 // Make sure we start from 0 290 uint3 entryPosInt = (uint3)(entryPos - _APVMinEntryPosition); 291 uint flatIdx = dot(entryPosInt, uint3(1, _APVEntryCount.x, _APVEntryCount.y)); 292 293 // Dynamic branch must be enforced to avoid out-of-bounds memory access in LoadCellIndexMetaData 294 uint result = 0xffffffff; 295 UNITY_BRANCH if (isALoadedCell) 296 { 297 int stepSize; 298 int3 minRelativeIdx; 299 uint3 sizeOfValid; 300 uint chunkIdx; 301 if (LoadCellIndexMetaData(flatIdx, chunkIdx, stepSize, minRelativeIdx, sizeOfValid)) 302 { 303 float3 residualPosWS = posWS - topLeftEntryWS; 304 uint3 localBrickIndex = floor(residualPosWS / (_APVMinBrickSize * stepSize)); 305 localBrickIndex = min(localBrickIndex, (uint3)(3 * 3 * 3 - 1)); // due to floating point issue, we may query an invalid brick 306 localBrickIndex -= minRelativeIdx; // Relative to valid region 307 308 UNITY_BRANCH 309 if (all(localBrickIndex < sizeOfValid)) 310 { 311 uint flattenedLocationInCell = dot(localBrickIndex, uint3(sizeOfValid.y, 1, sizeOfValid.x * sizeOfValid.y)); 312 uint locationInPhysicalBuffer = chunkIdx * (uint)PROBE_INDEX_CHUNK_SIZE + flattenedLocationInCell; 313 result = apvRes.index[locationInPhysicalBuffer]; 314 } 315 } 316 } 317 318 return result; 319} 320 321// ------------------------------------------------------------- 322// Loading functions 323// ------------------------------------------------------------- 324APVResources FillAPVResources() 325{ 326 APVResources apvRes; 327 apvRes.index = _APVResIndex; 328 329 apvRes.L0_L1Rx = _APVResL0_L1Rx; 330 331 apvRes.L1G_L1Ry = _APVResL1G_L1Ry; 332 apvRes.L1B_L1Rz = _APVResL1B_L1Rz; 333 334 apvRes.L2_0 = _APVResL2_0; 335 apvRes.L2_1 = _APVResL2_1; 336 apvRes.L2_2 = _APVResL2_2; 337 apvRes.L2_3 = _APVResL2_3; 338 339 apvRes.Validity = _APVResValidity; 340 apvRes.SkyOcclusionL0L1 = _SkyOcclusionTexL0L1; 341 apvRes.SkyShadingDirectionIndices = _SkyShadingDirectionIndicesTex; 342 apvRes.SkyPrecomputedDirections = _SkyPrecomputedDirections; 343 344 apvRes.ProbeOcclusion = _APVProbeOcclusion; 345 346 return apvRes; 347} 348 349 350bool TryToGetPoolUVWAndSubdiv(APVResources apvRes, float3 posWSForSample, out float3 uvw, out uint subdiv) 351{ 352 // resolve the index 353 uint packed_pool_idx = GetIndexData(apvRes, posWSForSample.xyz); 354 355 // unpack pool idx 356 // size is encoded in the upper 4 bits 357 subdiv = (packed_pool_idx >> 28) & 15; 358 359 float flattened_pool_idx = packed_pool_idx & ((1 << 28) - 1); 360 float3 pool_idx; 361 pool_idx.z = floor(flattened_pool_idx * _APVRcpPoolDimXY); 362 flattened_pool_idx -= (pool_idx.z * (_APVPoolDim.x * _APVPoolDim.y)); 363 pool_idx.y = floor(flattened_pool_idx * _APVRcpPoolDim.x); 364 pool_idx.x = floor(flattened_pool_idx - (pool_idx.y * _APVPoolDim.x)); 365 366 // calculate uv offset and scale 367 float brickSizeWS = pow(3.0, subdiv) * _APVMinBrickSize; 368 float3 offset = frac(posWSForSample.xyz / brickSizeWS); // [0;1] in brick space 369 //offset = clamp( offset, 0.25, 0.75 ); // [0.25;0.75] in brick space (is this actually necessary?) 370 371 uvw = (pool_idx + 0.5 + (3.0 * offset)) * _APVRcpPoolDim; // add offset with brick footprint converted to text footprint in pool texel space 372 373 // no valid brick loaded for this index, fallback to ambient probe 374 // Note: we could instead early return when we know we'll have invalid UVs, but some bade code gen on Vulkan generates shader warnings if we do. 375 return packed_pool_idx != 0xffffffffu; 376} 377 378bool TryToGetPoolUVWAndSubdiv(APVResources apvRes, float3 posWS, float3 normalWS, float3 viewDirWS, out float3 uvw, out uint subdiv, out float3 biasedPosWS) 379{ 380 biasedPosWS = (posWS + normalWS * _APVNormalBias) + viewDirWS * _APVViewBias; 381 return TryToGetPoolUVWAndSubdiv(apvRes, biasedPosWS, uvw, subdiv); 382} 383 384bool TryToGetPoolUVW(APVResources apvRes, float3 posWS, float3 normalWS, float3 viewDir, out float3 uvw) 385{ 386 uint unusedSubdiv; 387 float3 unusedPos; 388 return TryToGetPoolUVWAndSubdiv(apvRes, posWS, normalWS, viewDir, uvw, unusedSubdiv, unusedPos); 389} 390 391 392APVSample SampleAPV(APVResources apvRes, float3 uvw) 393{ 394 APVSample apvSample; 395 half4 L0_L1Rx = half4(SAMPLE_TEXTURE3D_LOD(apvRes.L0_L1Rx, s_linear_clamp_sampler, uvw, 0).rgba); 396 half4 L1G_L1Ry = half4(SAMPLE_TEXTURE3D_LOD(apvRes.L1G_L1Ry, s_linear_clamp_sampler, uvw, 0).rgba); 397 half4 L1B_L1Rz = half4(SAMPLE_TEXTURE3D_LOD(apvRes.L1B_L1Rz, s_linear_clamp_sampler, uvw, 0).rgba); 398 399 apvSample.L0 = L0_L1Rx.xyz; 400 apvSample.L1_R = half3(L0_L1Rx.w, L1G_L1Ry.w, L1B_L1Rz.w); 401 apvSample.L1_G = L1G_L1Ry.xyz; 402 apvSample.L1_B = L1B_L1Rz.xyz; 403 404#ifdef PROBE_VOLUMES_L2 405 apvSample.L2_R = half4(SAMPLE_TEXTURE3D_LOD(apvRes.L2_0, s_linear_clamp_sampler, uvw, 0).rgba); 406 apvSample.L2_G = half4(SAMPLE_TEXTURE3D_LOD(apvRes.L2_1, s_linear_clamp_sampler, uvw, 0).rgba); 407 apvSample.L2_B = half4(SAMPLE_TEXTURE3D_LOD(apvRes.L2_2, s_linear_clamp_sampler, uvw, 0).rgba); 408 apvSample.L2_C = half3(SAMPLE_TEXTURE3D_LOD(apvRes.L2_3, s_linear_clamp_sampler, uvw, 0).rgb); 409#endif // PROBE_VOLUMES_L2 410 411 if (_APVSkyOcclusionWeight > 0) 412 apvSample.skyOcclusionL0L1 = SAMPLE_TEXTURE3D_LOD(apvRes.SkyOcclusionL0L1, s_linear_clamp_sampler, uvw, 0).rgba; 413 else 414 apvSample.skyOcclusionL0L1 = float4(0, 0, 0, 0); 415 416 if (_APVSkyDirectionWeight > 0) 417 { 418 // No interpolation for sky shading indices 419 int3 texCoord = uvw * _APVPoolDim - 0.5f; 420 uint index = LOAD_TEXTURE3D(apvRes.SkyShadingDirectionIndices, texCoord).x * 255.0; 421 422 if (index == 255) 423 apvSample.skyShadingDirection = float3(0, 0, 0); 424 else 425 apvSample.skyShadingDirection = apvRes.SkyPrecomputedDirections[index].rgb; 426 } 427 else 428 apvSample.skyShadingDirection = float3(0, 0, 0); 429 430#ifdef USE_APV_PROBE_OCCLUSION 431 apvSample.probeOcclusion = SAMPLE_TEXTURE3D_LOD(apvRes.ProbeOcclusion, s_linear_clamp_sampler, uvw, 0).rgba; 432#endif 433 434 apvSample.status = APV_SAMPLE_STATUS_ENCODED; 435 436 return apvSample; 437} 438 439APVSample LoadAndDecodeAPV(APVResources apvRes, int3 loc) 440{ 441 APVSample apvSample; 442 443 half4 L0_L1Rx = half4(LOAD_TEXTURE3D(apvRes.L0_L1Rx, loc).rgba); 444 half4 L1G_L1Ry = half4(LOAD_TEXTURE3D(apvRes.L1G_L1Ry, loc).rgba); 445 half4 L1B_L1Rz = half4(LOAD_TEXTURE3D(apvRes.L1B_L1Rz, loc).rgba); 446 447 apvSample.L0 = L0_L1Rx.xyz; 448 apvSample.L1_R = half3(L0_L1Rx.w, L1G_L1Ry.w, L1B_L1Rz.w); 449 apvSample.L1_G = L1G_L1Ry.xyz; 450 apvSample.L1_B = L1B_L1Rz.xyz; 451 452#ifdef PROBE_VOLUMES_L2 453 apvSample.L2_R = half4(LOAD_TEXTURE3D(apvRes.L2_0, loc).rgba); 454 apvSample.L2_G = half4(LOAD_TEXTURE3D(apvRes.L2_1, loc).rgba); 455 apvSample.L2_B = half4(LOAD_TEXTURE3D(apvRes.L2_2, loc).rgba); 456 apvSample.L2_C = half3(LOAD_TEXTURE3D(apvRes.L2_3, loc).rgb); 457#endif // PROBE_VOLUMES_L2 458 459#ifdef USE_APV_PROBE_OCCLUSION 460 apvSample.probeOcclusion = LOAD_TEXTURE3D(apvRes.ProbeOcclusion, loc).rgba; 461#endif 462 463 apvSample.status = APV_SAMPLE_STATUS_ENCODED; 464 apvSample.Decode(); 465 466 return apvSample; 467} 468 469void WeightSample(inout APVSample apvSample, half weight) 470{ 471 apvSample.L0 *= weight; 472 apvSample.L1_R *= weight; 473 apvSample.L1_G *= weight; 474 apvSample.L1_B *= weight; 475 476#ifdef PROBE_VOLUMES_L2 477 apvSample.L2_R *= weight; 478 apvSample.L2_G *= weight; 479 apvSample.L2_B *= weight; 480 apvSample.L2_C *= weight; 481#endif // PROBE_VOLUMES_L2 482 483#ifdef USE_APV_PROBE_OCCLUSION 484 apvSample.probeOcclusion *= weight; 485#endif 486 487 apvSample.skyOcclusionL0L1 *= weight; 488} 489 490void AccumulateSamples(inout APVSample dst, APVSample other, half weight) 491{ 492 WeightSample(other, weight); 493 dst.L0 += other.L0; 494 dst.L1_R += other.L1_R; 495 dst.L1_G += other.L1_G; 496 dst.L1_B += other.L1_B; 497 498#ifdef PROBE_VOLUMES_L2 499 dst.L2_R += other.L2_R; 500 dst.L2_G += other.L2_G; 501 dst.L2_B += other.L2_B; 502 dst.L2_C += other.L2_C; 503#endif // PROBE_VOLUMES_L2 504 505#ifdef USE_APV_PROBE_OCCLUSION 506 dst.probeOcclusion += other.probeOcclusion; 507#endif 508 509 dst.skyOcclusionL0L1 += other.skyOcclusionL0L1; 510} 511 512uint LoadValidityMask(APVResources apvRes, uint renderingLayer, int3 coord) 513{ 514 float rawValidity = LOAD_TEXTURE3D(apvRes.Validity, coord).x; 515 516 uint validityMask; 517 if (_APVLayerCount == 1) 518 { 519 validityMask = rawValidity * 255.0; 520 } 521 else 522 { 523 // If the object is on none of the masks, enable all layers to still sample validity correctly 524 uint globalLayer = _ProbeVolumeLayerMask[0] | _ProbeVolumeLayerMask[1] | _ProbeVolumeLayerMask[2] | _ProbeVolumeLayerMask[3]; 525 if ((renderingLayer & globalLayer) == 0) renderingLayer = 0xFFFFFFFF; 526 527 validityMask = 0; 528 if ((renderingLayer & _ProbeVolumeLayerMask[0]) != 0) 529 validityMask = asuint(rawValidity); 530 if ((renderingLayer & _ProbeVolumeLayerMask[1]) != 0) 531 validityMask |= asuint(rawValidity) >> 8; 532 if ((renderingLayer & _ProbeVolumeLayerMask[2]) != 0) 533 validityMask |= asuint(rawValidity) >> 16; 534 if ((renderingLayer & _ProbeVolumeLayerMask[3]) != 0) 535 validityMask |= asuint(rawValidity) >> 24; 536 validityMask = validityMask & 0xFF; 537 } 538 539 return validityMask; 540} 541 542float UnpackSamplingWeight(uint mask, float3 texFrac) 543{ 544 int3 dir = 0; 545 dir.x = (int)(mask >> 1) & 3; 546 dir.y = (int)(mask >> 4) & 3; 547 dir.z = (int)(mask >> 7) & 3; 548 549 float3 weights; 550 weights.x = saturate(mask & 1) + (dir.x - 1) * texFrac.x; 551 weights.y = saturate(mask & 8) + (dir.y - 1) * texFrac.y; 552 weights.z = saturate(mask & 64) + (dir.z - 1) * texFrac.z; 553 554 return weights.x * weights.y * weights.z; 555} 556 557void UnpackSamplingOffset(uint mask, float3 texFrac, out float3 uvwOffset) 558{ 559 uvwOffset.x = saturate(mask & 4) + (saturate(mask & 2) * texFrac.x - texFrac.x); 560 uvwOffset.y = saturate(mask & 32) + (saturate(mask & 16) * texFrac.y - texFrac.y); 561 uvwOffset.z = saturate(mask & 256) + (saturate(mask & 128) * texFrac.z - texFrac.z); 562} 563 564APVSample QualityLeakReduction(APVResources apvRes, uint renderingLayer, inout float3 uvw) 565{ 566 float3 texCoord = uvw * _APVPoolDim - .5f; 567 float3 texFrac = frac(texCoord); 568 569 half3 offset; 570 571 uint validityMask = LoadValidityMask(apvRes, renderingLayer, texCoord); 572 uint antileak = _AntiLeakData[validityMask]; 573 574 UnpackSamplingOffset(antileak >> 0, texFrac, offset); 575 APVSample apvSample = SampleAPV(apvRes, uvw + offset * _APVRcpPoolDim); 576 577 // Optional additional samples for quality leak reduction 578 if (validityMask != 0xFF) 579 { 580 float3 weights; 581 weights.x = UnpackSamplingWeight(antileak >> 0, texFrac); 582 weights.y = UnpackSamplingWeight(antileak >> 9, texFrac); 583 weights.z = UnpackSamplingWeight(antileak >> 18, texFrac); 584 585 weights *= rcp(max(0.0001, weights.x+weights.y+weights.z)); 586 WeightSample(apvSample, weights.x); 587 588 UNITY_BRANCH if (weights.y != 0) 589 { 590 UnpackSamplingOffset(antileak >> 9, texFrac, offset); 591 APVSample partialSample = SampleAPV(apvRes, uvw + offset * _APVRcpPoolDim); 592 AccumulateSamples(apvSample, partialSample, weights.y); 593 } 594 595 UNITY_BRANCH if (weights.z != 0) 596 { 597 UnpackSamplingOffset(antileak >> 18, texFrac, offset); 598 APVSample partialSample = SampleAPV(apvRes, uvw + offset * _APVRcpPoolDim); 599 AccumulateSamples(apvSample, partialSample, weights.z); 600 } 601 } 602 603 return apvSample; 604} 605 606void WarpUVWLeakReduction(APVResources apvRes, uint renderingLayer, inout float3 uvw) 607{ 608 float3 texCoord = uvw * _APVPoolDim - 0.5f; 609 half3 texFrac = half3(frac(texCoord)); 610 611 uint validityMask = LoadValidityMask(apvRes, renderingLayer, texCoord); 612 if (validityMask != 0xFF) 613 { 614 half weights[8]; 615 half totalW = 0.0; 616 half3 offset = 0; 617 uint i; 618 619 UNITY_UNROLL 620 for (i = 0; i < 8; ++i) 621 { 622 uint3 probeOffset = GetSampleOffset(i); 623 half validityWeight = 624 ((probeOffset.x == 1) ? texFrac.x : 1.0f - texFrac.x) * 625 ((probeOffset.y == 1) ? texFrac.y : 1.0f - texFrac.y) * 626 ((probeOffset.z == 1) ? texFrac.z : 1.0f - texFrac.z); 627 628 validityWeight *= GetValidityWeight(i, validityMask); 629 half weight = saturate(validityWeight); 630 weights[i] = weight; 631 totalW += weight; 632 } 633 634 offset = -texFrac; 635 half rcpTotalW = rcp(max(0.0001, totalW)); 636 637 UNITY_UNROLL 638 for (i = 0; i < 8; ++i) 639 { 640 uint3 probeOffset = GetSampleOffset(i); 641 offset += (half3)probeOffset * (weights[i] * rcpTotalW); 642 } 643 644 uvw += (float3)offset * _APVRcpPoolDim; 645 } 646} 647 648APVSample SampleAPV(APVResources apvRes, float3 posWS, float3 biasNormalWS, uint renderingLayer, float3 viewDir) 649{ 650 APVSample outSample; 651 652 posWS -= _APVWorldOffset; 653 654 uint subdiv; 655 float3 pool_uvw; 656 float3 biasedPosWS; 657 if (TryToGetPoolUVWAndSubdiv(apvRes, posWS, biasNormalWS, viewDir, pool_uvw, subdiv, biasedPosWS)) 658 { 659 UNITY_BRANCH if (_APVLeakReductionMode == APVLEAKREDUCTIONMODE_QUALITY) 660 { 661 outSample = QualityLeakReduction(apvRes, renderingLayer, pool_uvw); 662 } 663 else 664 { 665 if (_APVLeakReductionMode == APVLEAKREDUCTIONMODE_PERFORMANCE) 666 WarpUVWLeakReduction(apvRes, renderingLayer, pool_uvw); 667 outSample = SampleAPV(apvRes, pool_uvw); 668 } 669 } 670 else 671 { 672 ZERO_INITIALIZE(APVSample, outSample); 673 674 #ifdef USE_APV_PROBE_OCCLUSION 675 // The "neutral" value for probe occlusion is 1.0. We write it here to prevent objects that are outside 676 // the bounds of the probe volume from being entirely shadowed. 677 outSample.probeOcclusion = 1.0f; 678 #endif 679 680 outSample.status = APV_SAMPLE_STATUS_INVALID; 681 } 682 683 return outSample; 684} 685 686 687APVSample SampleAPV(float3 posWS, float3 biasNormalWS, uint renderingLayer, float3 viewDir) 688{ 689 APVResources apvRes = FillAPVResources(); 690 return SampleAPV(apvRes, posWS, biasNormalWS, renderingLayer, viewDir); 691} 692 693// ------------------------------------------------------------- 694// Dynamic Sky Handling 695// ------------------------------------------------------------- 696 697// Expects Layout DC, x, y, z 698// See on baking side in DynamicGISkyOcclusion.hlsl 699float EvalSHSkyOcclusion(float3 dir, APVSample apvSample) 700{ 701 // L0 L1 702 float4 temp = float4(kSHBasis0, kSHBasis1 * dir.x, kSHBasis1 * dir.y, kSHBasis1 * dir.z); 703 return _APVSkyOcclusionWeight * dot(temp, apvSample.skyOcclusionL0L1); 704} 705 706float3 EvaluateOccludedSky(APVSample apvSample, float3 N) 707{ 708 float occValue = EvalSHSkyOcclusion(N, apvSample); 709 float3 shadingNormal = N; 710 711 if (_APVSkyDirectionWeight > 0) 712 { 713 shadingNormal = apvSample.skyShadingDirection; 714 float normSquared = dot(shadingNormal, shadingNormal); 715 if (normSquared < 0.2f) 716 shadingNormal = N; 717 else 718 { 719 shadingNormal = shadingNormal * rsqrt(normSquared); 720 } 721 } 722 return occValue * EvaluateAmbientProbe(shadingNormal); 723} 724 725// ------------------------------------------------------------- 726// Internal Evaluation functions (avoid usage in caller code outside this file) 727// ------------------------------------------------------------- 728float3 EvaluateAPVL0(APVSample apvSample) 729{ 730 return apvSample.L0; 731} 732 733void EvaluateAPVL1(APVSample apvSample, float3 N, out float3 diffuseLighting) 734{ 735 diffuseLighting = SHEvalLinearL1(N, apvSample.L1_R, apvSample.L1_G, apvSample.L1_B); 736} 737 738#ifdef PROBE_VOLUMES_L2 739void EvaluateAPVL1L2(APVSample apvSample, float3 N, out float3 diffuseLighting) 740{ 741 EvaluateAPVL1(apvSample, N, diffuseLighting); 742 diffuseLighting += SHEvalLinearL2(N, apvSample.L2_R, apvSample.L2_G, apvSample.L2_B, float4(apvSample.L2_C, 0.0f)); 743} 744#endif 745 746 747// ------------------------------------------------------------- 748// "Public" Evaluation functions, the one that callers outside this file should use 749// ------------------------------------------------------------- 750void EvaluateAdaptiveProbeVolume(APVSample apvSample, float3 normalWS, out float3 bakeDiffuseLighting) 751{ 752 if (apvSample.status != APV_SAMPLE_STATUS_INVALID) 753 { 754 apvSample.Decode(); 755 756#if defined(PROBE_VOLUMES_L1) 757 EvaluateAPVL1(apvSample, normalWS, bakeDiffuseLighting); 758#elif defined(PROBE_VOLUMES_L2) 759 EvaluateAPVL1L2(apvSample, normalWS, bakeDiffuseLighting); 760#endif 761 762 bakeDiffuseLighting += apvSample.L0; 763 if (_APVSkyOcclusionWeight > 0) 764 bakeDiffuseLighting += EvaluateOccludedSky(apvSample, normalWS); 765 766 //if (_APVWeight < 1.f) 767 { 768 bakeDiffuseLighting = bakeDiffuseLighting * _APVWeight; 769 } 770 } 771 else 772 { 773 // no valid brick, fallback to ambient probe 774 bakeDiffuseLighting = EvaluateAmbientProbe(normalWS); 775 } 776} 777 778void EvaluateAdaptiveProbeVolume(APVSample apvSample, float3 normalWS, float3 backNormalWS, out float3 bakeDiffuseLighting, out float3 backBakeDiffuseLighting) 779{ 780 if (apvSample.status != APV_SAMPLE_STATUS_INVALID) 781 { 782 apvSample.Decode(); 783 784#ifdef PROBE_VOLUMES_L1 785 EvaluateAPVL1(apvSample, normalWS, bakeDiffuseLighting); 786 EvaluateAPVL1(apvSample, backNormalWS, backBakeDiffuseLighting); 787#elif defined(PROBE_VOLUMES_L2) 788 EvaluateAPVL1L2(apvSample, normalWS, bakeDiffuseLighting); 789 EvaluateAPVL1L2(apvSample, backNormalWS, backBakeDiffuseLighting); 790#endif 791 792 bakeDiffuseLighting += apvSample.L0; 793 backBakeDiffuseLighting += apvSample.L0; 794 if (_APVSkyOcclusionWeight > 0) 795 { 796 bakeDiffuseLighting += EvaluateOccludedSky(apvSample, normalWS); 797 backBakeDiffuseLighting += EvaluateOccludedSky(apvSample, backNormalWS); 798 } 799 800 //if (_APVWeight < 1.f) 801 { 802 bakeDiffuseLighting = bakeDiffuseLighting * _APVWeight; 803 backBakeDiffuseLighting = backBakeDiffuseLighting * _APVWeight; 804 } 805 } 806 else 807 { 808 // no valid brick, fallback to ambient probe 809 bakeDiffuseLighting = EvaluateAmbientProbe(normalWS); 810 backBakeDiffuseLighting = EvaluateAmbientProbe(backNormalWS); 811 } 812} 813 814void EvaluateAdaptiveProbeVolume(in float3 posWS, in float3 normalWS, in float3 backNormalWS, in float3 reflDir, in float3 viewDir, 815 in float2 positionSS, in uint renderingLayer, out float3 bakeDiffuseLighting, out float3 backBakeDiffuseLighting, out float3 lightingInReflDir) 816{ 817 APVResources apvRes = FillAPVResources(); 818 819 posWS = AddNoiseToSamplingPosition(posWS, positionSS, viewDir); 820 821 APVSample apvSample = SampleAPV(posWS, normalWS, renderingLayer, viewDir); 822 823 if (apvSample.status != APV_SAMPLE_STATUS_INVALID) 824 { 825 apvSample.Decode(); 826 827#ifdef PROBE_VOLUMES_L1 828 EvaluateAPVL1(apvSample, normalWS, bakeDiffuseLighting); 829 EvaluateAPVL1(apvSample, backNormalWS, backBakeDiffuseLighting); 830 EvaluateAPVL1(apvSample, reflDir, lightingInReflDir); 831#elif defined(PROBE_VOLUMES_L2) 832 EvaluateAPVL1L2(apvSample, normalWS, bakeDiffuseLighting); 833 EvaluateAPVL1L2(apvSample, backNormalWS, backBakeDiffuseLighting); 834 EvaluateAPVL1L2(apvSample, reflDir, lightingInReflDir); 835#endif 836 837 bakeDiffuseLighting += apvSample.L0; 838 backBakeDiffuseLighting += apvSample.L0; 839 lightingInReflDir += apvSample.L0; 840 if (_APVSkyOcclusionWeight > 0) 841 { 842 bakeDiffuseLighting += EvaluateOccludedSky(apvSample, normalWS); 843 backBakeDiffuseLighting += EvaluateOccludedSky(apvSample, backNormalWS); 844 lightingInReflDir += EvaluateOccludedSky(apvSample, reflDir); 845 } 846 847 //if (_APVWeight < 1.f) 848 { 849 bakeDiffuseLighting = bakeDiffuseLighting * _APVWeight; 850 backBakeDiffuseLighting = backBakeDiffuseLighting * _APVWeight; 851 } 852 } 853 else 854 { 855 bakeDiffuseLighting = EvaluateAmbientProbe(normalWS); 856 backBakeDiffuseLighting = EvaluateAmbientProbe(backNormalWS); 857 lightingInReflDir = -1; 858 } 859} 860 861void EvaluateAdaptiveProbeVolume(in float3 posWS, in float3 normalWS, in float3 backNormalWS, in float3 viewDir, 862 in float2 positionSS, in uint renderingLayer, out float3 bakeDiffuseLighting, out float3 backBakeDiffuseLighting, out float4 probeOcclusion) 863{ 864 bakeDiffuseLighting = float3(0.0, 0.0, 0.0); 865 backBakeDiffuseLighting = float3(0.0, 0.0, 0.0); 866 867 posWS = AddNoiseToSamplingPosition(posWS, positionSS, viewDir); 868 869 APVSample apvSample = SampleAPV(posWS, normalWS, renderingLayer, viewDir); 870#ifdef USE_APV_PROBE_OCCLUSION 871 probeOcclusion = apvSample.probeOcclusion; 872#else 873 probeOcclusion = 1; 874#endif 875 876 EvaluateAdaptiveProbeVolume(apvSample, normalWS, backNormalWS, bakeDiffuseLighting, backBakeDiffuseLighting); 877} 878 879void EvaluateAdaptiveProbeVolume(in float3 posWS, in float3 normalWS, in float3 backNormalWS, in float3 viewDir, 880 in float2 positionSS, in uint renderingLayer, out float3 bakeDiffuseLighting, out float3 backBakeDiffuseLighting) 881{ 882 float4 unusedProbeOcclusion = 0; 883 EvaluateAdaptiveProbeVolume(posWS, normalWS, backNormalWS, viewDir, positionSS, renderingLayer, bakeDiffuseLighting, backBakeDiffuseLighting, unusedProbeOcclusion); 884} 885 886void EvaluateAdaptiveProbeVolume(in float3 posWS, in float3 normalWS, in float3 viewDir, in float2 positionSS, in uint renderingLayer, 887 out float3 bakeDiffuseLighting, out float4 probeOcclusion) 888{ 889 bakeDiffuseLighting = float3(0.0, 0.0, 0.0); 890 891 posWS = AddNoiseToSamplingPosition(posWS, positionSS, viewDir); 892 893 APVSample apvSample = SampleAPV(posWS, normalWS, renderingLayer, viewDir); 894#ifdef USE_APV_PROBE_OCCLUSION 895 probeOcclusion = apvSample.probeOcclusion; 896#else 897 probeOcclusion = 1; 898#endif 899 900 EvaluateAdaptiveProbeVolume(apvSample, normalWS, bakeDiffuseLighting); 901} 902 903void EvaluateAdaptiveProbeVolume(in float3 posWS, in float3 normalWS, in float3 viewDir, in float2 positionSS, in uint renderingLayer, 904 out float3 bakeDiffuseLighting) 905{ 906 float4 unusedProbeOcclusion = 0; 907 EvaluateAdaptiveProbeVolume(posWS, normalWS, viewDir, positionSS, renderingLayer, bakeDiffuseLighting, unusedProbeOcclusion); 908} 909 910void EvaluateAdaptiveProbeVolume(in float3 posWS, in float2 positionSS, out float3 bakeDiffuseLighting) 911{ 912 APVResources apvRes = FillAPVResources(); 913 914 posWS = AddNoiseToSamplingPosition(posWS, positionSS, 1); 915 posWS -= _APVWorldOffset; 916 917 float3 uvw; 918 if (TryToGetPoolUVW(apvRes, posWS, 0, 0, uvw)) 919 { 920 bakeDiffuseLighting = SAMPLE_TEXTURE3D_LOD(apvRes.L0_L1Rx, s_linear_clamp_sampler, uvw, 0).rgb; 921 } 922 else 923 { 924 bakeDiffuseLighting = EvaluateAmbientProbe(0); 925 } 926} 927 928// public APIs for backward compatibility 929// to be removed after Unity 6 930void EvaluateAdaptiveProbeVolume(in float3 posWS, in float3 normalWS, in float3 backNormalWS, in float3 reflDir, in float3 viewDir, in float2 positionSS, out float3 bakeDiffuseLighting, out float3 backBakeDiffuseLighting, out float3 lightingInReflDir) 931{ EvaluateAdaptiveProbeVolume(posWS, normalWS, backNormalWS, reflDir, viewDir, positionSS, 0xFFFFFFFF, bakeDiffuseLighting, backBakeDiffuseLighting, lightingInReflDir); } 932void EvaluateAdaptiveProbeVolume(in float3 posWS, in float3 normalWS, in float3 backNormalWS, in float3 viewDir, in float2 positionSS, out float3 bakeDiffuseLighting, out float3 backBakeDiffuseLighting) 933{ EvaluateAdaptiveProbeVolume(posWS, normalWS, backNormalWS, viewDir, positionSS, 0xFFFFFFFF, bakeDiffuseLighting, backBakeDiffuseLighting); } 934void EvaluateAdaptiveProbeVolume(in float3 posWS, in float3 normalWS, in float3 viewDir, in float2 positionSS, out float3 bakeDiffuseLighting) 935{ EvaluateAdaptiveProbeVolume(posWS, normalWS, viewDir, positionSS, 0xFFFFFFFF, bakeDiffuseLighting); } 936 937// ------------------------------------------------------------- 938// Reflection Probe Normalization functions 939// ------------------------------------------------------------- 940// Same idea as in Rendering of COD:IW [Drobot 2017] 941 942float EvaluateReflectionProbeSH(float3 sampleDir, float4 reflProbeSHL0L1, float4 reflProbeSHL2_1, float reflProbeSHL2_2) 943{ 944 float outFactor = 0; 945 float L0 = reflProbeSHL0L1.x; 946 float L1 = dot(reflProbeSHL0L1.yzw, sampleDir); 947 948 outFactor = L0 + L1; 949 950#ifdef PROBE_VOLUMES_L2 951 952 // IMPORTANT: The encoding is unravelled C# side before being sent 953 954 float4 vB = sampleDir.xyzz * sampleDir.yzzx; 955 // First 4 coeff. 956 float L2 = dot(reflProbeSHL2_1, vB); 957 float vC = sampleDir.x * sampleDir.x - sampleDir.y * sampleDir.y; 958 L2 += reflProbeSHL2_2 * vC; 959 960 outFactor += L2; 961#endif // PROBE_VOLUMES_L2 962 963 return outFactor; 964} 965 966float GetReflectionProbeNormalizationFactor(float3 lightingInReflDir, float3 sampleDir, float4 reflProbeSHL0L1, float4 reflProbeSHL2_1, float reflProbeSHL2_2) 967{ 968 float refProbeNormalization = EvaluateReflectionProbeSH(sampleDir, reflProbeSHL0L1, reflProbeSHL2_1, reflProbeSHL2_2); 969 970 float localNormalization = Luminance(real3(lightingInReflDir)); 971 return lerp(1.f, clamp(SafeDiv(localNormalization, refProbeNormalization), _APVMinReflProbeNormalizationFactor, _APVMaxReflProbeNormalizationFactor), _APVWeight); 972 973} 974 975#endif // __PROBEVOLUME_HLSL__