|
|
|
|
|
|
} |
|
|
|
|
|
|
|
// Computes F(x)/P(x), s.t. x = sqrt(r^2 + t^2). |
|
|
|
float3 ComputeBilateralWeight(float3 S, float r, float t, float rcpDistScale, float rcpPdf) |
|
|
|
float3 ComputeBilateralWeight(float3 S, float r, float t, float rcpPdf) |
|
|
|
// Reducing the integration distance is equivalent to stretching the integration axis. |
|
|
|
float3 val = KernelValCircle(sqrt(r * r + t * t) * rcpDistScale, S); |
|
|
|
float3 val = KernelValCircle(sqrt(r * r + t * t), S); |
|
|
|
// Rescaling of the PDF is handled via 'totalWeight'. |
|
|
|
// Rescaling of the PDF is handled by 'totalWeight'. |
|
|
|
millimPerUnit, scaledPixPerMm, rcpDistScale, totalIrradiance, totalWeight) \ |
|
|
|
millimPerUnit, pixelsPerMm, totalIrradiance, totalWeight) \ |
|
|
|
{ \ |
|
|
|
float r = kernel[profileID][i][0]; \ |
|
|
|
/* The relative sample position is known at compile time. */ \ |
|
|
|
|
|
|
float2 position = centerPosUnSS + vec * scaledPixPerMm; \ |
|
|
|
float2 position = centerPosUnSS + vec * pixelsPerMm; \ |
|
|
|
float3 irradiance = LOAD_TEXTURE2D(_IrradianceSource, position).rgb; \ |
|
|
|
\ |
|
|
|
/* TODO: see if making this a [branch] improves performance. */ \ |
|
|
|
|
|
|
float d = LinearEyeDepth(z, _ZBufferParams); \ |
|
|
|
float t = millimPerUnit * d - (millimPerUnit * centerDepthVS); \ |
|
|
|
float p = kernel[profileID][i][1]; \ |
|
|
|
float3 w = ComputeBilateralWeight(shapeParam, r, t, rcpDistScale, p); \ |
|
|
|
float3 w = ComputeBilateralWeight(shapeParam, r, t, p); \ |
|
|
|
\ |
|
|
|
totalIrradiance += w * irradiance; \ |
|
|
|
totalWeight += w; \ |
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
#define SSS_LOOP(n, kernel, profileID, shapeParam, centerPosUnSS, centerDepthVS, \ |
|
|
|
millimPerUnit, scaledPixPerMm, rcpDistScale, totalIrradiance, totalWeight) \ |
|
|
|
millimPerUnit, pixelsPerMm, totalIrradiance, totalWeight) \ |
|
|
|
{ \ |
|
|
|
float centerRcpPdf = kernel[profileID][0][1]; \ |
|
|
|
float3 centerWeight = KernelValCircle(0, shapeParam) * centerRcpPdf; \ |
|
|
|
|
|
|
for (uint i = 1; i < n; i++) \ |
|
|
|
{ \ |
|
|
|
SSS_ITER(i, n, kernel, profileID, shapeParam, centerPosUnSS, centerDepthVS, \ |
|
|
|
millimPerUnit, scaledPixPerMm, rcpDistScale, totalIrradiance, totalWeight) \ |
|
|
|
millimPerUnit, pixelsPerMm, totalIrradiance, totalWeight) \ |
|
|
|
} \ |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
float3 cornerPosVS = ComputeViewSpacePosition(cornerPosSS, centerDepth, _InvProjMatrix); |
|
|
|
|
|
|
|
// Compute the view-space dimensions of the pixel as a quad projected onto geometry. |
|
|
|
float2 unitsPerPixel = 2 * (cornerPosVS.xy - centerPosVS.xy); |
|
|
|
float2 unitsPerPixel = 2 * abs(cornerPosVS.xy - centerPosVS.xy); |
|
|
|
float metersPerUnit = _WorldScales[profileID]; |
|
|
|
float millimPerUnit = MILLIMETERS_PER_METER * metersPerUnit; |
|
|
|
float2 scaledPixPerMm = distScale * rcp(millimPerUnit * unitsPerPixel); |
|
|
|
// Rescaling the filter is equivalent to inversely scaling the world. |
|
|
|
float metersPerUnit = _WorldScales[profileID] / distScale; |
|
|
|
float millimPerUnit = MILLIMETERS_PER_METER * metersPerUnit; |
|
|
|
float2 pixelsPerMm = rcp(millimPerUnit * unitsPerPixel); |
|
|
|
|
|
|
|
// Take the first (central) sample. |
|
|
|
// TODO: copy its neighborhood into LDS. |
|
|
|
|
|
|
float maxDistInPixels = maxDistance * max(scaledPixPerMm.x, scaledPixPerMm.y); |
|
|
|
float maxDistInPixels = maxDistance * max(pixelsPerMm.x, pixelsPerMm.y); |
|
|
|
|
|
|
|
[branch] |
|
|
|
if (distScale == 0 || maxDistInPixels < 1) |
|
|
|
{ |
|
|
|
|
|
|
#else |
|
|
|
SSS_LOOP(SSS_N_SAMPLES_FAR_FIELD, _FilterKernelsFarField, |
|
|
|
profileID, shapeParam, centerPosition, centerPosVS.z, |
|
|
|
millimPerUnit, scaledPixPerMm, rcp(distScale), |
|
|
|
totalIrradiance, totalWeight) |
|
|
|
millimPerUnit, pixelsPerMm, totalIrradiance, totalWeight) |
|
|
|
#endif |
|
|
|
} |
|
|
|
else |
|
|
|
|
|
|
#else |
|
|
|
SSS_LOOP(SSS_N_SAMPLES_NEAR_FIELD, _FilterKernelsNearField, |
|
|
|
profileID, shapeParam, centerPosition, centerPosVS.z, |
|
|
|
millimPerUnit, scaledPixPerMm, rcp(distScale), |
|
|
|
totalIrradiance, totalWeight) |
|
|
|
millimPerUnit, pixelsPerMm, totalIrradiance, totalWeight) |
|
|
|
float metersPerUnit = _WorldScales[profileID] * SSS_BASIC_DISTANCE_SCALE; |
|
|
|
float centimPerUnit = CENTIMETERS_PER_METER * metersPerUnit; |
|
|
|
float2 scaledPixPerCm = distScale * rcp(centimPerUnit * unitsPerPixel); |
|
|
|
float unitScale = centimPerUnit / distScale; |
|
|
|
// Rescaling the filter is equivalent to inversely scaling the world. |
|
|
|
float metersPerUnit = _WorldScales[profileID] / distScale * SSS_BASIC_DISTANCE_SCALE; |
|
|
|
float centimPerUnit = CENTIMETERS_PER_METER * metersPerUnit; |
|
|
|
float2 pixelsPerCm = rcp(centimPerUnit * unitsPerPixel); |
|
|
|
float scaledStepSize = scaledPixPerCm.x; |
|
|
|
float2 unitDirection = float2(1, 0); |
|
|
|
float2 unitDir = float2(1, 0); |
|
|
|
float scaledStepSize = scaledPixPerCm.y; |
|
|
|
float2 unitDirection = float2(0, 1); |
|
|
|
float2 unitDir = float2(0, 1); |
|
|
|
float2 scaledDirection = scaledPixPerCm * unitDirection; |
|
|
|
float2 scaledDirection = pixelsPerCm * unitDir; |
|
|
|
float phi = 0; // Random rotation; unused for now |
|
|
|
float2x2 rotationMatrix = float2x2(cos(phi), -sin(phi), sin(phi), cos(phi)); |
|
|
|
float2 rotatedDirection = mul(rotationMatrix, scaledDirection); |
|
|
|
|
|
|
// We perform point sampling. Therefore, we can avoid the cost |
|
|
|
// of filtering if we stay within the bounds of the current pixel. |
|
|
|
// We use the value of 1 instead of 0.5 as an optimization. |
|
|
|
float maxDistInPixels = scaledStepSize * maxDistance; |
|
|
|
float maxDistInPixels = maxDistance * max(pixelsPerCm.x, pixelsPerCm.y); |
|
|
|
|
|
|
|
[branch] |
|
|
|
if (distScale == 0 || maxDistInPixels < 1) |
|
|
|
|
|
|
// Ref #2: Separable SSS, Supplementary Materials, Section E. |
|
|
|
float rawDepth = LOAD_TEXTURE2D(_MainDepthTexture, samplePosition).r; |
|
|
|
float sampleDepth = LinearEyeDepth(rawDepth, _ZBufferParams); |
|
|
|
float zDistance = unitScale * sampleDepth - (unitScale * centerPosVS.z); |
|
|
|
float zDistance = centimPerUnit * sampleDepth - (centimPerUnit * centerPosVS.z); |
|
|
|
sampleWeight *= exp(-zDistance * zDistance * halfRcpVariance); |
|
|
|
#endif |
|
|
|
|
|
|
|