|
|
|
|
|
|
#pragma multi_compile _ SSS_FILTER_HORIZONTAL_AND_COMBINE |
|
|
|
// <<< Old SSS Model |
|
|
|
|
|
|
|
// Tweak parameters for the Disney SSS below. |
|
|
|
#define SSS_BILATERAL_FILTER 1 |
|
|
|
#define SSS_USE_TANGENT_PLANE 0 |
|
|
|
#define SSS_CLAMP_COLOR_BLEED 0 |
|
|
|
#define SSS_DEBUG 0 |
|
|
|
|
|
|
|
// Do not modify these. |
|
|
|
#define SSS_BILATERAL 1 |
|
|
|
#define SSS_DEBUG 0 |
|
|
|
#ifdef SSS_MODEL_BASIC |
|
|
|
#define RBG_BILATERAL_WEIGHTS 0 |
|
|
|
#endif |
|
|
|
|
|
|
|
//------------------------------------------------------------------------------------- |
|
|
|
// Include |
|
|
|
|
|
|
//------------------------------------------------------------------------------------- |
|
|
|
// Inputs & outputs |
|
|
|
//------------------------------------------------------------------------------------- |
|
|
|
|
|
|
|
float4x4 _ViewMatrix, _ProjMatrix; // TEMP: make these global |
|
|
|
float _FilterKernelsNearField[SSS_N_PROFILES][SSS_N_SAMPLES_NEAR_FIELD][2]; // 0 = radius, 1 = reciprocal of the PDF |
|
|
|
float _FilterKernelsFarField[SSS_N_PROFILES][SSS_N_SAMPLES_FAR_FIELD][2]; // 0 = radius, 1 = reciprocal of the PDF |
|
|
|
#else |
|
|
|
|
|
|
return /* 0.25 * */ S * (expOneThird + expOneThird * expOneThird * expOneThird); |
|
|
|
} |
|
|
|
|
|
|
|
// Computes F(x)/P(x), s.t. x = sqrt(r^2 + t^2). |
|
|
|
float3 ComputeBilateralWeight(float3 S, float r, float t, float rcpPdf) |
|
|
|
// Computes F(r)/P(r), s.t. r = sqrt(a^2 + b^2). |
|
|
|
// Rescaling of the PDF is handled by 'totalWeight'. |
|
|
|
float3 ComputeBilateralWeight(float a2, float b, float mmPerUnit, float3 S, float rcpPdf) |
|
|
|
#if (SSS_BILATERAL == 0) |
|
|
|
t = 0; |
|
|
|
#if (SSS_BILATERAL_FILTER == 0) |
|
|
|
b = 0; |
|
|
|
#endif |
|
|
|
|
|
|
|
#if SSS_USE_TANGENT_PLANE |
|
|
|
// Both 'a2' and 'b2' require unit conversion. |
|
|
|
float r = sqrt(a2 + b * b) * mmPerUnit; |
|
|
|
#else |
|
|
|
// Only 'b2' requires unit conversion. |
|
|
|
float r = sqrt(a2 + (b * mmPerUnit) * (b * mmPerUnit)); |
|
|
|
float3 val = KernelValCircle(sqrt(r * r + t * t), S); |
|
|
|
// Rescaling of the PDF is handled by 'totalWeight'. |
|
|
|
return val * rcpPdf; |
|
|
|
#if SSS_CLAMP_COLOR_BLEED |
|
|
|
return saturate(KernelValCircle(r, S) * rcpPdf); |
|
|
|
#else |
|
|
|
return KernelValCircle(r, S) * rcpPdf; |
|
|
|
#endif |
|
|
|
#define SSS_ITER(i, n, kernel, profileID, shapeParam, centerPosUnSS, centerDepthVS, \ |
|
|
|
millimPerUnit, pixelsPerMm, totalIrradiance, totalWeight) \ |
|
|
|
#define SSS_ITER(i, n, kernel, profileID, shapeParam, centerPosUnSS, centerPosVS, \ |
|
|
|
useTangentPlane, tangentX, tangentY, mmPerUnit, pixelsPerMm, \ |
|
|
|
totalIrradiance, totalWeight) \ |
|
|
|
{ \ |
|
|
|
float r = kernel[profileID][i][0]; \ |
|
|
|
/* The relative sample position is known at compile time. */ \ |
|
|
|
|
|
|
float2 position = centerPosUnSS + vec * pixelsPerMm; \ |
|
|
|
float3 irradiance = LOAD_TEXTURE2D(_IrradianceSource, position).rgb; \ |
|
|
|
/* Compute the screen-space position and the associated irradiance. */ \ |
|
|
|
float2 position; float3 irradiance; \ |
|
|
|
/* Compute the squared distance (in mm) in the screen-aligned plane. */ \ |
|
|
|
float dXY2; \ |
|
|
|
\ |
|
|
|
if (useTangentPlane) \ |
|
|
|
{ \ |
|
|
|
/* 'vec' is given relative to the tangent frame. */ \ |
|
|
|
float3 relPosVS = vec.x * tangentX + vec.y * tangentY; \ |
|
|
|
float3 positionVS = centerPosVS + relPosVS; \ |
|
|
|
float4 positionCS = mul(_ProjMatrix, float4(positionVS, 1)); \ |
|
|
|
float2 positionSS = positionCS.xy * (rcp(positionCS.w) * 0.5) + 0.5; \ |
|
|
|
\ |
|
|
|
position = positionSS * _ScreenSize.xy; \ |
|
|
|
irradiance = LOAD_TEXTURE2D(_IrradianceSource, position).rgb; \ |
|
|
|
dXY2 = dot(relPosVS.xy, relPosVS.xy); \ |
|
|
|
} \ |
|
|
|
else \ |
|
|
|
{ \ |
|
|
|
/* 'vec' is given directly in screen-space. */ \ |
|
|
|
position = centerPosUnSS + vec * pixelsPerMm; \ |
|
|
|
irradiance = LOAD_TEXTURE2D(_IrradianceSource, position).rgb; \ |
|
|
|
dXY2 = r * r; \ |
|
|
|
} \ |
|
|
|
\ |
|
|
|
/* TODO: see if making this a [branch] improves performance. */ \ |
|
|
|
[flatten] \ |
|
|
|
|
|
|
float z = LOAD_TEXTURE2D(_MainDepthTexture, position).r; \ |
|
|
|
float d = LinearEyeDepth(z, _ZBufferParams); \ |
|
|
|
float t = millimPerUnit * d - (millimPerUnit * centerDepthVS); \ |
|
|
|
float t = d - centerPosVS.z; \ |
|
|
|
float3 w = ComputeBilateralWeight(shapeParam, r, t, p); \ |
|
|
|
float3 w = ComputeBilateralWeight(dXY2, t, mmPerUnit, shapeParam, p); \ |
|
|
|
\ |
|
|
|
totalIrradiance += w * irradiance; \ |
|
|
|
totalWeight += w; \ |
|
|
|
|
|
|
} \ |
|
|
|
} |
|
|
|
|
|
|
|
#define SSS_LOOP(n, kernel, profileID, shapeParam, centerPosUnSS, centerDepthVS, \ |
|
|
|
millimPerUnit, pixelsPerMm, totalIrradiance, totalWeight) \ |
|
|
|
#define SSS_LOOP(n, kernel, profileID, shapeParam, centerPosUnSS, centerPosVS, \ |
|
|
|
useTangentPlane, tangentX, tangentY, mmPerUnit, pixelsPerMm, \ |
|
|
|
totalIrradiance, totalWeight) \ |
|
|
|
{ \ |
|
|
|
float centerRcpPdf = kernel[profileID][0][1]; \ |
|
|
|
float3 centerWeight = KernelValCircle(0, shapeParam) * centerRcpPdf; \ |
|
|
|
|
|
|
\ |
|
|
|
/* Perform integration over the screen-aligned plane in the view space. */ \ |
|
|
|
/* TODO: it would be more accurate to use the tangent plane instead. */ \ |
|
|
|
/* Integrate over the screen-aligned or tangent plane in the view space. */ \ |
|
|
|
SSS_ITER(i, n, kernel, profileID, shapeParam, centerPosUnSS, centerDepthVS, \ |
|
|
|
millimPerUnit, pixelsPerMm, totalIrradiance, totalWeight) \ |
|
|
|
SSS_ITER(i, n, kernel, profileID, shapeParam, centerPosUnSS, centerPosVS, \ |
|
|
|
useTangentPlane, tangentX, tangentY, mmPerUnit, pixelsPerMm, \ |
|
|
|
totalIrradiance, totalWeight) \ |
|
|
|
} \ |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
float maxDistance = _FilterKernelsBasic[profileID][SSS_BASIC_N_SAMPLES - 1].a; |
|
|
|
#endif |
|
|
|
|
|
|
|
// Take the first (central) sample. |
|
|
|
// TODO: copy its neighborhood into LDS. |
|
|
|
float2 centerPosition = posInput.unPositionSS; |
|
|
|
float3 centerIrradiance = LOAD_TEXTURE2D(_IrradianceSource, centerPosition).rgb; |
|
|
|
|
|
|
|
float centerDepth = LOAD_TEXTURE2D(_MainDepthTexture, posInput.unPositionSS).r; |
|
|
|
float centerDepth = LOAD_TEXTURE2D(_MainDepthTexture, centerPosition).r; |
|
|
|
// Compute the view-space dimensions of the pixel as a quad projected onto geometry. |
|
|
|
float2 unitsPerPixel = 2 * abs(cornerPosVS.xy - centerPosVS.xy); |
|
|
|
float metersPerUnit = _WorldScales[profileID] / distScale; |
|
|
|
float millimPerUnit = MILLIMETERS_PER_METER * metersPerUnit; |
|
|
|
float2 pixelsPerMm = rcp(millimPerUnit * unitsPerPixel); |
|
|
|
float mmPerUnit = MILLIMETERS_PER_METER * (_WorldScales[profileID] / distScale); |
|
|
|
float unitsPerMm = rcp(mmPerUnit); |
|
|
|
// Take the first (central) sample. |
|
|
|
// TODO: copy its neighborhood into LDS. |
|
|
|
float2 centerPosition = posInput.unPositionSS; |
|
|
|
float3 centerIrradiance = LOAD_TEXTURE2D(_IrradianceSource, centerPosition).rgb; |
|
|
|
|
|
|
|
// Compute the view-space dimensions of the pixel as a quad projected onto geometry. |
|
|
|
float2 unitsPerPixel = 2 * abs(cornerPosVS.xy - centerPosVS.xy); |
|
|
|
float2 pixelsPerMm = rcp(unitsPerPixel) * unitsPerMm; |
|
|
|
// N.b.: our LoD selection algorithm is the same regardless of |
|
|
|
// whether we integrate over the tangent plane or not, since we |
|
|
|
// don't want the orientation of the tangent plane to create |
|
|
|
// divergence of execution across the warp. |
|
|
|
float maxDistInPixels = maxDistance * max(pixelsPerMm.x, pixelsPerMm.y); |
|
|
|
|
|
|
|
[branch] |
|
|
|
|
|
|
#endif |
|
|
|
} |
|
|
|
|
|
|
|
const bool useTangentPlane = SSS_USE_TANGENT_PLANE != 0; |
|
|
|
|
|
|
|
// Compute the tangent frame in view space. |
|
|
|
float3 normalVS = mul((float3x3)_ViewMatrix, bsdfData.normalWS); |
|
|
|
float3 tangentX = GetLocalFrame(normalVS)[0] * unitsPerMm; |
|
|
|
float3 tangentY = GetLocalFrame(normalVS)[1] * unitsPerMm; |
|
|
|
|
|
|
|
// Accumulate filtered irradiance and bilateral weights (for renormalization). |
|
|
|
float3 totalIrradiance, totalWeight; |
|
|
|
|
|
|
|
|
|
|
return float4(0.5, 0.5, 0, 1); |
|
|
|
#else |
|
|
|
SSS_LOOP(SSS_N_SAMPLES_FAR_FIELD, _FilterKernelsFarField, |
|
|
|
profileID, shapeParam, centerPosition, centerPosVS.z, |
|
|
|
millimPerUnit, pixelsPerMm, totalIrradiance, totalWeight) |
|
|
|
profileID, shapeParam, centerPosition, centerPosVS, |
|
|
|
useTangentPlane, tangentX, tangentY, mmPerUnit, pixelsPerMm, |
|
|
|
totalIrradiance, totalWeight) |
|
|
|
#endif |
|
|
|
} |
|
|
|
else |
|
|
|
|
|
|
#else |
|
|
|
SSS_LOOP(SSS_N_SAMPLES_NEAR_FIELD, _FilterKernelsNearField, |
|
|
|
profileID, shapeParam, centerPosition, centerPosVS.z, |
|
|
|
millimPerUnit, pixelsPerMm, totalIrradiance, totalWeight) |
|
|
|
profileID, shapeParam, centerPosition, centerPosVS, |
|
|
|
useTangentPlane, tangentX, tangentY, mmPerUnit, pixelsPerMm, |
|
|
|
totalIrradiance, totalWeight) |
|
|
|
#endif |
|
|
|
} |
|
|
|
#else |
|
|
|
|
|
|
// Compute the view-space dimensions of the pixel as a quad projected onto geometry. |
|
|
|
float2 unitsPerPixel = 2 * abs(cornerPosVS.xy - centerPosVS.xy); |
|
|
|
float2 pixelsPerCm = rcp(centimPerUnit * unitsPerPixel); |
|
|
|
|
|
|
|
// Compute the filtering direction. |
|
|
|
|
|
|
[flatten] |
|
|
|
if (any(sampleIrradiance)) |
|
|
|
{ |
|
|
|
#if SSS_BILATERAL |
|
|
|
// Apply bilateral weighting. |
|
|
|
// Ref #1: Skin Rendering by Pseudo–Separable Cross Bilateral Filtering. |
|
|
|
// Ref #2: Separable SSS, Supplementary Materials, Section E. |
|
|
|
|
|
|
sampleWeight *= exp(-zDistance * zDistance * halfRcpVariance); |
|
|
|
#endif |
|
|
|
|
|
|
|
totalIrradiance += sampleWeight * sampleIrradiance; |
|
|
|
totalWeight += sampleWeight; |
|
|
|