浏览代码

Merge pull request #1018 from EvgeniiG/auto_thickness

Implement the mixed thickness mode for transmission
/main
GitHub 6 年前
当前提交
22312097
共有 4 个文件被更改,包括 131 次插入94 次删除
  1. 2
      .gitignore
  2. 53
      ScriptableRenderPipeline/Core/CoreRP/ShaderLibrary/Shadow/ShadowAlgorithms.hlsl
  3. 6
      ScriptableRenderPipeline/HDRenderPipeline/HDRP/Lighting/LightLoop/Shadow.hlsl
  4. 164
      ScriptableRenderPipeline/HDRenderPipeline/HDRP/Material/Lit/Lit.hlsl

2
.gitignore


*.resS
*.sdf
*.sln
*.sublime-project
*.sublime-workspace
*.suo
*.userprefs

53
ScriptableRenderPipeline/Core/CoreRP/ShaderLibrary/Shadow/ShadowAlgorithms.hlsl


return EvalShadow_GetTexcoords( sd, positionWS, ndc, perspProj );
}
uint2 EvalShadow_GetTexcoords( ShadowData sd, real3 positionWS, out real2 closestSampleNDC, bool perspProj )
real2 EvalShadow_GetTexcoords( ShadowData sd, real3 positionWS, out real2 closestSampleNDC, bool perspProj )
{
real4 posCS = EvalShadow_WorldToShadow( sd, positionWS, perspProj );
real2 posNDC = perspProj ? (posCS.xy / posCS.w) : posCS.xy;

return uint2( (posTC * sd.scaleOffset.xy + sd.scaleOffset.zw) * sd.textureSize.xy );
return posTC * sd.scaleOffset.xy + sd.scaleOffset.zw;
uint2 EvalShadow_GetIntTexcoords( ShadowData sd, real3 positionWS, out real2 closestSampleNDC, bool perspProj )
{
real2 texCoords = EvalShadow_GetTexcoords(sd, positionWS, closestSampleNDC, perspProj);
return uint2(texCoords * sd.textureSize.xy);
}
//
// Biasing functions

sd = shadowContext.shadowDatas[index + faceIndex];
real4 closestNDC = { 0,0,0,1 };
uint2 texelIdx = EvalShadow_GetTexcoords( sd, positionWS, closestNDC.xy, true );
uint2 texelIdx = EvalShadow_GetIntTexcoords( sd, positionWS, closestNDC.xy, true );
// load the texel
uint texIdx, sampIdx;

sd = shadowContext.shadowDatas[index + faceIndex];
real4 closestNDC = { 0,0,0,1 };
uint2 texelIdx = EvalShadow_GetTexcoords( sd, positionWS, closestNDC.xy, true );
uint2 texelIdx = EvalShadow_GetIntTexcoords( sd, positionWS, closestNDC.xy, true );
// load the texel
closestNDC.z = LOAD_TEXTURE2D_ARRAY_LOD( tex, texelIdx, sd.slice, 0 ).x;

ShadowData sd = shadowContext.shadowDatas[index];
real4 closestNDC = { 0,0,0,1 };
uint2 texelIdx = EvalShadow_GetTexcoords( sd, positionWS, closestNDC.xy, true );
uint2 texelIdx = EvalShadow_GetIntTexcoords( sd, positionWS, closestNDC.xy, true );
// load the texel
uint texIdx, sampIdx;

ShadowData sd = shadowContext.shadowDatas[index];
real4 closestNDC = { 0,0,0,1 };
uint2 texelIdx = EvalShadow_GetTexcoords( sd, positionWS, closestNDC.xy, true );
uint2 texelIdx = EvalShadow_GetIntTexcoords( sd, positionWS, closestNDC.xy, true );
// load the texel
closestNDC.z = LOAD_TEXTURE2D_ARRAY_LOD( tex, texelIdx, sd.slice, 0 ).x;

sd = shadowContext.shadowDatas[index + faceIndex];
real4 closestNDC = { 0,0,0,1 };
uint2 texelIdx = EvalShadow_GetTexcoords( sd, positionWS, closestNDC.xy, true );
uint2 texelIdx = EvalShadow_GetIntTexcoords( sd, positionWS, closestNDC.xy, true );
// load the texel
uint texIdx, sampIdx;

sd = shadowContext.shadowDatas[index + faceIndex];
real4 closestNDC = { 0,0,0,1 };
uint2 texelIdx = EvalShadow_GetTexcoords( sd, positionWS, closestNDC.xy, true );
uint2 texelIdx = EvalShadow_GetIntTexcoords( sd, positionWS, closestNDC.xy, true );
// load the texel
closestNDC.z = LOAD_TEXTURE2D_ARRAY_LOD( tex, texelIdx, sd.slice, 0 ).x;

return closestWS.xyz / closestWS.w;
}
real EvalShadow_SampleClosestDistance_Punctual( ShadowContext shadowContext, Texture2DArray tex, SamplerState sampl,
real3 positionWS, int index, real3 L, real3 lightPositionWS )
{
// get the algorithm
ShadowData sd = shadowContext.shadowDatas[index];
uint shadowType;
UnpackShadowType( sd.shadowType, shadowType );
// load the right shadow data for the current face
int faceIndex = shadowType == GPUSHADOWTYPE_POINT ? (CubeMapFaceID( -L ) + 1) : 0;
sd = shadowContext.shadowDatas[index + faceIndex];
real4 closestNDC = { 0,0,0,1 };
real2 texelIdx = EvalShadow_GetTexcoords( sd, positionWS, closestNDC.xy, true );
// sample the shadow map
closestNDC.z = SAMPLE_TEXTURE2D_ARRAY_LOD( tex, sampl, texelIdx, sd.slice, 0 ).x;
// reconstruct depth position
real4 closestWS = mul( closestNDC, sd.shadowToWorld );
real3 occluderPosWS = closestWS.xyz / closestWS.w;
return distance( occluderPosWS, lightPositionWS );
}
real3 EvalShadow_GetClosestSample_Cascade( ShadowContext shadowContext, real3 positionWS, real3 normalWS, int index, real4 L )
{
// load the right shadow data for the current face

if( shadowSplitIndex < 0 )
return 1.0;
return 0.0;
uint2 texelIdx = EvalShadow_GetTexcoords( sd, positionWS, closestNDC.xy, false );
uint2 texelIdx = EvalShadow_GetIntTexcoords( sd, positionWS, closestNDC.xy, false );
// load the texel
uint texIdx, sampIdx;

int shadowSplitIndex = EvalShadow_GetSplitIndex( shadowContext, index, positionWS, payloadOffset, alpha );
if( shadowSplitIndex < 0 )
return 1.0;
return 0.0;
uint2 texelIdx = EvalShadow_GetTexcoords( sd, positionWS, closestNDC.xy, false );
uint2 texelIdx = EvalShadow_GetIntTexcoords( sd, positionWS, closestNDC.xy, false );
// load the texel
uint texIdx, sampIdx;

6
ScriptableRenderPipeline/HDRenderPipeline/HDRP/Lighting/LightLoop/Shadow.hlsl


{
return EvalShadow_GetClosestSample_Punctual( shadowContext, shadowContext.tex2DArray[SHADOW_DISPATCH_PUNC_TEX], positionWS, index, L );
}
float GetPunctualShadowClosestDistance( ShadowContext shadowContext, SamplerState sampl, real3 positionWS, int index, float3 L, float3 lightPositionWS)
{
return EvalShadow_SampleClosestDistance_Punctual( shadowContext, shadowContext.tex2DArray[SHADOW_DISPATCH_PUNC_TEX], sampl, positionWS, index, L, lightPositionWS );
}
#endif
// cleanup the defines

164
ScriptableRenderPipeline/HDRenderPipeline/HDRP/Material/Lit/Lit.hlsl


// Additional bits set in 'bsdfData.materialFeatures' to save registers and simplify feature tracking.
#define MATERIAL_FEATURE_FLAGS_SSS_OUTPUT_SPLIT_LIGHTING ((MATERIAL_FEATURE_MASK_FLAGS + 1) << 0)
#define MATERIAL_FEATURE_FLAGS_SSS_TEXTURING_MODE_OFFSET FastLog2((MATERIAL_FEATURE_MASK_FLAGS + 1) << 1) // 2 bits
#define MATERIAL_FEATURE_FLAGS_TRANSMISSION_MODE_AUTO_THICKNESS ((MATERIAL_FEATURE_MASK_FLAGS + 1) << 3)
#define MATERIAL_FEATURE_FLAGS_TRANSMISSION_MODE_MIXED_THICKNESS ((MATERIAL_FEATURE_MASK_FLAGS + 1) << 3)
uint FeatureFlagsToTileVariant(uint featureFlags)
{

// the current object. That's not a problem, since large thickness will result in low intensity.
bool useThinObjectMode = IsBitSet(asuint(_TransmissionFlags), diffusionProfile);
bsdfData.materialFeatures |= useThinObjectMode ? 0 : MATERIAL_FEATURE_FLAGS_TRANSMISSION_MODE_AUTO_THICKNESS;
bsdfData.materialFeatures |= useThinObjectMode ? 0 : MATERIAL_FEATURE_FLAGS_TRANSMISSION_MODE_MIXED_THICKNESS;
// Compute transmittance using baked thickness here. It may be overridden for direct lighting
// in the auto-thickness mode (but is always be used for indirect lighting).

// Decompress feature-agnostic data from the G-Buffer.
float3 baseColor = inGBuffer0.rgb;
bsdfData.specularOcclusion = inGBuffer0.a; // Later possibly overwritten by SSS
bsdfData.perceptualRoughness = inGBuffer1.a;
bsdfData.perceptualRoughness = inGBuffer1.a;
bakeDiffuseLighting = inGBuffer3.rgb;

FillMaterialTransmission(sssData.diffusionProfile, inGBuffer2.g, bsdfData);
}
}
else
{
bsdfData.specularOcclusion = inGBuffer0.a;
}
// Special handling for anisotropy: When anisotropy is present in a tile, the whole tile will use anisotropy to avoid divergent evaluation of GGX that increase the cost
// Note that it mean that when we have the worse case, we always use Anisotropy and shader like deferred.shader are always the worst case (but only used for debugging)

