浏览代码

Merge pull request #283 from EvgeniiG/master

Implement SSS LOD functionality
/RenderPassXR_Sandbox
GitHub 7 年前
当前提交
9df4abaf
共有 3 个文件被更改,包括 107 次插入55 次删除
  1. 146
      Assets/ScriptableRenderPipeline/HDRenderPipeline/Material/Lit/Resources/CombineSubsurfaceScattering.shader
  2. 14
      Assets/ScriptableRenderPipeline/HDRenderPipeline/Material/Lit/SubsurfaceScatteringProfile.cs
  3. 2
      Assets/ScriptableRenderPipeline/HDRenderPipeline/Material/Lit/SubsurfaceScatteringProfile.cs.hlsl

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


#pragma vertex Vert
#pragma fragment Frag
#define SSS_PASS
#define SSS_PASS 1
#define SSS_DEBUG 0
#define MILLIMETERS_PER_METER 1000
//-------------------------------------------------------------------------------------

return valX * rcpPdfX;
}
#define SSS_ITER(i, n, kernel, profileID, shapeParam, centerPosUnSS, centerDepthVS, \
millimPerUnit, scaledPixPerMm, rcpDistScale, totalIrradiance, totalWeight) \
{ \
float r = kernel[profileID][i][0]; \
/* The relative sample position is known at the compile time. */ \
float phi = TWO_PI * Fibonacci2d(i, n).y; \
float2 vec = r * float2(cos(phi), sin(phi)); \
\
float2 position = centerPosUnSS + vec * scaledPixPerMm; \
float rcpPdf = kernel[profileID][i][1]; \
float depth = LOAD_TEXTURE2D(_MainDepthTexture, position).r; \
float3 irradiance = LOAD_TEXTURE2D(_IrradianceSource, position).rgb; \
\
[flatten] \
if (any(irradiance) == false) \
{ \
/*************************************************************************/ \
/* 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). */ \
/*************************************************************************/ \
continue; \
} \
\
/* Apply bilateral weighting. */ \
float d = LinearEyeDepth(depth, _ZBufferParams); \
float z = millimPerUnit * d - (millimPerUnit * centerDepthVS); \
float3 w = ComputeBilateralWeight(shapeParam, r, z, rcpDistScale, rcpPdf); \
\
totalIrradiance += w * irradiance; \
totalWeight += w; \
}
struct Attributes
{
uint vertexID : SV_VertexID;

float maxDistance = _ShapeParameters[profileID].a;
// Reconstruct the view-space position.
float2 cornerPosSS = posInput.positionSS + 0.5 * _ScreenSize.zw;
float rawDepth = LOAD_TEXTURE2D(_MainDepthTexture, posInput.unPositionSS).r;
float3 centerPosVS = ComputeViewSpacePosition(posInput.positionSS, rawDepth, _InvProjMatrix);
float3 cornerPosVS = ComputeViewSpacePosition(cornerPosSS, rawDepth, _InvProjMatrix);
float2 centerPosSS = posInput.positionSS;
float2 cornerPosSS = centerPosSS + 0.5 * _ScreenSize.zw;
float centerDepth = LOAD_TEXTURE2D(_MainDepthTexture, posInput.unPositionSS).r;
float3 centerPosVS = ComputeViewSpacePosition(centerPosSS, centerDepth, _InvProjMatrix);
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);

