2021-09-09 20:42:29 -04:00

368 lines
15 KiB
HLSL

#include "Packages/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/PathTracing/Shaders/PathTracingIntersection.hlsl"
#include "Packages/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/PathTracing/Shaders/PathTracingMaterial.hlsl"
#include "Packages/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/PathTracing/Shaders/PathTracingBSDF.hlsl"
// Lit Material Data:
//
// bsdfWeight0 Diffuse BRDF
// bsdfWeight1 Coat GGX BRDF
// bsdfWeight2 Spec GGX BRDF
// bsdfWeight3 Spec GGX BTDF
void ProcessBSDFData(PathIntersection pathIntersection, BuiltinData builtinData, inout BSDFData bsdfData)
{
// Adjust roughness to reduce fireflies
bsdfData.roughnessT = max(pathIntersection.maxRoughness, bsdfData.roughnessT);
bsdfData.roughnessB = max(pathIntersection.maxRoughness, bsdfData.roughnessB);
float NdotV = abs(dot(bsdfData.normalWS, WorldRayDirection()));
// Modify fresnel0 value to take iridescence into account (code adapted from Lit.hlsl to produce identical results)
if (bsdfData.iridescenceMask > 0.0)
{
float topIOR = lerp(1.0, CLEAR_COAT_IOR, bsdfData.coatMask);
float viewAngle = sqrt(1.0 + (Sq(NdotV) - 1.0) / Sq(topIOR));
bsdfData.fresnel0 = lerp(bsdfData.fresnel0, EvalIridescence(topIOR, viewAngle, bsdfData.iridescenceThickness, bsdfData.fresnel0), bsdfData.iridescenceMask);
}
// We store an energy compensation coefficient for GGX into the specular occlusion (code adapted from Lit.hlsl to produce identical results)
#ifdef LIT_USE_GGX_ENERGY_COMPENSATION
float roughness = 0.5 * (bsdfData.roughnessT + bsdfData.roughnessB);
float2 coordLUT = Remap01ToHalfTexelCoord(float2(sqrt(NdotV), roughness), FGDTEXTURE_RESOLUTION);
float E = SAMPLE_TEXTURE2D_LOD(_PreIntegratedFGD_GGXDisneyDiffuse, s_linear_clamp_sampler, coordLUT, 0).y;
bsdfData.specularOcclusion = (1.0 - E) / E;
#else
bsdfData.specularOcclusion = 0.0;
#endif
#if defined(_SURFACE_TYPE_TRANSPARENT) && !HAS_REFRACTION
// Turn alpha blending into proper refraction
bsdfData.transmittanceMask = 1.0 - builtinData.opacity;
bsdfData.ior = 1.0;
#endif
}
bool CreateMaterialData(PathIntersection pathIntersection, BuiltinData builtinData, BSDFData bsdfData, inout float3 shadingPosition, inout float theSample, out MaterialData mtlData)
{
// Alter values in the material's bsdfData struct, to better suit path tracing
mtlData.bsdfData = bsdfData;
ProcessBSDFData(pathIntersection, builtinData, mtlData.bsdfData);
mtlData.V = -WorldRayDirection();
// Assume no coating by default
float coatingTransmission = 1.0;
// First determine if our incoming direction V is above (exterior) or below (interior) the surface
if (IsAbove(mtlData.bsdfData.geomNormalWS, mtlData.V))
{
float NdotV = dot(mtlData.bsdfData.normalWS, mtlData.V);
float Fcoat = F_Schlick(CLEAR_COAT_F0, NdotV) * mtlData.bsdfData.coatMask;
float Fspec = Luminance(F_Schlick(mtlData.bsdfData.fresnel0, NdotV));
// If N.V < 0 (can happen with normal mapping) we want to avoid spec sampling
bool consistentNormal = (NdotV > 0.001);
mtlData.bsdfWeight[1] = consistentNormal ? Fcoat : 0.0;
coatingTransmission = 1.0 - mtlData.bsdfWeight[1];
mtlData.bsdfWeight[2] = consistentNormal ? coatingTransmission * lerp(Fspec, 0.5, 0.5 * (mtlData.bsdfData.roughnessT + mtlData.bsdfData.roughnessB)) * (1.0 + Fspec * mtlData.bsdfData.specularOcclusion) : 0.0;
mtlData.bsdfWeight[3] = consistentNormal ? (coatingTransmission - mtlData.bsdfWeight[2]) * mtlData.bsdfData.transmittanceMask : 0.0;
mtlData.bsdfWeight[0] = coatingTransmission * (1.0 - mtlData.bsdfData.transmittanceMask) * Luminance(mtlData.bsdfData.diffuseColor) * mtlData.bsdfData.ambientOcclusion;
}
#ifdef _SURFACE_TYPE_TRANSPARENT
else // Below
{
float NdotV = -dot(mtlData.bsdfData.normalWS, mtlData.V);
float F = F_FresnelDielectric(1.0 / mtlData.bsdfData.ior, NdotV);
// If N.V < 0 (can happen with normal mapping) we want to avoid spec sampling
bool consistentNormal = (NdotV > 0.001);
mtlData.bsdfWeight[0] = 0.0;
mtlData.bsdfWeight[1] = 0.0;
mtlData.bsdfWeight[2] = consistentNormal ? F : 0.0;
mtlData.bsdfWeight[3] = consistentNormal ? (1.0 - mtlData.bsdfWeight[1]) * mtlData.bsdfData.transmittanceMask : 0.0;
}
#endif
// Normalize the weights
float wSum = mtlData.bsdfWeight[0] + mtlData.bsdfWeight[1] + mtlData.bsdfWeight[2] + mtlData.bsdfWeight[3];
if (wSum < BSDF_WEIGHT_EPSILON)
return false;
mtlData.bsdfWeight /= wSum;
#ifdef _MATERIAL_FEATURE_SUBSURFACE_SCATTERING
float subsurfaceWeight = mtlData.bsdfWeight[0] * mtlData.bsdfData.subsurfaceMask * (1.0 - pathIntersection.maxRoughness);
mtlData.isSubsurface = theSample < subsurfaceWeight;
if (mtlData.isSubsurface)
{
// We do a full, ray-traced subsurface scattering computation here:
// Let's try and change shading position and normal, and replace the diffuse color by the subsurface throughput
mtlData.subsurfaceWeightFactor = subsurfaceWeight;
SSS::Result subsurfaceResult;
float3 meanFreePath = 0.001 / (_ShapeParamsAndMaxScatterDists[mtlData.bsdfData.diffusionProfileIndex].rgb * _WorldScalesAndFilterRadiiAndThicknessRemaps[mtlData.bsdfData.diffusionProfileIndex].x);
if (!SSS::RandomWalk(shadingPosition, mtlData.bsdfData.normalWS, mtlData.bsdfData.diffuseColor, meanFreePath, pathIntersection.pixelCoord, subsurfaceResult))
return false;
shadingPosition = subsurfaceResult.exitPosition;
mtlData.bsdfData.normalWS = subsurfaceResult.exitNormal;
mtlData.bsdfData.geomNormalWS = subsurfaceResult.exitNormal;
mtlData.bsdfData.diffuseColor = subsurfaceResult.throughput * coatingTransmission;
}
else
{
// Otherwise, we just compute BSDFs as usual
mtlData.subsurfaceWeightFactor = 1.0 - subsurfaceWeight;
mtlData.bsdfWeight[0] = max(mtlData.bsdfWeight[0] - subsurfaceWeight, BSDF_WEIGHT_EPSILON);
mtlData.bsdfWeight /= mtlData.subsurfaceWeightFactor;
theSample -= subsurfaceWeight;
}
// Rescale the sample we used for the SSS selection test
theSample /= mtlData.subsurfaceWeightFactor;
#endif
return true;
}
// Little helper to get the specular compensation term
float3 GetSpecularCompensation(BSDFData bsdfData)
{
return 1.0 + bsdfData.specularOcclusion * bsdfData.fresnel0;
}
bool SampleMaterial(MaterialData mtlData, float3 inputSample, out float3 sampleDir, out MaterialResult result)
{
Init(result);
#ifdef _MATERIAL_FEATURE_SUBSURFACE_SCATTERING
if (mtlData.isSubsurface)
{
if (!BRDF::SampleLambert(mtlData, inputSample, sampleDir, result.diffValue, result.diffPdf))
return false;
result.diffValue *= mtlData.bsdfData.ambientOcclusion * (1.0 - mtlData.bsdfData.transmittanceMask);
return true;
}
#endif
if (IsAbove(mtlData))
{
float3 value;
float pdf;
float fresnelSpec, fresnelClearCoat = 0.0;
if (inputSample.z < mtlData.bsdfWeight[0]) // Diffuse BRDF
{
if (!BRDF::SampleDiffuse(mtlData, inputSample, sampleDir, result.diffValue, result.diffPdf))
return false;
result.diffPdf *= mtlData.bsdfWeight[0];
if (mtlData.bsdfWeight[1] > BSDF_WEIGHT_EPSILON)
{
BRDF::EvaluateGGX(mtlData, CLEAR_COAT_ROUGHNESS, CLEAR_COAT_F0, sampleDir, value, pdf, fresnelClearCoat);
fresnelClearCoat *= mtlData.bsdfData.coatMask;
result.specValue += value * mtlData.bsdfData.coatMask;
result.specPdf += mtlData.bsdfWeight[1] * pdf;
}
result.diffValue *= mtlData.bsdfData.ambientOcclusion * (1.0 - mtlData.bsdfData.transmittanceMask) * (1.0 - fresnelClearCoat);
if (mtlData.bsdfWeight[2] > BSDF_WEIGHT_EPSILON)
{
BRDF::EvaluateAnisoGGX(mtlData, mtlData.bsdfData.fresnel0, sampleDir, value, pdf, fresnelSpec);
result.specValue += value * (1.0 - fresnelClearCoat) * GetSpecularCompensation(mtlData.bsdfData);
result.specPdf += mtlData.bsdfWeight[2] * pdf;
}
}
else if (inputSample.z < mtlData.bsdfWeight[0] + mtlData.bsdfWeight[1]) // Clear coat BRDF
{
if (!BRDF::SampleGGX(mtlData, CLEAR_COAT_ROUGHNESS, CLEAR_COAT_F0, inputSample, sampleDir, result.specValue, result.specPdf, fresnelClearCoat))
return false;
fresnelClearCoat *= mtlData.bsdfData.coatMask;
result.specValue *= mtlData.bsdfData.coatMask;
result.specPdf *= mtlData.bsdfWeight[1];
if (mtlData.bsdfWeight[0] > BSDF_WEIGHT_EPSILON)
{
BRDF::EvaluateDiffuse(mtlData, sampleDir, result.diffValue, result.diffPdf);
result.diffValue *= mtlData.bsdfData.ambientOcclusion * (1.0 - mtlData.bsdfData.transmittanceMask) * (1.0 - fresnelClearCoat);
result.diffPdf *= mtlData.bsdfWeight[0];
}
if (mtlData.bsdfWeight[2] > BSDF_WEIGHT_EPSILON)
{
BRDF::EvaluateAnisoGGX(mtlData, mtlData.bsdfData.fresnel0, sampleDir, value, pdf, fresnelSpec);
result.specValue += value * (1.0 - fresnelClearCoat) * GetSpecularCompensation(mtlData.bsdfData);
result.specPdf += mtlData.bsdfWeight[2] * pdf;
}
}
else if (inputSample.z < mtlData.bsdfWeight[0] + mtlData.bsdfWeight[1] + mtlData.bsdfWeight[2]) // Specular BRDF
{
if (!BRDF::SampleAnisoGGX(mtlData, mtlData.bsdfData.fresnel0, inputSample, sampleDir, result.specValue, result.specPdf, fresnelSpec))
return false;
result.specValue *= GetSpecularCompensation(mtlData.bsdfData);
result.specPdf *= mtlData.bsdfWeight[2];
if (mtlData.bsdfWeight[1] > BSDF_WEIGHT_EPSILON)
{
BRDF::EvaluateGGX(mtlData, CLEAR_COAT_ROUGHNESS, CLEAR_COAT_F0, sampleDir, value, pdf, fresnelClearCoat);
fresnelClearCoat *= mtlData.bsdfData.coatMask;
result.specValue = result.specValue * (1.0 - fresnelClearCoat) + value * mtlData.bsdfData.coatMask;
result.specPdf += mtlData.bsdfWeight[1] * pdf;
}
if (mtlData.bsdfWeight[0] > BSDF_WEIGHT_EPSILON)
{
BRDF::EvaluateDiffuse(mtlData, sampleDir, result.diffValue, result.diffPdf);
result.diffValue *= mtlData.bsdfData.ambientOcclusion * (1.0 - mtlData.bsdfData.transmittanceMask) * (1.0 - fresnelClearCoat);
result.diffPdf *= mtlData.bsdfWeight[0];
}
}
#ifdef _SURFACE_TYPE_TRANSPARENT
else // Specular BTDF
{
if (!BTDF::SampleAnisoGGX(mtlData, inputSample, sampleDir, result.specValue, result.specPdf))
return false;
#ifdef _REFRACTION_THIN
sampleDir = refract(sampleDir, mtlData.bsdfData.normalWS, mtlData.bsdfData.ior);
if (!any(sampleDir))
return false;
#endif
result.specValue *= mtlData.bsdfData.transmittanceMask;
result.specPdf *= mtlData.bsdfWeight[3];
}
#endif
#ifdef _MATERIAL_FEATURE_SUBSURFACE_SCATTERING
// We compensate for the fact that there is no spec when computing SSS
result.specValue /= mtlData.subsurfaceWeightFactor;
#endif
}
else // Below
{
#ifdef _SURFACE_TYPE_TRANSPARENT
#ifdef _REFRACTION_THIN
if (mtlData.bsdfData.transmittanceMask)
{
// Just go through (although we should not end up here)
sampleDir = -mtlData.V;
result.specValue = DELTA_PDF;
result.specPdf = DELTA_PDF;
}
#else
if (inputSample.z < mtlData.bsdfWeight[2]) // Specular BRDF
{
if (!BRDF::SampleDelta(mtlData, sampleDir, result.specValue, result.specPdf))
return false;
result.specPdf *= mtlData.bsdfWeight[2];
}
else // Specular BTDF
{
if (!BTDF::SampleDelta(mtlData, sampleDir, result.specValue, result.specPdf))
return false;
result.specPdf *= mtlData.bsdfWeight[3];
}
#endif
#else
return false;
#endif
}
return true;
}
void EvaluateMaterial(MaterialData mtlData, float3 sampleDir, out MaterialResult result)
{
Init(result);
#ifdef _MATERIAL_FEATURE_SUBSURFACE_SCATTERING
if (mtlData.isSubsurface)
{
BRDF::EvaluateLambert(mtlData, sampleDir, result.diffValue, result.diffPdf);
result.diffValue *= 1.0 - mtlData.bsdfData.transmittanceMask; // AO purposedly ignored here
return;
}
#endif
if (IsAbove(mtlData))
{
float3 value;
float pdf;
float fresnelSpec, fresnelClearCoat = 0.0;
if (mtlData.bsdfWeight[1] > BSDF_WEIGHT_EPSILON)
{
BRDF::EvaluateGGX(mtlData, CLEAR_COAT_ROUGHNESS, CLEAR_COAT_F0, sampleDir, result.specValue, result.specPdf, fresnelClearCoat);
fresnelClearCoat *= mtlData.bsdfData.coatMask;
result.specValue *= mtlData.bsdfData.coatMask;
result.specPdf *= mtlData.bsdfWeight[1];
}
if (mtlData.bsdfWeight[0] > BSDF_WEIGHT_EPSILON)
{
BRDF::EvaluateDiffuse(mtlData, sampleDir, result.diffValue, result.diffPdf);
result.diffValue *= (1.0 - mtlData.bsdfData.transmittanceMask) * (1.0 - fresnelClearCoat); // AO purposedly ignored here
result.diffPdf *= mtlData.bsdfWeight[0];
}
if (mtlData.bsdfWeight[2] > BSDF_WEIGHT_EPSILON)
{
BRDF::EvaluateAnisoGGX(mtlData, mtlData.bsdfData.fresnel0, sampleDir, value, pdf, fresnelSpec);
result.specValue += value * (1.0 - fresnelClearCoat) * GetSpecularCompensation(mtlData.bsdfData);
result.specPdf += mtlData.bsdfWeight[2] * pdf;
}
#ifdef _MATERIAL_FEATURE_SUBSURFACE_SCATTERING
// We compensate for the fact that there is no spec when computing SSS
result.specValue /= mtlData.subsurfaceWeightFactor;
#endif
}
}
float AdjustPathRoughness(MaterialData mtlData, MaterialResult mtlResult, bool isSampleBelow, float pathRoughness)
{
// Adjust the max roughness, based on the estimated diff/spec ratio
float adjustedPathRoughness = (mtlResult.specPdf * max(mtlData.bsdfData.roughnessT, mtlData.bsdfData.roughnessB) + mtlResult.diffPdf) / (mtlResult.diffPdf + mtlResult.specPdf);
#ifdef _SURFACE_TYPE_TRANSPARENT
// When transmitting with an IOR close to 1.0, roughness is barely noticeable -> take that into account for path roughness adjustment
if (IsBelow(mtlData) != isSampleBelow)
adjustedPathRoughness = lerp(pathRoughness, adjustedPathRoughness, smoothstep(1.0, 1.3, mtlData.bsdfData.ior));
#endif
return adjustedPathRoughness;
}
float3 ApplyAbsorption(MaterialData mtlData, float dist, bool isSampleBelow, float3 value)
{
#if defined(_SURFACE_TYPE_TRANSPARENT) && HAS_REFRACTION
// Apply absorption on rays below the interface, using Beer-Lambert's law
if (isSampleBelow)
{
#ifdef _REFRACTION_THIN
value *= exp(-mtlData.bsdfData.absorptionCoefficient * REFRACTION_THIN_DISTANCE);
#else
value *= exp(-mtlData.bsdfData.absorptionCoefficient * dist);
#endif
}
#endif
return value;
}