// - we integrate the diffuse reflectance profile w.r.t. the radius (while also accounting
// for the thickness) to compute the transmittance;
// - we multiply the transmitted radiance by the transmittance.
float3 EvaluateTransmission(BSDFData bsdfData, float3 transmittance, float NdotL, float NdotV, float attenuation)
float3 EvaluateTransmission(BSDFData bsdfData, float3 transmittance, float NdotL, float NdotV, float LdotV, float attenuation)
// Apply wrapped lighting to better handle thin objects at grazing angles.
float negatedNdotL = -NdotL;
// Apply wrapped lighting to better handle thin objects (cards) at grazing angles.
bool autoThicknessMode = HasFeatureFlag(bsdfData.materialFeatures, MATERIAL_FEATURE_FLAGS_TRANSMISSION_MODE_AUTO_THICKNESS);
float backNdotL = autoThicknessMode ? negatedNdotL : wrappedNdotL;
// Apply BSDF-specific diffuse transmission to attenuation. See also: [SSS-NOTE-TRSM]
// We don't multiply by 'bsdfData.diffuseColor' here. It's done only once in PostEvaluateBSDF().

attenuation *= INV_PI * F_Transm_Schlick(0, 0.5, NdotV) * F_Transm_Schlick(0, 0.5, abs(backNdotL));
attenuation *= DisneyDiffuse(NdotV, max(0, -NdotL), LdotV, bsdfData.perceptualRoughness);
float intensity = max(0, attenuation * backNdotL); // Warning: attenuation can be greater than 1 due to the inverse square attenuation (when position is close to light)
float intensity = attenuation * wrappedNdotL;
return intensity * transmittance;
}

