You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
572 lines
26 KiB
572 lines
26 KiB
#ifndef VOLUMETRIC_CLOUDS_DENOISING_H
|
|
#define VOLUMETRIC_CLOUDS_DENOISING_H
|
|
|
|
// Need a max to avoid infinitely far away points
|
|
#define MAX_VOLUMETRIC_CLOUDS_DISTANCE 200000.0f
|
|
|
|
// Half resolution volumetric cloud texture
|
|
TEXTURE2D_X(_VolumetricCloudsTexture);
|
|
TEXTURE2D_X(_DepthStatusTexture);
|
|
|
|
// Clouds data
|
|
TEXTURE2D_X(_CloudsLightingTexture);
|
|
TEXTURE2D_X(_CloudsDepthTexture);
|
|
|
|
// Half resolution depth buffer
|
|
TEXTURE2D_X(_HalfResDepthBuffer);
|
|
|
|
// Given that the sky is virtually a skybox, we cannot use the motion vector buffer
|
|
float2 EvaluateCloudMotionVectors(float2 fullResCoord, float deviceDepth, float positionFlag)
|
|
{
|
|
float3 V = GetCloudViewDirWS(fullResCoord);
|
|
|
|
float depth = min(DecodeInfiniteDepth(deviceDepth, _CloudNearPlane), MAX_VOLUMETRIC_CLOUDS_DISTANCE);
|
|
float4 worldPos = float4(V * depth, positionFlag);
|
|
float4 prevPos = worldPos;
|
|
|
|
float4 curClipPos = mul(UNITY_MATRIX_UNJITTERED_VP, worldPos);
|
|
float4 prevClipPos = mul(_CameraPrevViewProjection[unity_StereoEyeIndex], prevPos);
|
|
|
|
float2 previousPositionCS = prevClipPos.xy / prevClipPos.w;
|
|
float2 positionCS = curClipPos.xy / curClipPos.w;
|
|
|
|
// Convert from Clip space (-1..1) to NDC 0..1 space
|
|
float2 velocity = (positionCS - previousPositionCS) * 0.5;
|
|
#if UNITY_UV_STARTS_AT_TOP
|
|
velocity.y = -velocity.y;
|
|
#endif
|
|
return velocity;
|
|
}
|
|
|
|
// Our dispatch is a 8x8 tile. We can access up to 3x3 values at dispatch's half resolution
|
|
// around the center pixel which represents a total of 36 uniques values for the tile.
|
|
groupshared float gs_cacheR[36];
|
|
groupshared float gs_cacheG[36];
|
|
groupshared float gs_cacheB[36];
|
|
groupshared float gs_cacheA[36];
|
|
groupshared float gs_cacheDP[36];
|
|
groupshared float gs_cacheDC[36];
|
|
groupshared float gs_cachePS[36];
|
|
|
|
void FillCloudReprojectionLDS(uint groupIndex, uint2 groupOrigin)
|
|
{
|
|
// Define which value we will be acessing with this worker thread
|
|
int acessCoordX = groupIndex % 6;
|
|
int acessCoordY = groupIndex / 6;
|
|
|
|
// Everything we are accessing is in trace res (quarter rez).
|
|
uint2 traceGroupOrigin = groupOrigin / 2;
|
|
|
|
// The initial position of the access
|
|
int2 originXY = traceGroupOrigin - int2(1, 1);
|
|
|
|
// Compute the sample position
|
|
int2 sampleCoord = int2(clamp(originXY.x + acessCoordX, 0, _TraceScreenSize.x - 1), clamp(originXY.y + acessCoordY, 0, _TraceScreenSize.y - 1));
|
|
|
|
// The representative coordinate to use depends if we are using the checkerboard integration pattern (or not)
|
|
int checkerBoardIndex = ComputeCheckerBoardIndex(sampleCoord, _SubPixelIndex);
|
|
int2 representativeCoord = sampleCoord * 2 + (_EnableIntegration ? (int2)HalfResolutionIndexToOffset(checkerBoardIndex) : int2(0, 0));
|
|
|
|
// Read the sample values
|
|
float sampleDP = LOAD_TEXTURE2D_X(_HalfResDepthBuffer, representativeCoord).x;
|
|
float4 sampleVal = LOAD_TEXTURE2D_X(_CloudsLightingTexture, sampleCoord);
|
|
float sampleDC = LOAD_TEXTURE2D_X(_CloudsDepthTexture, sampleCoord).x;
|
|
|
|
// Store into the LDS
|
|
gs_cacheR[groupIndex] = sampleVal.r;
|
|
gs_cacheG[groupIndex] = sampleVal.g;
|
|
gs_cacheB[groupIndex] = sampleVal.b;
|
|
gs_cacheA[groupIndex] = sampleVal.a;
|
|
gs_cacheDP[groupIndex] = sampleDP;
|
|
gs_cacheDC[groupIndex] = sampleDC;
|
|
}
|
|
|
|
uint OffsetToLDSAdress(uint2 groupThreadId, int2 offset)
|
|
{
|
|
// Compute the tap coordinate in the 6x6 grid
|
|
uint2 tapAddress = (uint2)((int2)(groupThreadId / 2 + 1) + offset);
|
|
return clamp((uint)(tapAddress.x) % 6 + tapAddress.y * 6, 0, 35);
|
|
}
|
|
|
|
float GetCloudDepth_LDS(uint2 groupThreadId, int2 offset)
|
|
{
|
|
return gs_cacheDC[OffsetToLDSAdress(groupThreadId, offset)];
|
|
}
|
|
|
|
float4 GetCloudLighting_LDS(uint2 groupThreadId, int2 offset)
|
|
{
|
|
uint ldsTapAddress = OffsetToLDSAdress(groupThreadId, offset);
|
|
return float4(gs_cacheR[ldsTapAddress], gs_cacheG[ldsTapAddress], gs_cacheB[ldsTapAddress], gs_cacheA[ldsTapAddress]);
|
|
}
|
|
|
|
struct CloudReprojectionData
|
|
{
|
|
float4 cloudLighting;
|
|
float pixelDepth;
|
|
float cloudDepth;
|
|
};
|
|
|
|
CloudReprojectionData GetCloudReprojectionDataSample(uint index)
|
|
{
|
|
CloudReprojectionData outVal;
|
|
outVal.cloudLighting.r = gs_cacheR[index];
|
|
outVal.cloudLighting.g = gs_cacheG[index];
|
|
outVal.cloudLighting.b = gs_cacheB[index];
|
|
outVal.cloudLighting.a = gs_cacheA[index];
|
|
outVal.pixelDepth = gs_cacheDP[index];
|
|
outVal.cloudDepth = gs_cacheDC[index];
|
|
return outVal;
|
|
}
|
|
|
|
CloudReprojectionData GetCloudReprojectionDataSample(uint2 groupThreadId, int2 offset)
|
|
{
|
|
return GetCloudReprojectionDataSample(OffsetToLDSAdress(groupThreadId, offset));
|
|
}
|
|
|
|
float4 ClipCloudsToRegion(float4 history, float4 minimum, float4 maximum, inout float validityFactor)
|
|
{
|
|
// The transmittance is overriden using a clamp
|
|
float clampedTransmittance = clamp(history.w, minimum.w, maximum.w);
|
|
|
|
// The lighting is overriden using a clip
|
|
float3 center = 0.5 * (maximum.xyz + minimum.xyz);
|
|
float3 extents = 0.5 * (maximum.xyz - minimum.xyz);
|
|
|
|
// This is actually `distance`, however the keyword is reserved
|
|
float3 offset = history.xyz - center;
|
|
float3 v_unit = offset.xyz / extents.xyz;
|
|
float3 absUnit = abs(v_unit);
|
|
float maxUnit = Max3(absUnit.x, absUnit.y, absUnit.z);
|
|
|
|
// We make the history less valid if we had to clip it
|
|
validityFactor *= maxUnit > 1.0 ? 0.5 : 1.0;
|
|
|
|
if (maxUnit > 1.0)
|
|
return float4(center + (offset / maxUnit), clampedTransmittance);
|
|
else
|
|
return float4(history.xyz, clampedTransmittance);
|
|
}
|
|
|
|
// Function that fills the struct as we cannot use arrays
|
|
void FillCloudReprojectionNeighborhoodData_NOLDS(int2 traceCoord, int subRegionIdx, out NeighborhoodUpsampleData3x3 neighborhoodData)
|
|
{
|
|
// Fill the sample data
|
|
neighborhoodData.lowValue0 = LOAD_TEXTURE2D_X(_CloudsLightingTexture, traceCoord + int2(-1, -1));
|
|
neighborhoodData.lowValue1 = LOAD_TEXTURE2D_X(_CloudsLightingTexture, traceCoord + int2(0, -1));
|
|
neighborhoodData.lowValue2 = LOAD_TEXTURE2D_X(_CloudsLightingTexture, traceCoord + int2(1, -1));
|
|
|
|
neighborhoodData.lowValue3 = LOAD_TEXTURE2D_X(_CloudsLightingTexture, traceCoord + int2(-1, 0));
|
|
neighborhoodData.lowValue4 = LOAD_TEXTURE2D_X(_CloudsLightingTexture, traceCoord + int2(0, 0));
|
|
neighborhoodData.lowValue5 = LOAD_TEXTURE2D_X(_CloudsLightingTexture, traceCoord + int2(1, 0));
|
|
|
|
neighborhoodData.lowValue6 = LOAD_TEXTURE2D_X(_CloudsLightingTexture, traceCoord + int2(-1, 1));
|
|
neighborhoodData.lowValue7 = LOAD_TEXTURE2D_X(_CloudsLightingTexture, traceCoord + int2(0, 1));
|
|
neighborhoodData.lowValue8 = LOAD_TEXTURE2D_X(_CloudsLightingTexture, traceCoord + int2(1, 1));
|
|
|
|
int2 traceTapCoord = traceCoord + int2(-1, -1);
|
|
int checkerBoardIndex = ComputeCheckerBoardIndex(traceTapCoord, _SubPixelIndex);
|
|
int2 representativeCoord = traceTapCoord * 2 + HalfResolutionIndexToOffset(checkerBoardIndex);
|
|
neighborhoodData.lowDepthA.x = LOAD_TEXTURE2D_X(_HalfResDepthBuffer, representativeCoord).x;
|
|
neighborhoodData.lowWeightA.x = _DistanceBasedWeights[subRegionIdx * 3 + 0].x;
|
|
|
|
traceTapCoord = traceCoord + int2(0, -1);
|
|
checkerBoardIndex = ComputeCheckerBoardIndex(traceTapCoord, _SubPixelIndex);
|
|
representativeCoord = traceTapCoord * 2 + HalfResolutionIndexToOffset(checkerBoardIndex);
|
|
neighborhoodData.lowDepthA.y = LOAD_TEXTURE2D_X(_HalfResDepthBuffer, representativeCoord).x;
|
|
neighborhoodData.lowWeightA.y = _DistanceBasedWeights[subRegionIdx * 3 + 0].y;
|
|
|
|
traceTapCoord = traceCoord + int2(1, -1);
|
|
checkerBoardIndex = ComputeCheckerBoardIndex(traceTapCoord, _SubPixelIndex);
|
|
representativeCoord = traceTapCoord * 2 + HalfResolutionIndexToOffset(checkerBoardIndex);
|
|
neighborhoodData.lowDepthA.z = LOAD_TEXTURE2D_X(_HalfResDepthBuffer, representativeCoord).x;
|
|
neighborhoodData.lowWeightA.z = _DistanceBasedWeights[subRegionIdx * 3 + 0].z;
|
|
|
|
traceTapCoord = traceCoord + int2(-1, 0);
|
|
checkerBoardIndex = ComputeCheckerBoardIndex(traceTapCoord, _SubPixelIndex);
|
|
representativeCoord = traceTapCoord * 2 + HalfResolutionIndexToOffset(checkerBoardIndex);
|
|
neighborhoodData.lowDepthA.w = LOAD_TEXTURE2D_X(_HalfResDepthBuffer, representativeCoord).x;
|
|
neighborhoodData.lowWeightA.w = _DistanceBasedWeights[subRegionIdx * 3 + 0].w;
|
|
|
|
traceTapCoord = traceCoord + int2(0, 0);
|
|
checkerBoardIndex = ComputeCheckerBoardIndex(traceTapCoord, _SubPixelIndex);
|
|
representativeCoord = traceTapCoord * 2 + HalfResolutionIndexToOffset(checkerBoardIndex);
|
|
neighborhoodData.lowDepthB.x = LOAD_TEXTURE2D_X(_HalfResDepthBuffer, representativeCoord).x;
|
|
neighborhoodData.lowWeightB.x = _DistanceBasedWeights[subRegionIdx * 3 + 1].x;
|
|
|
|
traceTapCoord = traceCoord + int2(1, 0);
|
|
checkerBoardIndex = ComputeCheckerBoardIndex(traceTapCoord, _SubPixelIndex);
|
|
representativeCoord = traceTapCoord * 2 + HalfResolutionIndexToOffset(checkerBoardIndex);
|
|
neighborhoodData.lowDepthB.y = LOAD_TEXTURE2D_X(_HalfResDepthBuffer, representativeCoord).x;
|
|
neighborhoodData.lowWeightB.y = _DistanceBasedWeights[subRegionIdx * 3 + 1].y;
|
|
|
|
traceTapCoord = traceCoord + int2(-1, 1);
|
|
checkerBoardIndex = ComputeCheckerBoardIndex(traceTapCoord, _SubPixelIndex);
|
|
representativeCoord = traceTapCoord * 2 + HalfResolutionIndexToOffset(checkerBoardIndex);
|
|
neighborhoodData.lowDepthB.z = LOAD_TEXTURE2D_X(_HalfResDepthBuffer, representativeCoord).x;
|
|
neighborhoodData.lowWeightB.z = _DistanceBasedWeights[subRegionIdx * 3 + 1].z;
|
|
|
|
traceTapCoord = traceCoord + int2(0, 1);
|
|
checkerBoardIndex = ComputeCheckerBoardIndex(traceTapCoord, _SubPixelIndex);
|
|
representativeCoord = traceTapCoord * 2 + HalfResolutionIndexToOffset(checkerBoardIndex);
|
|
neighborhoodData.lowDepthB.w = LOAD_TEXTURE2D_X(_HalfResDepthBuffer, representativeCoord).x;
|
|
neighborhoodData.lowWeightB.w = _DistanceBasedWeights[subRegionIdx * 3 + 1].w;
|
|
|
|
traceTapCoord = traceCoord + int2(1, 1);
|
|
checkerBoardIndex = ComputeCheckerBoardIndex(traceTapCoord, _SubPixelIndex);
|
|
representativeCoord = traceTapCoord * 2 + HalfResolutionIndexToOffset(checkerBoardIndex);
|
|
neighborhoodData.lowDepthC = LOAD_TEXTURE2D_X(_HalfResDepthBuffer, representativeCoord).x;
|
|
neighborhoodData.lowWeightC = _DistanceBasedWeights[subRegionIdx * 3 + 2].x;
|
|
|
|
// In the reprojection case, all masks are valid
|
|
neighborhoodData.lowMasksA = 1.0f;
|
|
neighborhoodData.lowMasksB = 1.0f;
|
|
neighborhoodData.lowMasksC = 1.0f;
|
|
}
|
|
|
|
// Function that fills the struct as we cannot use arrays
|
|
void FillCloudReprojectionNeighborhoodData(int2 groupThreadId, int subRegionIdx, out NeighborhoodUpsampleData3x3 neighborhoodData)
|
|
{
|
|
// Fill the sample data
|
|
CloudReprojectionData data = GetCloudReprojectionDataSample(groupThreadId, int2(-1, -1));
|
|
neighborhoodData.lowValue0 = data.cloudLighting;
|
|
neighborhoodData.lowDepthA.x = data.pixelDepth;
|
|
neighborhoodData.lowWeightA.x = _DistanceBasedWeights[subRegionIdx * 3 + 0].x;
|
|
|
|
data = GetCloudReprojectionDataSample(groupThreadId, int2(0, -1));
|
|
neighborhoodData.lowValue1 = data.cloudLighting;
|
|
neighborhoodData.lowDepthA.y = data.pixelDepth;
|
|
neighborhoodData.lowWeightA.y = _DistanceBasedWeights[subRegionIdx * 3 + 0].y;
|
|
|
|
data = GetCloudReprojectionDataSample(groupThreadId, int2(1, -1));
|
|
neighborhoodData.lowValue2 = data.cloudLighting;
|
|
neighborhoodData.lowDepthA.z = data.pixelDepth;
|
|
neighborhoodData.lowWeightA.z = _DistanceBasedWeights[subRegionIdx * 3 + 0].z;
|
|
|
|
data = GetCloudReprojectionDataSample(groupThreadId, int2(-1, 0));
|
|
neighborhoodData.lowValue3 = data.cloudLighting;
|
|
neighborhoodData.lowDepthA.w = data.pixelDepth;
|
|
neighborhoodData.lowWeightA.w = _DistanceBasedWeights[subRegionIdx * 3 + 0].w;
|
|
|
|
data = GetCloudReprojectionDataSample(groupThreadId, int2(0, 0));
|
|
neighborhoodData.lowValue4 = data.cloudLighting;
|
|
neighborhoodData.lowDepthB.x = data.pixelDepth;
|
|
neighborhoodData.lowWeightB.x = _DistanceBasedWeights[subRegionIdx * 3 + 1].x;
|
|
|
|
data = GetCloudReprojectionDataSample(groupThreadId, int2(1, 0));
|
|
neighborhoodData.lowValue5 = data.cloudLighting;
|
|
neighborhoodData.lowDepthB.y = data.pixelDepth;
|
|
neighborhoodData.lowWeightB.y = _DistanceBasedWeights[subRegionIdx * 3 + 1].y;
|
|
|
|
data = GetCloudReprojectionDataSample(groupThreadId, int2(-1, 1));
|
|
neighborhoodData.lowValue6 = data.cloudLighting;
|
|
neighborhoodData.lowDepthB.z = data.pixelDepth;
|
|
neighborhoodData.lowWeightB.z = _DistanceBasedWeights[subRegionIdx * 3 + 1].z;
|
|
|
|
data = GetCloudReprojectionDataSample(groupThreadId, int2(0, 1));
|
|
neighborhoodData.lowValue7 = data.cloudLighting;
|
|
neighborhoodData.lowDepthB.w = data.pixelDepth;
|
|
neighborhoodData.lowWeightB.w = _DistanceBasedWeights[subRegionIdx * 3 + 1].w;
|
|
|
|
data = GetCloudReprojectionDataSample(groupThreadId, int2(1, 1));
|
|
neighborhoodData.lowValue8 = data.cloudLighting;
|
|
neighborhoodData.lowDepthC = data.pixelDepth;
|
|
neighborhoodData.lowWeightC = _DistanceBasedWeights[subRegionIdx * 3 + 2].x;
|
|
|
|
// In the reprojection case, all masks are valid
|
|
neighborhoodData.lowMasksA = 1.0f;
|
|
neighborhoodData.lowMasksB = 1.0f;
|
|
neighborhoodData.lowMasksC = 1.0f;
|
|
}
|
|
|
|
struct CloudUpscaleData
|
|
{
|
|
float4 cloudLighting;
|
|
float pixelDepth;
|
|
float pixelStatus;
|
|
float cloudDepth;
|
|
};
|
|
|
|
CloudUpscaleData GetCloudUpscaleDataSample(uint index)
|
|
{
|
|
CloudUpscaleData outVal;
|
|
outVal.cloudLighting.r = gs_cacheR[index];
|
|
outVal.cloudLighting.g = gs_cacheG[index];
|
|
outVal.cloudLighting.b = gs_cacheB[index];
|
|
outVal.cloudLighting.a = gs_cacheA[index];
|
|
outVal.pixelDepth = gs_cacheDP[index];
|
|
outVal.pixelStatus = gs_cachePS[index];
|
|
outVal.cloudDepth = gs_cacheDC[index];
|
|
return outVal;
|
|
}
|
|
|
|
CloudUpscaleData GetCloudUpscaleDataSample(uint2 groupThreadId, int2 offset)
|
|
{
|
|
return GetCloudUpscaleDataSample(OffsetToLDSAdress(groupThreadId, offset));
|
|
}
|
|
|
|
// Function that fills the struct as we cannot use arrays
|
|
void FillCloudUpscaleNeighborhoodData(int2 groupThreadId, int subRegionIdx, out NeighborhoodUpsampleData3x3 neighborhoodData)
|
|
{
|
|
// Fill the sample data
|
|
CloudUpscaleData data = GetCloudUpscaleDataSample(groupThreadId, int2(-1, -1));
|
|
neighborhoodData.lowValue0 = data.cloudLighting;
|
|
neighborhoodData.lowDepthA.x = data.pixelDepth;
|
|
neighborhoodData.lowMasksA.x = data.pixelStatus;
|
|
neighborhoodData.lowWeightA.x = _DistanceBasedWeights[subRegionIdx * 3 + 0].x;
|
|
|
|
data = GetCloudUpscaleDataSample(groupThreadId, int2(0, -1));
|
|
neighborhoodData.lowValue1 = data.cloudLighting;
|
|
neighborhoodData.lowDepthA.y = data.pixelDepth;
|
|
neighborhoodData.lowMasksA.y = data.pixelStatus;
|
|
neighborhoodData.lowWeightA.y = _DistanceBasedWeights[subRegionIdx * 3 + 0].y;
|
|
|
|
data = GetCloudUpscaleDataSample(groupThreadId, int2(1, -1));
|
|
neighborhoodData.lowValue2 = data.cloudLighting;
|
|
neighborhoodData.lowDepthA.z = data.pixelDepth;
|
|
neighborhoodData.lowMasksA.z = data.pixelStatus;
|
|
neighborhoodData.lowWeightA.z = _DistanceBasedWeights[subRegionIdx * 3 + 0].z;
|
|
|
|
data = GetCloudUpscaleDataSample(groupThreadId, int2(-1, 0));
|
|
neighborhoodData.lowValue3 = data.cloudLighting;
|
|
neighborhoodData.lowDepthA.w = data.pixelDepth;
|
|
neighborhoodData.lowMasksA.w = data.pixelStatus;
|
|
neighborhoodData.lowWeightA.w = _DistanceBasedWeights[subRegionIdx * 3 + 0].w;
|
|
|
|
data = GetCloudUpscaleDataSample(groupThreadId, int2(0, 0));
|
|
neighborhoodData.lowValue4 = data.cloudLighting;
|
|
neighborhoodData.lowDepthB.x = data.pixelDepth;
|
|
neighborhoodData.lowMasksB.x = data.pixelStatus;
|
|
neighborhoodData.lowWeightB.x = _DistanceBasedWeights[subRegionIdx * 3 + 1].x;
|
|
|
|
data = GetCloudUpscaleDataSample(groupThreadId, int2(1, 0));
|
|
neighborhoodData.lowValue5 = data.cloudLighting;
|
|
neighborhoodData.lowDepthB.y = data.pixelDepth;
|
|
neighborhoodData.lowMasksB.y = data.pixelStatus;
|
|
neighborhoodData.lowWeightB.y = _DistanceBasedWeights[subRegionIdx * 3 + 1].y;
|
|
|
|
data = GetCloudUpscaleDataSample(groupThreadId, int2(-1, 1));
|
|
neighborhoodData.lowValue6 = data.cloudLighting;
|
|
neighborhoodData.lowDepthB.z = data.pixelDepth;
|
|
neighborhoodData.lowMasksB.z = data.pixelStatus;
|
|
neighborhoodData.lowWeightB.z = _DistanceBasedWeights[subRegionIdx * 3 + 1].z;
|
|
|
|
data = GetCloudUpscaleDataSample(groupThreadId, int2(0, 1));
|
|
neighborhoodData.lowValue7 = data.cloudLighting;
|
|
neighborhoodData.lowDepthB.w = data.pixelDepth;
|
|
neighborhoodData.lowMasksB.w = data.pixelStatus;
|
|
neighborhoodData.lowWeightB.w = _DistanceBasedWeights[subRegionIdx * 3 + 1].w;
|
|
|
|
data = GetCloudUpscaleDataSample(groupThreadId, int2(1, 1));
|
|
neighborhoodData.lowValue8 = data.cloudLighting;
|
|
neighborhoodData.lowDepthC = data.pixelDepth;
|
|
neighborhoodData.lowMasksC = data.pixelStatus;
|
|
neighborhoodData.lowWeightC = _DistanceBasedWeights[subRegionIdx * 3 + 2].x;
|
|
}
|
|
|
|
// Function that fills the struct as we cannot use arrays
|
|
void FillCloudUpscaleNeighborhoodData_NOLDS(int2 traceCoord, int subRegionIdx, out NeighborhoodUpsampleData3x3 neighborhoodData)
|
|
{
|
|
// Fill the sample data (TOP LEFT)
|
|
float4 lightingVal = LOAD_TEXTURE2D_X(_VolumetricCloudsTexture, traceCoord + int2(-1, -1));
|
|
float3 depthStatusValue = LOAD_TEXTURE2D_X(_DepthStatusTexture, traceCoord + int2(-1, -1)).xyz;
|
|
neighborhoodData.lowValue0 = lightingVal;
|
|
neighborhoodData.lowDepthA.x = depthStatusValue.y;
|
|
neighborhoodData.lowMasksA.x = saturate(depthStatusValue.x);
|
|
neighborhoodData.lowWeightA.x = _DistanceBasedWeights[subRegionIdx * 3 + 0].x;
|
|
|
|
// Fill the sample data (TOP CENTER)
|
|
lightingVal = LOAD_TEXTURE2D_X(_VolumetricCloudsTexture, traceCoord + int2(0, -1));
|
|
depthStatusValue = LOAD_TEXTURE2D_X(_DepthStatusTexture, traceCoord + int2(0, -1)).xyz;
|
|
neighborhoodData.lowValue1 = lightingVal;
|
|
neighborhoodData.lowDepthA.y = depthStatusValue.y;
|
|
neighborhoodData.lowMasksA.y = saturate(depthStatusValue.x);
|
|
neighborhoodData.lowWeightA.y = _DistanceBasedWeights[subRegionIdx * 3 + 0].y;
|
|
|
|
// Fill the sample data (TOP RIGHT)
|
|
lightingVal = LOAD_TEXTURE2D_X(_VolumetricCloudsTexture, traceCoord + int2(1, -1));
|
|
depthStatusValue = LOAD_TEXTURE2D_X(_DepthStatusTexture, traceCoord + int2(1, -1)).xyz;
|
|
neighborhoodData.lowValue2 = lightingVal;
|
|
neighborhoodData.lowDepthA.z = depthStatusValue.y;
|
|
neighborhoodData.lowMasksA.z = saturate(depthStatusValue.x);
|
|
neighborhoodData.lowWeightA.z = depthStatusValue.z;
|
|
neighborhoodData.lowWeightA.z = _DistanceBasedWeights[subRegionIdx * 3 + 0].z;
|
|
|
|
// Fill the sample data (MID LEFT)
|
|
lightingVal = LOAD_TEXTURE2D_X(_VolumetricCloudsTexture, traceCoord + int2(-1, 0));
|
|
depthStatusValue = LOAD_TEXTURE2D_X(_DepthStatusTexture, traceCoord + int2(-1, 0)).xyz;
|
|
neighborhoodData.lowValue3 = lightingVal;
|
|
neighborhoodData.lowDepthA.w = depthStatusValue.y;
|
|
neighborhoodData.lowMasksA.w = saturate(depthStatusValue.x);
|
|
neighborhoodData.lowWeightA.w = _DistanceBasedWeights[subRegionIdx * 3 + 0].w;
|
|
|
|
// Fill the sample data (MID CENTER)
|
|
lightingVal = LOAD_TEXTURE2D_X(_VolumetricCloudsTexture, traceCoord + int2(0, 0));
|
|
depthStatusValue = LOAD_TEXTURE2D_X(_DepthStatusTexture, traceCoord + int2(0, 0)).xyz;
|
|
neighborhoodData.lowValue4 = lightingVal;
|
|
neighborhoodData.lowDepthB.x = depthStatusValue.y;
|
|
neighborhoodData.lowMasksB.x = saturate(depthStatusValue.x);
|
|
neighborhoodData.lowWeightB.x = _DistanceBasedWeights[subRegionIdx * 3 + 1].x;
|
|
|
|
// Fill the sample data (MID RIGHT)
|
|
lightingVal = LOAD_TEXTURE2D_X(_VolumetricCloudsTexture, traceCoord + int2(1, 0));
|
|
depthStatusValue = LOAD_TEXTURE2D_X(_DepthStatusTexture, traceCoord + int2(1, 0)).xyz;
|
|
neighborhoodData.lowValue5 = lightingVal;
|
|
neighborhoodData.lowDepthB.y = depthStatusValue.y;
|
|
neighborhoodData.lowMasksB.y = saturate(depthStatusValue.x);
|
|
neighborhoodData.lowWeightB.y = _DistanceBasedWeights[subRegionIdx * 3 + 1].y;
|
|
|
|
// Fill the sample data (BOTTOM LEFT)
|
|
lightingVal = LOAD_TEXTURE2D_X(_VolumetricCloudsTexture, traceCoord + int2(-1, 1));
|
|
depthStatusValue = LOAD_TEXTURE2D_X(_DepthStatusTexture, traceCoord + int2(-1, 1)).xyz;
|
|
neighborhoodData.lowValue6 = lightingVal;
|
|
neighborhoodData.lowDepthB.z = depthStatusValue.y;
|
|
neighborhoodData.lowMasksB.z = saturate(depthStatusValue.x);
|
|
neighborhoodData.lowWeightB.z = depthStatusValue.z;
|
|
neighborhoodData.lowWeightB.z = _DistanceBasedWeights[subRegionIdx * 3 + 1].z;
|
|
|
|
// Fill the sample data (BOTTOM CENTER)
|
|
lightingVal = LOAD_TEXTURE2D_X(_VolumetricCloudsTexture, traceCoord + int2(0, 1));
|
|
depthStatusValue = LOAD_TEXTURE2D_X(_DepthStatusTexture, traceCoord + int2(0, 1)).xyz;
|
|
neighborhoodData.lowValue7 = lightingVal;
|
|
neighborhoodData.lowDepthB.w = depthStatusValue.y;
|
|
neighborhoodData.lowMasksB.w = saturate(depthStatusValue.x);
|
|
neighborhoodData.lowWeightB.w = depthStatusValue.z;
|
|
neighborhoodData.lowWeightB.w = _DistanceBasedWeights[subRegionIdx * 3 + 1].w;
|
|
|
|
// Fill the sample data (BOTTOM CENTER)
|
|
lightingVal = LOAD_TEXTURE2D_X(_VolumetricCloudsTexture, traceCoord + int2(1, 1));
|
|
depthStatusValue = LOAD_TEXTURE2D_X(_DepthStatusTexture, traceCoord + int2(1, 1)).xyz;
|
|
neighborhoodData.lowValue8 = lightingVal;
|
|
neighborhoodData.lowDepthC = depthStatusValue.y;
|
|
neighborhoodData.lowMasksC = saturate(depthStatusValue.x);
|
|
neighborhoodData.lowWeightC = _DistanceBasedWeights[subRegionIdx * 3 + 2].x;
|
|
}
|
|
|
|
float EvaluateUpscaledCloudDepth(int2 groupThreadId, NeighborhoodUpsampleData3x3 nhd)
|
|
{
|
|
// There are some cases where we need to provide a depth value for the volumetric clouds (mainly for the fog now, but this may change in the future)
|
|
// Given that the cloud value for a final pixel doesn't come from a single half resolution pixel (it is interpolated), we also need to interpolate
|
|
// the depth. That said, we cannot interpolate cloud values with non-cloudy pixels values and we need to exclude them from the evaluation
|
|
// Also, we should be doing the interpolation in linear space to be accurate, but it becomes more expensive and experimentally I didn't find
|
|
// any artifact of doing it in logarithmic space.
|
|
float finalDepth = 0.0f;
|
|
float sumWeight = 0.0f;
|
|
|
|
// Top left
|
|
float weight = (nhd.lowValue0.w != 1.0 ? 1.0 : 0.0) * nhd.lowMasksA.x;
|
|
finalDepth += weight * GetCloudDepth_LDS(groupThreadId, int2(-1, -1));
|
|
sumWeight += weight;
|
|
|
|
// Top center
|
|
weight = (nhd.lowValue1.w != 1.0 ? 1.0 : 0.0) * nhd.lowMasksA.y;
|
|
finalDepth += weight * GetCloudDepth_LDS(groupThreadId, int2(0, -1));
|
|
sumWeight += weight;
|
|
|
|
// Top right
|
|
weight = (nhd.lowValue2.w != 1.0 ? 1.0 : 0.0) * nhd.lowMasksA.z;
|
|
finalDepth += weight * GetCloudDepth_LDS(groupThreadId, int2(1, -1));
|
|
sumWeight += weight;
|
|
|
|
// Mid left
|
|
weight = (nhd.lowValue3.w != 1.0 ? 1.0 : 0.0) * nhd.lowMasksA.w;
|
|
finalDepth += weight * GetCloudDepth_LDS(groupThreadId, int2(-1, 0));
|
|
sumWeight += weight;
|
|
|
|
// Mid center
|
|
weight = (nhd.lowValue4.w != 1.0 ? 1.0 : 0.0) * nhd.lowMasksB.x;
|
|
finalDepth += weight * GetCloudDepth_LDS(groupThreadId, int2(0, 0));
|
|
sumWeight += weight;
|
|
|
|
// Mid right
|
|
weight = (nhd.lowValue5.w != 1.0 ? 1.0 : 0.0) * nhd.lowMasksB.y;
|
|
finalDepth += weight * GetCloudDepth_LDS(groupThreadId, int2(1, 0));
|
|
sumWeight += weight;
|
|
|
|
// Bottom left
|
|
weight = (nhd.lowValue6.w != 1.0 ? 1.0 : 0.0) * nhd.lowMasksB.z;
|
|
finalDepth += weight * GetCloudDepth_LDS(groupThreadId, int2(-1, 1));
|
|
sumWeight += weight;
|
|
|
|
// Bottom mid
|
|
weight = (nhd.lowValue7.w != 1.0 ? 1.0 : 0.0) * nhd.lowMasksB.w;
|
|
finalDepth += weight * GetCloudDepth_LDS(groupThreadId, int2(0, 1));
|
|
sumWeight += weight;
|
|
|
|
// Bottom mid
|
|
weight = (nhd.lowValue8.w != 1.0 ? 1.0 : 0.0) * nhd.lowMasksC;
|
|
finalDepth += weight * GetCloudDepth_LDS(groupThreadId, int2(1, 1));
|
|
sumWeight += weight;
|
|
|
|
return finalDepth / sumWeight;
|
|
}
|
|
|
|
float EvaluateUpscaledCloudDepth_NOLDS(int2 halfResCoord, NeighborhoodUpsampleData3x3 nhd)
|
|
{
|
|
float finalDepth = 0.0f;
|
|
float sumWeight = 0.0f;
|
|
|
|
// Top left
|
|
float weight = (nhd.lowValue0.w != 1.0 ? 1.0 : 0.0) * nhd.lowMasksA.x;
|
|
finalDepth += weight * LOAD_TEXTURE2D_X(_DepthStatusTexture, halfResCoord + int2(-1, -1)).z;
|
|
sumWeight += weight;
|
|
|
|
// Top center
|
|
weight = (nhd.lowValue1.w != 1.0 ? 1.0 : 0.0) * nhd.lowMasksA.y;
|
|
finalDepth += weight * LOAD_TEXTURE2D_X(_DepthStatusTexture, halfResCoord + int2(0, -1)).z;
|
|
sumWeight += weight;
|
|
|
|
// Top right
|
|
weight = (nhd.lowValue2.w != 1.0 ? 1.0 : 0.0) * nhd.lowMasksA.z;
|
|
finalDepth += weight * LOAD_TEXTURE2D_X(_DepthStatusTexture, halfResCoord + int2(1, -1)).z;
|
|
sumWeight += weight;
|
|
|
|
// Mid left
|
|
weight = (nhd.lowValue3.w != 1.0 ? 1.0 : 0.0) * nhd.lowMasksA.w;
|
|
finalDepth += weight * LOAD_TEXTURE2D_X(_DepthStatusTexture, halfResCoord + int2(-1, 0)).z;
|
|
sumWeight += weight;
|
|
|
|
// Mid center
|
|
weight = (nhd.lowValue4.w != 1.0 ? 1.0 : 0.0) * nhd.lowMasksB.x;
|
|
finalDepth += weight * LOAD_TEXTURE2D_X(_DepthStatusTexture, halfResCoord + int2(0, 0)).z;
|
|
sumWeight += weight;
|
|
|
|
// Mid right
|
|
weight = (nhd.lowValue5.w != 1.0 ? 1.0 : 0.0) * nhd.lowMasksB.y;
|
|
finalDepth += weight * LOAD_TEXTURE2D_X(_DepthStatusTexture, halfResCoord + int2(1, 0)).z;
|
|
sumWeight += weight;
|
|
|
|
// Bottom left
|
|
weight = (nhd.lowValue6.w != 1.0 ? 1.0 : 0.0) * nhd.lowMasksB.z;
|
|
finalDepth += weight * LOAD_TEXTURE2D_X(_DepthStatusTexture, halfResCoord + int2(-1, 1)).z;
|
|
sumWeight += weight;
|
|
|
|
// Bottom mid
|
|
weight = (nhd.lowValue7.w != 1.0 ? 1.0 : 0.0) * nhd.lowMasksB.w;
|
|
finalDepth += weight * LOAD_TEXTURE2D_X(_DepthStatusTexture, halfResCoord + int2(0, 1)).z;
|
|
sumWeight += weight;
|
|
|
|
// Bottom mid
|
|
weight = (nhd.lowValue8.w != 1.0 ? 1.0 : 0.0) * nhd.lowMasksC;
|
|
finalDepth += weight * LOAD_TEXTURE2D_X(_DepthStatusTexture, halfResCoord + int2(1, 1)).z;
|
|
sumWeight += weight;
|
|
|
|
return sumWeight != 0.0f ? finalDepth / sumWeight : 0.0f;
|
|
}
|
|
|
|
// This function will return something strictly smaller than 0 if any of the lower res pixels
|
|
// have some amound of clouds.
|
|
float EvaluateRegionEmptiness(NeighborhoodUpsampleData3x3 data)
|
|
{
|
|
float emptyRegionFlag = 1.0f;
|
|
emptyRegionFlag *= lerp(1.0, data.lowValue0.w, data.lowWeightA.x != 0.0 ? 1.0 : 0.0);
|
|
emptyRegionFlag *= lerp(1.0, data.lowValue1.w, data.lowWeightA.y != 0.0 ? 1.0 : 0.0);
|
|
emptyRegionFlag *= lerp(1.0, data.lowValue2.w, data.lowWeightA.z != 0.0 ? 1.0 : 0.0);
|
|
emptyRegionFlag *= lerp(1.0, data.lowValue3.w, data.lowWeightA.w != 0.0 ? 1.0 : 0.0);
|
|
emptyRegionFlag *= lerp(1.0, data.lowValue4.w, data.lowWeightB.x != 0.0 ? 1.0 : 0.0);
|
|
emptyRegionFlag *= lerp(1.0, data.lowValue5.w, data.lowWeightB.y != 0.0 ? 1.0 : 0.0);
|
|
emptyRegionFlag *= lerp(1.0, data.lowValue6.w, data.lowWeightB.z != 0.0 ? 1.0 : 0.0);
|
|
emptyRegionFlag *= lerp(1.0, data.lowValue7.w, data.lowWeightB.w != 0.0 ? 1.0 : 0.0);
|
|
emptyRegionFlag *= lerp(1.0, data.lowValue8.w, data.lowWeightC != 0.0 ? 1.0 : 0.0);
|
|
return emptyRegionFlag;
|
|
}
|
|
#endif // VOLUMETRIC_CLOUDS_DENOISING_H
|