浏览代码

Optimize tangent plane SSS

/RenderPassXR_Sandbox
Evgenii Golubev 7 年前
当前提交
2cac41ab
共有 1 个文件被更改,包括 76 次插入111 次删除
  1. 187
      Assets/ScriptableRenderPipeline/HDRenderPipeline/Material/Lit/Resources/CombineSubsurfaceScattering.shader

187
Assets/ScriptableRenderPipeline/HDRenderPipeline/Material/Lit/Resources/CombineSubsurfaceScattering.shader


#pragma multi_compile _ SSS_FILTER_HORIZONTAL_AND_COMBINE
// <<< Old SSS Model
#define SSS_PASS 1
#define SSS_BILATERAL_FILTER 1
// Tweak parameters for the Disney SSS below.
#define SSS_BILATERAL_FILTER 1
// Do not modify these.
#define SSS_PASS 1
#ifdef SSS_MODEL_BASIC
#define RBG_BILATERAL_WEIGHTS 0
#endif
//-------------------------------------------------------------------------------------
// Include

// Inputs & outputs
//-------------------------------------------------------------------------------------
float4x4 _ViewMatrix, _ProjMatrix; // TEMP: make these global
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). Rescaling of the PDF is handled by 'totalWeight'.
float3 ComputeBilateralWeight(float r, float3 S, 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_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));
#endif
#if SSS_CLAMP_COLOR_BLEED
return saturate(KernelValCircle(r, S) * rcpPdf);
#else

#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(sqrt(r * r + t * t), shapeParam, 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) \
} \
}

#ifdef SSS_MODEL_DISNEY
// Rescaling the filter is equivalent to inversely scaling the world.
float metersPerUnit = _WorldScales[profileID] / distScale;
float millimPerUnit = MILLIMETERS_PER_METER * metersPerUnit;
#if SSS_USE_TANGENT_PLANE
[branch]
if (distScale == 0)
{
#if SSS_DEBUG
return float4(0, 0, 1, 1);
#else
return float4(bsdfData.diffuseColor * centerIrradiance, 1);
#endif
}
float mmPerUnit = MILLIMETERS_PER_METER * (_WorldScales[profileID] / distScale);
float unitsPerMm = rcp(mmPerUnit);
UpdatePositionInput(centerDepth, _InvViewProjMatrix, _ViewProjMatrix, posInput);
float3 normalVS = mul((float3x3)_ViewMatrix, bsdfData.normalWS);
// Compute the disk tangential to the surface.
float3x3 basisVS = GetLocalFrame(normalVS);
float3 tangentX = basisVS[0] * rcp(millimPerUnit);
float3 tangentY = basisVS[1] * rcp(millimPerUnit);
// Accumulate filtered irradiance and bilateral weights (for renormalization).
float3 totalIrradiance, totalWeight;
{
float centerRcpPdf = _FilterKernelsNearField[profileID][0][1];
float3 centerWeight = KernelValCircle(0, shapeParam) * centerRcpPdf;
totalIrradiance = centerWeight * centerIrradiance;
totalWeight = centerWeight;
[unroll]
for (uint i = 1; i < SSS_N_SAMPLES_NEAR_FIELD; i++)
{
float r = _FilterKernelsNearField[profileID][i][0];
/* The relative sample position is known at compile time. */
float phi = TWO_PI * Fibonacci2d(i, SSS_N_SAMPLES_NEAR_FIELD).y;
float2 vec = r * float2(cos(phi), sin(phi));
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;
float2 positionXY = positionSS * _ScreenSize.xy;
float3 irradiance = LOAD_TEXTURE2D(_IrradianceSource, positionXY).rgb;
/* TODO: see if making this a [branch] improves performance. */
[flatten]
if (any(irradiance))
{
/* Apply bilateral weighting. */
float z = LOAD_TEXTURE2D(_MainDepthTexture, positionXY).r;
float d = LinearEyeDepth(z, _ZBufferParams);
float t = d - positionVS.z;
float3 x = millimPerUnit * length(relPosVS + float3(0, 0, t));
float p = _FilterKernelsNearField[profileID][i][1];
#if SSS_BILATERAL_FILTER
float3 w = ComputeBilateralWeight(x, shapeParam, p);
#else
float3 w = ComputeBilateralWeight(r, shapeParam, p);
#endif
totalIrradiance += w * irradiance;
totalWeight += w;
}
else
{
/*************************************************************************/
/* The irradiance is 0. This could happen for 3 reasons. */
/* Most likely, the surface fragment does not have an SSS material. */
/* Alternatively, our sample comes from a region without any geometry. */
/* Finally, the surface fragment could be completely shadowed. */
/* Our blur is energy-preserving, so 'centerWeight' should be set to 0. */
/* We do not terminate the loop since we want to gather the contribution */
/* of the remaining samples (e.g. in case of hair covering skin). */
/* Note: See comment in the output of deferred.shader */
/*************************************************************************/
}
}
}
#else
float2 pixelsPerMm = rcp(millimPerUnit * unitsPerPixel);
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
// Rescaling the filter is equivalent to inversely scaling the world.
float metersPerUnit = _WorldScales[profileID] / distScale * SSS_BASIC_DISTANCE_SCALE;

[flatten]
if (any(sampleIrradiance))
{
#if SSS_BILATERAL_FILTER
// 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;

正在加载...
取消
保存