float3 N = bsdfData.normalWS;
float3 L = -lightData.forward; // Lights point backward in Unity
float NdotL = dot(N, L); // Note: Ideally this N here should be vertex normal - use for transmisison
float3 transmittance = bsdfData.transmittance;
bool autoThicknessMode = HasFeatureFlag(bsdfData.materialFeatures, MATERIAL_FEATURE_FLAGS_TRANSMISSION_MODE_AUTO_THICKNESS);
UNITY_BRANCH
if (autoThicknessMode && NdotL < 0 && lightData.shadowIndex >= 0)
{
// TODO: perform bilinear filtering of the shadow map.
// Recompute transmittance using the thickness value computed from the shadow map.
float3 occluderPosWS = GetDirectionalShadowClosestSample(lightLoopContext.shadowContext, posInput.positionWS, bsdfData.normalWS, lightData.shadowIndex, float4(L, 0));
float thicknessInUnits = distance(posInput.positionWS, occluderPosWS);
float thicknessInMeters = thicknessInUnits * _WorldScales[bsdfData.diffusionProfile].x;
float thicknessInMillimeters = thicknessInMeters * MILLIMETERS_PER_METER;
// TODO: optimize.
#if SHADEROPTIONS_USE_DISNEY_SSS
transmittance = ComputeTransmittanceDisney(_ShapeParams[bsdfData.diffusionProfile].rgb,
_TransmissionTintsAndFresnel0[bsdfData.diffusionProfile].rgb,
thicknessInMillimeters);
#else
transmittance = ComputeTransmittanceJimenez(_HalfRcpVariancesAndWeights[bsdfData.diffusionProfile][0].rgb,
_HalfRcpVariancesAndWeights[bsdfData.diffusionProfile][0].a,
_HalfRcpVariancesAndWeights[bsdfData.diffusionProfile][1].rgb,
_HalfRcpVariancesAndWeights[bsdfData.diffusionProfile][1].a,
_TransmissionTintsAndFresnel0[bsdfData.diffusionProfile].rgb,
thicknessInMillimeters);
#endif
// Make sure we do not sample the shadow map twice.
lightData.shadowIndex = -1;
// Note: we do not modify the distance to the light, or the light angle for the back face.
// This is a performance-saving optimization which makes sense as long as the thickness is small.
}
float NdotV = ClampNdotV(preLightData.NdotV);
float NdotL = dot(N, L);
float LdotV = dot(L, V);
float3 color;
float attenuation;