// Take the first (central) sample.
float2 samplePosition = posInput.unPositionSS;
float3 sampleIrradiance = LOAD_TEXTURE2D(_IrradianceSource, samplePosition).rgb;
float2 centerPosition = posInput.unPositionSS;
float3 centerIrradiance = LOAD_TEXTURE2D(_IrradianceSource, centerPosition).rgb;
float maxDistancePixels = maxDistance * max(scaledPixPerMm.x, scaledPixPerMm.y);
if (maxDistance * max(scaledPixPerMm.x, scaledPixPerMm.y) < 0.5)
if (maxDistancePixels < 0.5)
return float4(bsdfData.diffuseColor * sampleIrradiance, 1);
#if SSS_DEBUG
return float4(0, 0, 1, 1);
#else
return float4(bsdfData.diffuseColor * centerIrradiance, 1);
#endif
bool useNearFieldKernel = true; // TODO
// Accumulate filtered irradiance and bilateral weights (for renormalization).
float3 totalIrradiance, totalWeight;
if (useNearFieldKernel)
// Use fewer samples for SS regions smaller than 5x5 pixels (rotated by 45 degrees).
[branch]
if (maxDistancePixels < 4)
float sampleRcpPdf = _FilterKernelsNearField[profileID][0][1];
float3 sampleWeight = KernelValCircle(0, shapeParam) * sampleRcpPdf;
#if SSS_DEBUG
return float4(0.5, 0.5, 0, 1);
#endif
// Accumulate filtered irradiance and bilateral weights (for renormalization).
float3 totalIrradiance = sampleWeight * sampleIrradiance;
float3 totalWeight = sampleWeight;
float centerRcpPdf = _FilterKernelsFarField[profileID][0][1];
float3 centerWeight = KernelValCircle(0, shapeParam) * centerRcpPdf;
totalIrradiance = centerWeight * centerIrradiance;
totalWeight = centerWeight;
for (uint i = 1; i < SSS_N_SAMPLES_NEAR_FIELD; i++)
for (uint i = 1; i < SSS_N_SAMPLES_FAR_FIELD; i++)
// Everything except for the radius is a compile-time constant.
float r = _FilterKernelsNearField[profileID][i][0];
float phi = TWO_PI * Fibonacci2d(i, SSS_N_SAMPLES_NEAR_FIELD).y;
float2 pos = r * float2(cos(phi), sin(phi));
SSS_ITER(i, SSS_N_SAMPLES_FAR_FIELD, _FilterKernelsFarField,
profileID, shapeParam, centerPosition, centerPosVS.z,
millimPerUnit, scaledPixPerMm, rcp(distScale),
totalIrradiance, totalWeight)
}
}
else
{
#if SSS_DEBUG
return float4(1, 0, 0, 1);
#endif
samplePosition = posInput.unPositionSS + pos * scaledPixPerMm;
sampleRcpPdf = _FilterKernelsNearField[profileID][i][1];
float centerRcpPdf = _FilterKernelsNearField[profileID][0][1];
float3 centerWeight = KernelValCircle(0, shapeParam) * centerRcpPdf;
rawDepth = LOAD_TEXTURE2D(_MainDepthTexture, samplePosition).r;
sampleIrradiance = LOAD_TEXTURE2D(_IrradianceSource, samplePosition).rgb;
totalIrradiance = centerWeight * centerIrradiance;
totalWeight = centerWeight;
[flatten]
if (any(sampleIrradiance) == false)
{
// 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 'sampleWeight' 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).
continue;
}
// Apply bilateral weighting.
float sampleZ = LinearEyeDepth(rawDepth, _ZBufferParams);
float z = millimPerUnit * sampleZ - (millimPerUnit * centerPosVS.z);
sampleWeight = ComputeBilateralWeight(shapeParam, r, z, rcp(distScale), sampleRcpPdf);
totalIrradiance += sampleWeight * sampleIrradiance;
totalWeight += sampleWeight;
// Perform integration over the screen-aligned plane in the view space.
// TODO: it would be more accurate to use the tangent plane in the world space.
[unroll]
for (uint i = 1; i < SSS_N_SAMPLES_NEAR_FIELD; i++)
{
SSS_ITER(i, SSS_N_SAMPLES_NEAR_FIELD, _FilterKernelsNearField,
profileID, shapeParam, centerPosition, centerPosVS.z,
millimPerUnit, scaledPixPerMm, rcp(distScale),
totalIrradiance, totalWeight)
}
return float4(bsdfData.diffuseColor * totalIrradiance / totalWeight, 1);
}
else
{
return float4(0, 0, 0, 1); // TODO
}
return float4(bsdfData.diffuseColor * totalIrradiance / totalWeight, 1);
}
ENDHLSL
}

14
Assets/ScriptableRenderPipeline/HDRenderPipeline/Material/Lit/SubsurfaceScatteringProfile.cs


public const int SSS_N_PROFILES = 8; // Max. number of profiles, including the slot taken by the neutral profile
public const int SSS_NEUTRAL_PROFILE_ID = SSS_N_PROFILES - 1; // Does not result in blurring
public const int SSS_N_SAMPLES_NEAR_FIELD = 55; // Used for extreme close ups; must be a Fibonacci number
public const int SSS_N_SAMPLES_FAR_FIELD = 34; // Used at a regular distance; must be a Fibonacci number
public const int SSS_N_SAMPLES_FAR_FIELD = 21; // Used at a regular distance; must be a Fibonacci number
public const int SSS_TRSM_MODE_NONE = 0;
public const int SSS_TRSM_MODE_THIN = 1;
}

m_ScatteringDistance = m_FilterKernelNearField[SssConstants.SSS_N_SAMPLES_NEAR_FIELD - 1].x;
// TODO: far field.
// Importance sample the far field kernel.
for (int i = 0; i < SssConstants.SSS_N_SAMPLES_FAR_FIELD; i++)
{
float p = i * (1.0f / SssConstants.SSS_N_SAMPLES_FAR_FIELD);
float r = KernelCdfInverse(p, s);
// N.b.: computation of normalized weights, and multiplication by the surface albedo
// of the actual geometry is performed at runtime (in the shader).
m_FilterKernelFarField[i].x = r;
m_FilterKernelFarField[i].y = 1.0f / KernelPdf(r, s);
}
}
public Vector3 shapeParameter

2
Assets/ScriptableRenderPipeline/HDRenderPipeline/Material/Lit/SubsurfaceScatteringProfile.cs.hlsl


#define SSS_N_PROFILES (8)
#define SSS_NEUTRAL_PROFILE_ID (7)
#define SSS_N_SAMPLES_NEAR_FIELD (55)
#define SSS_N_SAMPLES_FAR_FIELD (34)
#define SSS_N_SAMPLES_FAR_FIELD (21)
#define SSS_TRSM_MODE_NONE (0)
#define SSS_TRSM_MODE_THIN (1)
正在加载...
取消
保存