|
|
|
|
|
|
// Inputs & outputs |
|
|
|
//-------------------------------------------------------------------------------------------------- |
|
|
|
|
|
|
|
RW_TEXTURE3D(float4, _VBufferLightingCurr); // RGB = radiance, A = optical depth |
|
|
|
TEXTURE3D(_VBufferLightingPrev); // RGB = radiance, A = optical depth |
|
|
|
RW_TEXTURE3D(float4, _VBufferLightingIntegral); // RGB = radiance, A = optical depth |
|
|
|
TEXTURE3D(_VBufferLightingHistory); // RGB = radiance, A = confidence (similarity) |
|
|
|
TEXTURE3D(_VBufferLightingFeedback); // RGB = radiance, A = confidence (similarity) |
|
|
|
// TODO: avoid creating another Constant Buffer... |
|
|
|
float4x4 _VBufferCoordToViewDirWS; // Actually just 3x3, but Unity can only set 4x4 |
|
|
|
float4x4 _VBufferCoordToViewDirWS; // Actually just 3x3, but Unity can only set 4x4 |
|
|
|
CBUFFER_END |
|
|
|
|
|
|
|
//-------------------------------------------------------------------------------------------------- |
|
|
|
|
|
|
return ray.originWS + t * ray.directionWS; |
|
|
|
} |
|
|
|
|
|
|
|
// Computes the in-scattered radiance along the ray. |
|
|
|
void FillVolumetricLightingBuffer(LightLoopContext context, uint featureFlags, |
|
|
|
PositionInputs posInput, Ray ray) |
|
|
|
BakeLightingData unused; // Unused, so define once |
|
|
|
|
|
|
|
// Computes the light integral (in-scattered radiance) within the voxel. |
|
|
|
// Multiplication by the scattering coefficient and the phase function is performed outside. |
|
|
|
float3 EvaluateVoxelLighting(LightLoopContext context, uint featureFlags, PositionInputs posInput, |
|
|
|
Ray ray, float t0, float t1, float dt, float rndVal, float extinction |
|
|
|
#ifdef LIGHTLOOP_TILE_PASS |
|
|
|
, uint clusterIndices[2], float clusterDepths[2]) |
|
|
|
#else |
|
|
|
) |
|
|
|
#endif |
|
|
|
BakeLightingData unused; // Unused, so define once |
|
|
|
float3 voxelRadiance = 0; |
|
|
|
float z0 = _VBufferDepthEncodingParams.x; // Start integration from the near plane |
|
|
|
float t0 = ray.ratioLenToZ * z0; |
|
|
|
float de = rcp(VBUFFER_SLICE_COUNT); // Log-encoded distance between slices |
|
|
|
|
|
|
|
float3 totalRadiance = 0; |
|
|
|
float opticalDepth = 0; |
|
|
|
|
|
|
|
uint sliceCountHack = max(VBUFFER_SLICE_COUNT, (uint)_VBufferDepthEncodingParams.x); // Prevent unrolling... |
|
|
|
|
|
|
|
#ifdef LIGHTLOOP_TILE_PASS |
|
|
|
// Our voxel is not necessarily completely inside a single light cluster. |
|
|
|
// Note that Z-binning can solve this problem, as we can iterate over all Z-bins |
|
|
|
// to compute min/max light indices, and then use this range for the entire slice. |
|
|
|
uint clusterIndices[2]; |
|
|
|
float clusterDepths[2]; |
|
|
|
clusterIndices[0] = GetLightClusterIndex(posInput.tileCoord, z0); |
|
|
|
clusterDepths[0] = GetLightClusterMinLinearDepth(posInput.tileCoord, clusterIndices[0]); |
|
|
|
#endif // LIGHTLOOP_TILE_PASS |
|
|
|
|
|
|
|
// TODO: replace 'sliceCountHack' with VBUFFER_SLICE_COUNT when the shader compiler bug is fixed. |
|
|
|
for (uint slice = 0; slice < sliceCountHack; slice++) |
|
|
|
if (featureFlags & LIGHTFEATUREFLAGS_DIRECTIONAL) |
|
|
|
float e1 = slice * de + de; // (slice + 1) / sliceCount |
|
|
|
float z1 = DecodeLogarithmicDepth(e1, _VBufferDepthEncodingParams); |
|
|
|
float t1 = ray.ratioLenToZ * z1; |
|
|
|
float dt = t1 - t0; |
|
|
|
float tOffset, weight; |
|
|
|
ImportanceSampleHomogeneousMedium(rndVal, extinction, dt, tOffset, weight); |
|
|
|
// Compute the position of the center of the voxel. |
|
|
|
// We will use it for participating media sampling and reprojection. |
|
|
|
float tc = t0 + 0.5 * dt; |
|
|
|
float3 centerWS = GetPointAtDistance(ray, tc); |
|
|
|
|
|
|
|
// Reproject 'centerWS'. It performs a point sample. |
|
|
|
float2 reprojPosNDC = ComputeNormalizedDeviceCoordinates(centerWS, _PrevViewProjMatrix); |
|
|
|
float reprojZ = mul(_PrevViewProjMatrix, float4(centerWS, 1)).w; |
|
|
|
float4 reprojValue = LoadInScatteredRadianceAndTransmittance(reprojPosNDC, reprojZ, |
|
|
|
_VBufferLightingPrev, |
|
|
|
_VBufferResolutionAndScale, |
|
|
|
_VBufferDepthEncodingParams); |
|
|
|
|
|
|
|
// Sample the participating medium at 'tc' (or 'centerWS'). |
|
|
|
// We consider it to be constant along the interval [t0, t1] (within the voxel). |
|
|
|
float3 scattering = _GlobalFog_Scattering; |
|
|
|
float extinction = _GlobalFog_Extinction; |
|
|
|
|
|
|
|
// TODO: use a low-discrepancy point set. |
|
|
|
float rndVal = 0.5; |
|
|
|
|
|
|
|
float3 sampleRadiance = 0; |
|
|
|
float t = t0 + tOffset; |
|
|
|
posInput.positionWS = GetPointAtDistance(ray, t); |
|
|
|
if (featureFlags & LIGHTFEATUREFLAGS_DIRECTIONAL) |
|
|
|
for (uint i = 0; i < _DirectionalLightCount; ++i) |
|
|
|
float tOffset, weight; |
|
|
|
ImportanceSampleHomogeneousMedium(rndVal, extinction, dt, tOffset, weight); |
|
|
|
// Fetch the light. |
|
|
|
DirectionalLightData light = _DirectionalLightDatas[i]; |
|
|
|
float3 L = -light.forward; // Lights point backwards in Unity |
|
|
|
float t = t0 + tOffset; |
|
|
|
posInput.positionWS = GetPointAtDistance(ray, t); |
|
|
|
float3 color; float attenuation; |
|
|
|
EvaluateLight_Directional(context, posInput, light, unused, 0, L, |
|
|
|
color, attenuation); |
|
|
|
for (uint i = 0; i < _DirectionalLightCount; ++i) |
|
|
|
{ |
|
|
|
// Fetch the light. |
|
|
|
DirectionalLightData light = _DirectionalLightDatas[i]; |
|
|
|
float3 L = -light.forward; // Lights point backwards in Unity |
|
|
|
// Note: the 'weight' accounts for transmittance from 't0' to 't'. |
|
|
|
float intensity = attenuation * weight; |
|
|
|
float3 color; float attenuation; |
|
|
|
EvaluateLight_Directional(context, posInput, light, unused, 0, L, |
|
|
|
color, attenuation); |
|
|
|
// Compute the amount of in-scattered radiance. |
|
|
|
voxelRadiance += intensity * color; |
|
|
|
} |
|
|
|
} |
|
|
|
// Note: the 'weight' accounts for transmittance from 't0' to 't'. |
|
|
|
float intensity = attenuation * weight; |
|
|
|
#ifdef LIGHTLOOP_TILE_PASS |
|
|
|
// Loop over 1 or 2 light clusters. |
|
|
|
int cluster = 0; |
|
|
|
do |
|
|
|
{ |
|
|
|
float tMin = max(t0, ray.ratioLenToZ * clusterDepths[cluster]); |
|
|
|
float tMax = t1; |
|
|
|
// Compute the amount of in-scattered radiance. |
|
|
|
sampleRadiance += intensity * color; |
|
|
|
} |
|
|
|
if (cluster == 0 && (clusterIndices[0] != clusterIndices[1])) |
|
|
|
{ |
|
|
|
tMax = min(t1, ray.ratioLenToZ * clusterDepths[1]); |
|
|
|
#else |
|
|
|
float tMin = t0; |
|
|
|
float tMax = t1; |
|
|
|
#endif // LIGHTLOOP_TILE_PASS |
|
|
|
#ifdef LIGHTLOOP_TILE_PASS |
|
|
|
clusterIndices[1] = GetLightClusterIndex(posInput.tileCoord, z1); |
|
|
|
clusterDepths[1] = GetLightClusterMinLinearDepth(posInput.tileCoord, clusterIndices[1]); |
|
|
|
|
|
|
|
// Loop over 1 or 2 light clusters. |
|
|
|
for (int cluster = 0; cluster < 2; cluster++) |
|
|
|
if (featureFlags & LIGHTFEATUREFLAGS_PUNCTUAL) |
|
|
|
float tMin = max(t0, ray.ratioLenToZ * clusterDepths[cluster]); |
|
|
|
float tMax = t1; |
|
|
|
uint lightCount, lightStart; |
|
|
|
if (cluster == 0 && (clusterIndices[0] != clusterIndices[1])) |
|
|
|
{ |
|
|
|
tMax = min(t1, ray.ratioLenToZ * clusterDepths[1]); |
|
|
|
} |
|
|
|
#else |
|
|
|
float tMin = t0; |
|
|
|
float tMax = t1; |
|
|
|
#endif // LIGHTLOOP_TILE_PASS |
|
|
|
#ifdef LIGHTLOOP_TILE_PASS |
|
|
|
GetCountAndStartCluster(posInput.tileCoord, clusterIndices[cluster], LIGHTCATEGORY_PUNCTUAL, |
|
|
|
lightStart, lightCount); |
|
|
|
#else |
|
|
|
lightCount = _PunctualLightCount; |
|
|
|
lightStart = 0; |
|
|
|
#endif // LIGHTLOOP_TILE_PASS |
|
|
|
if (featureFlags & LIGHTFEATUREFLAGS_PUNCTUAL) |
|
|
|
if (lightCount > 0) |
|
|
|
uint lightCount, lightStart; |
|
|
|
LightData light = FetchLight(lightStart, 0); |
|
|
|
#ifdef LIGHTLOOP_TILE_PASS |
|
|
|
GetCountAndStartCluster(posInput.tileCoord, clusterIndices[cluster], LIGHTCATEGORY_PUNCTUAL, |
|
|
|
lightStart, lightCount); |
|
|
|
#else |
|
|
|
lightCount = _PunctualLightCount; |
|
|
|
lightStart = 0; |
|
|
|
#endif // LIGHTLOOP_TILE_PASS |
|
|
|
uint i = 0, last = lightCount - 1; |
|
|
|
if (lightCount > 0) |
|
|
|
// Box lights require special handling (see the next while loop). |
|
|
|
while (i <= last && light.lightType != GPULIGHTTYPE_PROJECTOR_BOX) |
|
|
|
LightData light = FetchLight(lightStart, 0); |
|
|
|
float tEntr = tMin; |
|
|
|
float tExit = tMax; |
|
|
|
uint i = 0, last = lightCount - 1; |
|
|
|
bool sampleLight = true; |
|
|
|
// Box lights require special handling (see the next while loop). |
|
|
|
while (i <= last && light.lightType != GPULIGHTTYPE_PROJECTOR_BOX) |
|
|
|
// Perform ray-cone intersection for pyramid and spot lights. |
|
|
|
if (light.lightType != GPULIGHTTYPE_POINT) |
|
|
|
float tEntr = tMin; |
|
|
|
float tExit = tMax; |
|
|
|
|
|
|
|
bool sampleLight = true; |
|
|
|
float lenMul = 1; |
|
|
|
// Perform ray-cone intersection for pyramid and spot lights. |
|
|
|
if (light.lightType != GPULIGHTTYPE_POINT) |
|
|
|
if (light.lightType == GPULIGHTTYPE_PROJECTOR_PYRAMID) |
|
|
|
float lenMul = 1; |
|
|
|
// 'light.right' and 'light.up' vectors are pre-scaled on the CPU |
|
|
|
// s.t. if you were to place them at the distance of 1 directly in front |
|
|
|
// of the light, they would give you the "footprint" of the light. |
|
|
|
// For spot lights, the cone fit is exact. |
|
|
|
// For pyramid lights, however, this is the "inscribed" cone |
|
|
|
// (contained within the pyramid), and we want to intersect |
|
|
|
// the "escribed" cone (which contains the pyramid). |
|
|
|
// Therefore, we have to scale the radii by the sqrt(2). |
|
|
|
lenMul = rsqrt(2); |
|
|
|
} |
|
|
|
if (light.lightType == GPULIGHTTYPE_PROJECTOR_PYRAMID) |
|
|
|
{ |
|
|
|
// 'light.right' and 'light.up' vectors are pre-scaled on the CPU |
|
|
|
// s.t. if you were to place them at the distance of 1 directly in front |
|
|
|
// of the light, they would give you the "footprint" of the light. |
|
|
|
// For spot lights, the cone fit is exact. |
|
|
|
// For pyramid lights, however, this is the "inscribed" cone |
|
|
|
// (contained within the pyramid), and we want to intersect |
|
|
|
// the "escribed" cone (which contains the pyramid). |
|
|
|
// Therefore, we have to scale the radii by the sqrt(2). |
|
|
|
lenMul = rsqrt(2); |
|
|
|
} |
|
|
|
float3 coneAxisX = lenMul * light.right; |
|
|
|
float3 coneAxisY = lenMul * light.up; |
|
|
|
float3 coneAxisX = lenMul * light.right; |
|
|
|
float3 coneAxisY = lenMul * light.up; |
|
|
|
sampleLight = IntersectRayCone(ray.originWS, ray.directionWS, |
|
|
|
light.positionWS, light.forward, |
|
|
|
coneAxisX, coneAxisY, |
|
|
|
tMin, tMax, tEntr, tExit); |
|
|
|
} |
|
|
|
sampleLight = IntersectRayCone(ray.originWS, ray.directionWS, |
|
|
|
light.positionWS, light.forward, |
|
|
|
coneAxisX, coneAxisY, |
|
|
|
tMin, tMax, tEntr, tExit); |
|
|
|
} |
|
|
|
if (sampleLight) |
|
|
|
{ |
|
|
|
float t, distSq, rcpPdf; |
|
|
|
ImportanceSamplePunctualLight(rndVal, light.positionWS, |
|
|
|
ray.originWS, ray.directionWS, |
|
|
|
tEntr, tExit, t, distSq, rcpPdf); |
|
|
|
if (sampleLight) |
|
|
|
{ |
|
|
|
float t, distSq, rcpPdf; |
|
|
|
ImportanceSamplePunctualLight(rndVal, light.positionWS, |
|
|
|
ray.originWS, ray.directionWS, |
|
|
|
tEntr, tExit, t, distSq, rcpPdf); |
|
|
|
posInput.positionWS = GetPointAtDistance(ray, t); |
|
|
|
posInput.positionWS = GetPointAtDistance(ray, t); |
|
|
|
float3 lightToSample = posInput.positionWS - light.positionWS; |
|
|
|
float dist = sqrt(distSq); |
|
|
|
float3 L = -lightToSample * rsqrt(distSq); |
|
|
|
float3 lightToSample = posInput.positionWS - light.positionWS; |
|
|
|
float dist = sqrt(distSq); |
|
|
|
float3 L = -lightToSample * rsqrt(distSq); |
|
|
|
float3 color; float attenuation; |
|
|
|
EvaluateLight_Punctual(context, posInput, light, unused, 0, L, dist, distSq, |
|
|
|
color, attenuation); |
|
|
|
float3 color; float attenuation; |
|
|
|
EvaluateLight_Punctual(context, posInput, light, unused, 0, L, dist, distSq, |
|
|
|
color, attenuation); |
|
|
|
float intensity = attenuation * rcpPdf; |
|
|
|
float intensity = attenuation * rcpPdf; |
|
|
|
// Compute transmittance from 't0' to 't'. |
|
|
|
intensity *= TransmittanceHomogeneousMedium(extinction, t - t0); |
|
|
|
// Compute transmittance from 't0' to 't'. |
|
|
|
intensity *= TransmittanceHomogeneousMedium(extinction, t - t0); |
|
|
|
// Compute the amount of in-scattered radiance. |
|
|
|
voxelRadiance += color * intensity; |
|
|
|
} |
|
|
|
// Compute the amount of in-scattered radiance. |
|
|
|
sampleRadiance += color * intensity; |
|
|
|
} |
|
|
|
light = FetchLight(lightStart, min(++i, last)); |
|
|
|
} |
|
|
|
light = FetchLight(lightStart, min(++i, last)); |
|
|
|
} |
|
|
|
while (i <= last) // GPULIGHTTYPE_PROJECTOR_BOX |
|
|
|
{ |
|
|
|
light = FetchLight(lightStart, min(++i, last)); |
|
|
|
light.lightType = GPULIGHTTYPE_PROJECTOR_BOX; |
|
|
|
while (i <= last) // GPULIGHTTYPE_PROJECTOR_BOX |
|
|
|
{ |
|
|
|
light = FetchLight(lightStart, min(++i, last)); |
|
|
|
light.lightType = GPULIGHTTYPE_PROJECTOR_BOX; |
|
|
|
// Convert the box light from OBB to AABB. |
|
|
|
// 'light.right' and 'light.up' vectors are pre-scaled on the CPU by (2/w) and (2/h). |
|
|
|
float3x3 rotMat = float3x3(light.right, light.up, light.forward); |
|
|
|
// Convert the box light from OBB to AABB. |
|
|
|
// 'light.right' and 'light.up' vectors are pre-scaled on the CPU by (2/w) and (2/h). |
|
|
|
float3x3 rotMat = float3x3(light.right, light.up, light.forward); |
|
|
|
float3 o = mul(rotMat, ray.originWS - light.positionWS); |
|
|
|
float3 d = mul(rotMat, ray.directionWS); |
|
|
|
float3 o = mul(rotMat, ray.originWS - light.positionWS); |
|
|
|
float3 d = mul(rotMat, ray.directionWS); |
|
|
|
float range = light.size.x; |
|
|
|
float3 boxPt0 = float3(-1, -1, 0); |
|
|
|
float3 boxPt1 = float3( 1, 1, range); |
|
|
|
float range = light.size.x; |
|
|
|
float3 boxPt0 = float3(-1, -1, 0); |
|
|
|
float3 boxPt1 = float3( 1, 1, range); |
|
|
|
float tEntr, tExit; |
|
|
|
float tEntr, tExit; |
|
|
|
if (IntersectRayAABB(o, d, boxPt0, boxPt1, tMin, tMax, tEntr, tExit)) |
|
|
|
{ |
|
|
|
float tOffset, weight; |
|
|
|
ImportanceSampleHomogeneousMedium(rndVal, extinction, tExit - tEntr, tOffset, weight); |
|
|
|
if (IntersectRayAABB(o, d, boxPt0, boxPt1, tMin, tMax, tEntr, tExit)) |
|
|
|
{ |
|
|
|
float tOffset, weight; |
|
|
|
ImportanceSampleHomogeneousMedium(rndVal, extinction, tExit - tEntr, tOffset, weight); |
|
|
|
float t = tEntr + tOffset; |
|
|
|
posInput.positionWS = GetPointAtDistance(ray, t); |
|
|
|
float t = tEntr + tOffset; |
|
|
|
posInput.positionWS = GetPointAtDistance(ray, t); |
|
|
|
float3 L = -light.forward; |
|
|
|
float3 L = -light.forward; |
|
|
|
float3 color; float attenuation; |
|
|
|
EvaluateLight_Punctual(context, posInput, light, unused, 0, L, 1, 1, |
|
|
|
color, attenuation); |
|
|
|
float3 color; float attenuation; |
|
|
|
EvaluateLight_Punctual(context, posInput, light, unused, 0, L, 1, 1, |
|
|
|
color, attenuation); |
|
|
|
// Note: the 'weight' accounts for transmittance from 'tEntr' to 't'. |
|
|
|
float intensity = attenuation * weight; |
|
|
|
// Note: the 'weight' accounts for transmittance from 'tEntr' to 't'. |
|
|
|
float intensity = attenuation * weight; |
|
|
|
// Compute transmittance from 't0' to 'tEntr'. |
|
|
|
intensity *= TransmittanceHomogeneousMedium(extinction, tEntr - t0); |
|
|
|
// Compute transmittance from 't0' to 'tEntr'. |
|
|
|
intensity *= TransmittanceHomogeneousMedium(extinction, tEntr - t0); |
|
|
|
|
|
|
|
// Compute the amount of in-scattered radiance. |
|
|
|
sampleRadiance += intensity * color; |
|
|
|
} |
|
|
|
// Compute the amount of in-scattered radiance. |
|
|
|
voxelRadiance += intensity * color; |
|
|
|
#ifdef LIGHTLOOP_TILE_PASS |
|
|
|
// Check whether the voxel is completely inside the light cluster. |
|
|
|
if (clusterIndices[0] == clusterIndices[1]) break; |
|
|
|
#ifdef LIGHTLOOP_TILE_PASS |
|
|
|
cluster++; |
|
|
|
// Check whether the voxel is completely inside the light cluster. |
|
|
|
} while ((cluster < 2) && (clusterIndices[0] != clusterIndices[1])); |
|
|
|
#endif // LIGHTLOOP_TILE_PASS |
|
|
|
clusterIndices[0] = clusterIndices[1]; |
|
|
|
clusterDepths[0] = clusterDepths[1]; |
|
|
|
#endif // LIGHTLOOP_TILE_PASS |
|
|
|
return voxelRadiance; |
|
|
|
} |
|
|
|
|
|
|
|
// Computes the in-scattered radiance along the ray. |
|
|
|
void FillVolumetricLightingBuffer(LightLoopContext context, uint featureFlags, |
|
|
|
PositionInputs posInput, Ray ray) |
|
|
|
{ |
|
|
|
float z0 = _VBufferDepthEncodingParams.x; // Start integration from the near plane |
|
|
|
float t0 = ray.ratioLenToZ * z0; |
|
|
|
float de = rcp(VBUFFER_SLICE_COUNT); // Log-encoded distance between slices |
|
|
|
|
|
|
|
float3 totalRadiance = 0; |
|
|
|
float opticalDepth = 0; |
|
|
|
|
|
|
|
uint sliceCountHack = max(VBUFFER_SLICE_COUNT, (uint)_VBufferDepthEncodingParams.x); // Prevent unrolling... |
|
|
|
|
|
|
|
#ifdef LIGHTLOOP_TILE_PASS |
|
|
|
// Our voxel is not necessarily completely inside a single light cluster. |
|
|
|
// Note that Z-binning can solve this problem, as we can iterate over all Z-bins |
|
|
|
// to compute min/max light indices, and then use this range for the entire slice. |
|
|
|
uint clusterIndices[2]; |
|
|
|
float clusterDepths[2]; |
|
|
|
clusterIndices[0] = GetLightClusterIndex(posInput.tileCoord, z0); |
|
|
|
clusterDepths[0] = GetLightClusterMinLinearDepth(posInput.tileCoord, clusterIndices[0]); |
|
|
|
#endif // LIGHTLOOP_TILE_PASS |
|
|
|
|
|
|
|
// TODO: replace 'sliceCountHack' with VBUFFER_SLICE_COUNT when the shader compiler bug is fixed. |
|
|
|
for (uint slice = 0; slice < sliceCountHack; slice++) |
|
|
|
{ |
|
|
|
float e1 = slice * de + de; // (slice + 1) / sliceCount |
|
|
|
float z1 = DecodeLogarithmicDepth(e1, _VBufferDepthEncodingParams); |
|
|
|
float t1 = ray.ratioLenToZ * z1; |
|
|
|
float dt = t1 - t0; |
|
|
|
|
|
|
|
#ifdef LIGHTLOOP_TILE_PASS |
|
|
|
clusterIndices[1] = GetLightClusterIndex(posInput.tileCoord, z1); |
|
|
|
clusterDepths[1] = GetLightClusterMinLinearDepth(posInput.tileCoord, clusterIndices[1]); |
|
|
|
#endif |
|
|
|
|
|
|
|
// Compute the position of the center of the voxel. |
|
|
|
// We will use it for participating media sampling and reprojection. |
|
|
|
float tc = t0 + 0.5 * dt; |
|
|
|
float3 centerWS = GetPointAtDistance(ray, tc); |
|
|
|
|
|
|
|
// Sample the participating medium at 'tc' (or 'centerWS'). |
|
|
|
// We consider it to be constant along the interval [t0, t1] (within the voxel). |
|
|
|
float3 scattering = _GlobalFog_Scattering; |
|
|
|
float extinction = _GlobalFog_Extinction; |
|
|
|
|
|
|
|
// TODO: use a low-discrepancy point set. |
|
|
|
float rndVal = 0.5; |
|
|
|
|
|
|
|
float3 voxelRadiance = EvaluateVoxelLighting(context, featureFlags, posInput, |
|
|
|
ray, t0, t1, dt, rndVal, extinction |
|
|
|
#ifdef LIGHTLOOP_TILE_PASS |
|
|
|
, clusterIndices, clusterDepths); |
|
|
|
#else |
|
|
|
); |
|
|
|
#endif |
|
|
|
totalRadiance += (transmittance * IsotropicPhaseFunction()) * scattering * sampleRadiance; |
|
|
|
totalRadiance += (transmittance * IsotropicPhaseFunction()) * scattering * voxelRadiance; |
|
|
|
// Reproject 'centerWS'. It performs a point sample. |
|
|
|
float2 reprojPosNDC = ComputeNormalizedDeviceCoordinates(centerWS, _PrevViewProjMatrix); |
|
|
|
float reprojZ = mul(_PrevViewProjMatrix, float4(centerWS, 1)).w; |
|
|
|
float4 reprojValue = LoadInScatteredRadianceAndTransmittance(reprojPosNDC, reprojZ, |
|
|
|
_VBufferLightingHistory, |
|
|
|
_VBufferResolutionAndScale, |
|
|
|
_VBufferDepthEncodingParams); |
|
|
|
|
|
|
|
_VBufferLightingCurr[uint3(posInput.positionSS, slice)] = blendedValue; |
|
|
|
_VBufferLightingIntegral[uint3(posInput.positionSS, slice)] = blendedValue; |
|
|
|
#ifdef LIGHTLOOP_TILE_PASS |
|
|
|
clusterIndices[0] = clusterIndices[1]; |
|
|
|
clusterDepths[0] = clusterDepths[1]; |
|
|
|
#endif // LIGHTLOOP_TILE_PASS |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|