lighting.specular *= intensity * lightData.specularScale;
}
// TODO: move this before BSDF() to save VGPRs.
if (HasFeatureFlag(bsdfData.materialFeatures, MATERIALFEATUREFLAGS_LIT_TRANSMISSION))
// The mixed thickness mode is not supported by directional lights due to poor quality and high performance impact.
bool mixedThicknessMode = HasFeatureFlag(bsdfData.materialFeatures, MATERIAL_FEATURE_FLAGS_TRANSMISSION_MODE_MIXED_THICKNESS);
if (HasFeatureFlag(bsdfData.materialFeatures, MATERIALFEATUREFLAGS_LIT_TRANSMISSION) && !mixedThicknessMode)
lighting.diffuse += EvaluateTransmission(bsdfData, transmittance, NdotL, ClampNdotV(preLightData.NdotV), attenuation * lightData.diffuseScale);
lighting.diffuse += EvaluateTransmission(bsdfData, bsdfData.transmittance, NdotL, NdotV, LdotV, attenuation * lightData.diffuseScale);
}
// Save ALU by applying light and cookie colors only once.

}
float3 N = bsdfData.normalWS;
float NdotV = ClampNdotV(preLightData.NdotV);
float3 transmittance = bsdfData.transmittance;
bool autoThicknessMode = HasFeatureFlag(bsdfData.materialFeatures, MATERIAL_FEATURE_FLAGS_TRANSMISSION_MODE_AUTO_THICKNESS);
UNITY_BRANCH
if (autoThicknessMode && NdotL < 0 && lightData.shadowIndex >= 0)
{
// TODO: perform bilinear filtering of the shadow map.
// Recompute transmittance using the thickness value computed from the shadow map.
float3 occluderPosWS = GetPunctualShadowClosestSample(lightLoopContext.shadowContext, posInput.positionWS, lightData.shadowIndex, L);
float LdotV = dot(L, V);
float thicknessInUnits = distance(posInput.positionWS, occluderPosWS);
float thicknessInMeters = thicknessInUnits * _WorldScales[bsdfData.diffusionProfile].x;
float thicknessInMillimeters = thicknessInMeters * MILLIMETERS_PER_METER;
bool mixedThicknessMode = HasFeatureFlag(bsdfData.materialFeatures, MATERIAL_FEATURE_FLAGS_TRANSMISSION_MODE_MIXED_THICKNESS)
&& NdotL < 0 && lightData.shadowIndex >= 0;
// TODO: optimize.
#if SHADEROPTIONS_USE_DISNEY_SSS
transmittance = ComputeTransmittanceDisney(_ShapeParams[bsdfData.diffusionProfile].rgb,
_TransmissionTintsAndFresnel0[bsdfData.diffusionProfile].rgb,
thicknessInMillimeters);
#else
transmittance = ComputeTransmittanceJimenez(_HalfRcpVariancesAndWeights[bsdfData.diffusionProfile][0].rgb,
_HalfRcpVariancesAndWeights[bsdfData.diffusionProfile][0].a,
_HalfRcpVariancesAndWeights[bsdfData.diffusionProfile][1].rgb,
_HalfRcpVariancesAndWeights[bsdfData.diffusionProfile][1].a,
_TransmissionTintsAndFresnel0[bsdfData.diffusionProfile].rgb,
thicknessInMillimeters);
#endif
// Save the original version for the transmission code below.
int originalShadowIndex = lightData.shadowIndex;
if (mixedThicknessMode)
{
// Note: we do not modify the distance to the light, or the light angle for the back face.
// This is a performance-saving optimization which makes sense as long as the thickness is small.
}
float3 color;

// Restore the original shadow index.
lightData.shadowIndex = originalShadowIndex;
float intensity = max(0, attenuation * NdotL); // Warning: attenuation can be greater than 1 due to the inverse square attenuation (when position is close to light)

lighting.specular *= intensity * lightData.specularScale;
}
// TODO: move this before BSDF() to save VGPRs.
float3 transmittance = bsdfData.transmittance;
if (mixedThicknessMode)
{
// Recompute transmittance using the thickness value computed from the shadow map.
// Compute the distance from the light to the back face of the object along the light direction.
float distBackFaceToLight = GetPunctualShadowClosestDistance(lightLoopContext.shadowContext, s_linear_clamp_sampler,
posInput.positionWS, lightData.shadowIndex, L, lightData.positionWS);
// Our subsurface scattering models use the semi-infinite planar slab assumption.
// Therefore, we need to find the thickness along the normal.
float distFrontFaceToLight = distances.x;
float thicknessInUnits = (distFrontFaceToLight - distBackFaceToLight) * -NdotL;
float thicknessInMeters = thicknessInUnits * _WorldScales[bsdfData.diffusionProfile].x;
float thicknessInMillimeters = thicknessInMeters * MILLIMETERS_PER_METER;
#if SHADEROPTIONS_USE_DISNEY_SSS
// We need to make sure it's not less than the baked thickness to minimize light leaking.
float thicknessDelta = max(0, thicknessInMillimeters - bsdfData.thickness);
float3 S = _ShapeParams[bsdfData.diffusionProfile];
// Approximate the decrease of transmittance by e^(-1/3 * dt * S).
#if 0
float3 expOneThird = exp(((-1.0 / 3.0) * thicknessDelta) * S);
#else
// Help the compiler.
float k = (-1.0 / 3.0) * LOG2_E;
float3 p = (k * thicknessDelta) * S;
float3 expOneThird = exp2(p);
#endif
transmittance *= expOneThird;
#else // SHADEROPTIONS_USE_DISNEY_SSS
// We need to make sure it's not less than the baked thickness to minimize light leaking.
thicknessInMillimeters = max(thicknessInMillimeters, bsdfData.thickness);
transmittance = ComputeTransmittanceJimenez(_HalfRcpVariancesAndWeights[bsdfData.diffusionProfile][0].rgb,
_HalfRcpVariancesAndWeights[bsdfData.diffusionProfile][0].a,
_HalfRcpVariancesAndWeights[bsdfData.diffusionProfile][1].rgb,
_HalfRcpVariancesAndWeights[bsdfData.diffusionProfile][1].a,
_TransmissionTintsAndFresnel0[bsdfData.diffusionProfile].rgb,
thicknessInMillimeters);
#endif // SHADEROPTIONS_USE_DISNEY_SSS
}
// Note: we do not modify the distance to the light, or the light angle for the back face.
// This is a performance-saving optimization which makes sense as long as the thickness is small.
lighting.diffuse += EvaluateTransmission(bsdfData, transmittance, NdotL, ClampNdotV(preLightData.NdotV), attenuation * lightData.diffuseScale);
lighting.diffuse += EvaluateTransmission(bsdfData, transmittance, NdotL, NdotV, LdotV, attenuation * lightData.diffuseScale);
}
// Save ALU by applying light and cookie colors only once.

正在加载...
取消
保存