|
| 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 | +} |
0 commit comments