Skip to content

Commit

Permalink
[HDRP][Path Tracing] AxF material support (#4525)
Browse files Browse the repository at this point in the history
* Merge stacklit changes.

* Updated changelog.

* Updated docs

Removed Stacklit material limitation from Path tracing docs.,

* Fixed issue with NaNs generated on backfaces.

* Merged AxF base support, and SVBRDF implementation.

* Switched isoGGX to visible normal anisoGGX implementation.

* Refined SVBRDF and added Car Paint support.

* Updated changelog, and minor cosmetic changes.

* ...

* ...

* Updated couple test reference images (marginal changes).

* Changed test 1000 PT, which had also marginally changed.

Co-authored-by: Vic-Cooper <vic.cooper@unity3d.com>
  • Loading branch information
eturquin and Vic-Cooper authored May 13, 2021
1 parent b88f16e commit 8387320
Show file tree
Hide file tree
Showing 19 changed files with 684 additions and 87 deletions.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions com.unity.render-pipelines.high-definition/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
- Added the ability to animate many physical camera properties with Timeline.
- Added a mixed RayMarching/RayTracing mode for RTReflections and RTGI.
- Added path tracing support for stacklit material.
- Added path tracing support for AxF material.
- Added support for surface gradient based normal blending for decals.
- Added support for tessellation for all master node in shader graph.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ public struct SurfaceData
public Vector3 specularColor;

[SurfaceDataAttributes("Fresnel F0")]
public Vector3 fresnelF0;
public Vector3 fresnel0;

[SurfaceDataAttributes("Specular Lobe")]
public Vector3 specularLobe; // .xy for SVBRDF, .xyz for CARPAINT2, for _CarPaint2_CTSpreads per lobe roughnesses
Expand Down Expand Up @@ -152,12 +152,12 @@ public struct BSDFData
[SurfaceDataAttributes("", true)]
public Vector3 tangentWS;
[SurfaceDataAttributes("", true)]
public Vector3 biTangentWS;
public Vector3 bitangentWS;

// SVBRDF Variables
public Vector3 diffuseColor;
public Vector3 specularColor;
public Vector3 fresnelF0;
public Vector3 fresnel0;
public float perceptualRoughness; // approximated for SSAO
public Vector3 roughness; // .xy for SVBRDF, .xyz for CARPAINT2, for _CarPaint2_CTSpreads per lobe roughnesses
public float height_mm;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ struct SurfaceData
float3 tangentWS;
float3 diffuseColor;
float3 specularColor;
float3 fresnelF0;
float3 fresnel0;
float3 specularLobe;
float height_mm;
float anisotropyAngle;
Expand Down Expand Up @@ -128,10 +128,10 @@ struct BSDFData
float specularOcclusion;
float3 normalWS;
float3 tangentWS;
float3 biTangentWS;
float3 bitangentWS;
float3 diffuseColor;
float3 specularColor;
float3 fresnelF0;
float3 fresnel0;
float perceptualRoughness;
float3 roughness;
float height_mm;
Expand Down Expand Up @@ -188,7 +188,7 @@ void GetGeneratedSurfaceDataDebug(uint paramId, SurfaceData surfacedata, inout f
needLinearToSRGB = true;
break;
case DEBUGVIEW_AXF_SURFACEDATA_FRESNEL_F0:
result = surfacedata.fresnelF0;
result = surfacedata.fresnel0;
break;
case DEBUGVIEW_AXF_SURFACEDATA_SPECULAR_LOBE:
result = surfacedata.specularLobe;
Expand Down Expand Up @@ -282,7 +282,7 @@ void GetGeneratedBSDFDataDebug(uint paramId, BSDFData bsdfdata, inout float3 res
result = bsdfdata.tangentWS * 0.5 + 0.5;
break;
case DEBUGVIEW_AXF_BSDFDATA_BI_TANGENT_WS:
result = bsdfdata.biTangentWS * 0.5 + 0.5;
result = bsdfdata.bitangentWS * 0.5 + 0.5;
break;
case DEBUGVIEW_AXF_BSDFDATA_DIFFUSE_COLOR:
result = bsdfdata.diffuseColor;
Expand All @@ -291,7 +291,7 @@ void GetGeneratedBSDFDataDebug(uint paramId, BSDFData bsdfdata, inout float3 res
result = bsdfdata.specularColor;
break;
case DEBUGVIEW_AXF_BSDFDATA_FRESNEL_F0:
result = bsdfdata.fresnelF0;
result = bsdfdata.fresnel0;
break;
case DEBUGVIEW_AXF_BSDFDATA_PERCEPTUAL_ROUGHNESS:
result = bsdfdata.perceptualRoughness.xxx;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -880,7 +880,7 @@ float3 CarPaint_BTF(float thetaH, float thetaD, SurfaceData surfaceData, BSDFDa
// Base refers to the "base layer", ie not the coat if present.
float3 GetColorBaseFresnelF0(BSDFData bsdfData)
{
return bsdfData.fresnelF0.r * bsdfData.specularColor;
return bsdfData.fresnel0.r * bsdfData.specularColor;
}

// For raytracing fit to standard Lit:
Expand Down Expand Up @@ -1157,6 +1157,8 @@ float MultiLobesCookTorrance(BSDFData bsdfData, float NdotL, float NdotV, float

specularIntensity += coeff * CT_D(NdotH, spread) * CT_F(VdotH, F0);
}

// FIXME: should be 4 instead of PI at the denominator, this was a mistake in the original paper
specularIntensity *= G_CookTorrance(NdotH, NdotV, NdotL, VdotH) // Shadowing/Masking term
/ (PI * max(1e-3, NdotV * NdotL));

Expand Down Expand Up @@ -1212,7 +1214,7 @@ BSDFData ConvertSurfaceDataToBSDFData(uint2 positionSS, SurfaceData surfaceData)

bsdfData.normalWS = surfaceData.normalWS;
bsdfData.tangentWS = surfaceData.tangentWS;
bsdfData.biTangentWS = cross(bsdfData.normalWS, bsdfData.tangentWS);
bsdfData.bitangentWS = cross(bsdfData.normalWS, bsdfData.tangentWS);

bsdfData.roughness = 0;
// see AxFData.hlsl: important, this is used in PostEvaluateBSDF here and in AxFRayTracing
Expand All @@ -1222,7 +1224,7 @@ BSDFData ConvertSurfaceDataToBSDFData(uint2 positionSS, SurfaceData surfaceData)
bsdfData.diffuseColor = surfaceData.diffuseColor;
bsdfData.specularColor = surfaceData.specularColor;

bsdfData.fresnelF0 = surfaceData.fresnelF0; // See AxfData.hlsl: the actual sampled texture is always 1 channel, if we ever find otherwise, we will use the others.
bsdfData.fresnel0 = surfaceData.fresnel0; // See AxfData.hlsl: the actual sampled texture is always 1 channel, if we ever find otherwise, we will use the others.
bsdfData.height_mm = surfaceData.height_mm;

bsdfData.roughness.xy = HasAnisotropy() ? surfaceData.specularLobe.xy : surfaceData.specularLobe.xx;
Expand All @@ -1240,7 +1242,7 @@ BSDFData ConvertSurfaceDataToBSDFData(uint2 positionSS, SurfaceData surfaceData)
bsdfData.clearcoatNormalWS = HasClearcoat() ? surfaceData.clearcoatNormalWS : surfaceData.normalWS;

bsdfData.specularColor = GetCarPaintSpecularColor();
bsdfData.fresnelF0 = GetCarPaintFresnel0();
bsdfData.fresnel0 = GetCarPaintFresnel0();
bsdfData.roughness.xyz = surfaceData.specularLobe.xyz; // the later stores per lobe possibly modified (for geometric specular AA) _CarPaint2_CTSpreads
bsdfData.height_mm = 0;
#endif
Expand Down Expand Up @@ -1397,7 +1399,7 @@ PreLightData GetPreLightData(float3 viewWS_Clearcoat, PositionInputs posInput

// todo_fresnel: TOCHECK: Make BRDF and FGD for env. consistent with dirac lights for HasFresnelTerm() handling:
// currently, we only check it for Ward and its variants.
float3 tempF0 = HasFresnelTerm() ? bsdfData.fresnelF0.rrr : 1.0;
float3 tempF0 = HasFresnelTerm() ? bsdfData.fresnel0.rrr : 1.0;
tempF0 *= bsdfData.specularColor; // Important to use in the PreIntegratedFGD interpolated fetches!

float specularReflectivity;
Expand Down Expand Up @@ -1767,12 +1769,12 @@ float3 ComputeWard(float3 H, float LdotH, float NdotL, float NdotV, PreLightData
float F = 1.0;
switch (AXF_SVBRDF_BRDFVARIANTS_FRESNELTYPE)
{
case 1: F = F_FresnelDieletricSafe(Fresnel0ToIorSafe(bsdfData.fresnelF0.r), LdotH); break;
case 2: F = F_Schlick(bsdfData.fresnelF0.r, LdotH); break;
case 1: F = F_FresnelDieletricSafe(Fresnel0ToIorSafe(bsdfData.fresnel0.r), LdotH); break;
case 2: F = F_Schlick(bsdfData.fresnel0.r, LdotH); break;
}

// Evaluate normal distribution function
float3 tsH = float3(dot(H, bsdfData.tangentWS), dot(H, bsdfData.biTangentWS), dot(H, bsdfData.normalWS));
float3 tsH = float3(dot(H, bsdfData.tangentWS), dot(H, bsdfData.bitangentWS), dot(H, bsdfData.normalWS));
//float2 rotH = tsH.xy / tsH.z;
float2 rotH = tsH.xy / max(0.00001, tsH.z);
//float2 roughness = bsdfData.roughness.xy;
Expand Down Expand Up @@ -1804,7 +1806,7 @@ float3 ComputeBlinnPhong(float3 H, float LdotH, float NdotL, float NdotV, PreLi
float2 exponents = 2 * rcp(max(0.0001,(bsdfData.roughness.xy*bsdfData.roughness.xy))) - 2;

// Evaluate normal distribution function
float3 tsH = float3(dot(H, bsdfData.tangentWS), dot(H, bsdfData.biTangentWS), dot(H, bsdfData.normalWS));
float3 tsH = float3(dot(H, bsdfData.tangentWS), dot(H, bsdfData.bitangentWS), dot(H, bsdfData.normalWS));
float2 rotH = tsH.xy;

float3 N = 0;
Expand Down Expand Up @@ -1841,7 +1843,7 @@ float3 ComputeCookTorrance(float3 H, float LdotH, float NdotL, float NdotV, Pre
float sqNdotH = Sq(NdotH);

// Evaluate Fresnel term
float F = F_Schlick(bsdfData.fresnelF0.r, LdotH);
float F = F_Schlick(bsdfData.fresnel0.r, LdotH);

// Evaluate (isotropic) normal distribution function (Beckmann)
float roughness = GetScalarRoughnessFromAnisoRoughness(bsdfData.roughness.x, bsdfData.roughness.y);
Expand All @@ -1858,9 +1860,9 @@ float3 ComputeCookTorrance(float3 H, float LdotH, float NdotL, float NdotV, Pre
float3 ComputeGGX(float3 H, float LdotH, float NdotL, float NdotV, PreLightData preLightData, BSDFData bsdfData)
{
// Evaluate Fresnel term
float F = F_Schlick(bsdfData.fresnelF0.r, LdotH);
float F = F_Schlick(bsdfData.fresnel0.r, LdotH);

float3 tsH = float3(dot(H, bsdfData.tangentWS), dot(H, bsdfData.biTangentWS), dot(H, bsdfData.normalWS));
float3 tsH = float3(dot(H, bsdfData.tangentWS), dot(H, bsdfData.bitangentWS), dot(H, bsdfData.normalWS));

// Evaluate normal distribution function (Trowbridge-Reitz)
float N = D_GGXAniso(tsH.x, tsH.y, tsH.z, bsdfData.roughness.x, bsdfData.roughness.y);
Expand Down Expand Up @@ -2050,7 +2052,7 @@ CBSDF EvaluateBSDF(float3 viewWS_Clearcoat, float3 lightWS_Clearcoat, PreLightDa

// Apply flakes
//TODO_FLAKES
specularTerm += CarPaint_BTF(thetaH, thetaD, (SurfaceData)0, bsdfData, /*useBSDFData:*/true);;
specularTerm += CarPaint_BTF(thetaH, thetaD, (SurfaceData)0, bsdfData, /*useBSDFData:*/true);

cbsdf.diffR = clearcoatExtinction * diffuseTerm * saturate(NdotL);
cbsdf.specR = (clearcoatExtinction * specularTerm * saturate(NdotL) + clearcoatReflectionLobeNdotL);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -757,6 +757,43 @@ Shader "HDRP/AxF"

ENDHLSL
}

Pass
{
Name "PathTracingDXR"
Tags{ "LightMode" = "PathTracingDXR" }

HLSLPROGRAM

#pragma only_renderers d3d11
#pragma raytracing surface_shader

#pragma multi_compile _ DEBUG_DISPLAY

#define SHADERPASS SHADERPASS_PATH_TRACING

// This is just because it needs to be defined, shadow maps are not used.
#define SHADOW_LOW

#include "Packages/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/Raytracing/Shaders/RaytracingMacros.hlsl"

#include "Packages/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/Raytracing/Shaders/ShaderVariablesRaytracing.hlsl"
#include "Packages/com.unity.render-pipelines.high-definition/Runtime/Material/Material.hlsl"
#include "Packages/com.unity.render-pipelines.high-definition/Runtime/Lighting/Lighting.hlsl"

#include "Packages/com.unity.render-pipelines.high-definition/Runtime/Material/AxF/ShaderPass/AxFSharePass.hlsl"
#include "Packages/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/Raytracing/Shaders/RaytracingIntersection.hlsl"

#include "Packages/com.unity.render-pipelines.high-definition/Runtime/Lighting/LightLoop/LightLoopDef.hlsl"
#define HAS_LIGHTLOOP
#include "Packages/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/Raytracing/Shaders/RayTracingCommon.hlsl"
#include "Packages/com.unity.render-pipelines.high-definition/Runtime/Material/AxF/AxF.hlsl"
#include "Packages/com.unity.render-pipelines.high-definition/Runtime/Material/AxF/AxFData.hlsl"
#include "Packages/com.unity.render-pipelines.high-definition/Runtime/Material/AxF/AxFPathTracing.hlsl"
#include "Packages/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/ShaderPass/ShaderPassPathTracing.hlsl"

ENDHLSL
}
}

CustomEditor "Rendering.HighDefinition.AxFGUI"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -636,7 +636,7 @@ void GetSurfaceAndBuiltinData(FragInputs input, float3 V, inout PositionInputs p

// The AxF models include both a general coloring term that they call "specular color" while the f0 is actually another term,
// seemingly always scalar:
surfaceData.fresnelF0 = AXF_SAMPLE_SMP_TEXTURE2D(_SVBRDF_FresnelMap, sampler_SVBRDF_FresnelMap, uvMapping).x;
surfaceData.fresnel0 = AXF_SAMPLE_SMP_TEXTURE2D(_SVBRDF_FresnelMap, sampler_SVBRDF_FresnelMap, uvMapping).x;
surfaceData.height_mm = AXF_SAMPLE_SMP_TEXTURE2D(_SVBRDF_HeightMap, sampler_SVBRDF_HeightMap, uvMapping).x * _SVBRDF_HeightMapMaxMM;
// Our importer range remaps the [-HALF_PI, HALF_PI) range to [0,1). We map back here:
surfaceData.anisotropyAngle =
Expand Down Expand Up @@ -672,7 +672,7 @@ void GetSurfaceAndBuiltinData(FragInputs input, float3 V, inout PositionInputs p

// Useless for car paint BSDF
surfaceData.specularColor = 0;
surfaceData.fresnelF0 = 0;
surfaceData.fresnel0 = 0;
surfaceData.height_mm = 0;
surfaceData.anisotropyAngle = 0;
#endif
Expand Down Expand Up @@ -739,7 +739,7 @@ void GetSurfaceAndBuiltinData(FragInputs input, float3 V, inout PositionInputs p
surfaceData.tangentWS = Orthonormalize(surfaceData.tangentWS, surfaceData.normalWS);

// Instead of
// surfaceData.biTangentWS = Orthonormalize(input.tangentToWorld[1], surfaceData.normalWS),
// surfaceData.bitangentWS = Orthonormalize(input.tangentToWorld[1], surfaceData.normalWS),
// make AxF follow what we do in other HDRP shaders for consistency: use the
// cross product to finish building the TBN frame and thus get a frame matching
// the handedness of the world space (tangentToWorld can be passed right handed while
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
#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"

// AxF Material Data:
//
// bsdfWeight0 Diffuse BRDF
// bsdfWeight1 Clearoat BRDF
// bsdfWeight2 Specular BRDF(s)

float3 GetCoatNormal(MaterialData mtlData)
{
return mtlData.bsdfData.clearcoatNormalWS;
}

#ifdef _AXF_BRDF_TYPE_CAR_PAINT
float GetSpecularCoeffSum(MaterialData mtlData)
{
return mtlData.bsdfData.height_mm;
}
#endif

void ProcessBSDFData(PathIntersection pathIntersection, BuiltinData builtinData, MaterialData mtlData, inout BSDFData bsdfData)
{
// Adjust roughness to reduce fireflies
bsdfData.roughness.x = max(pathIntersection.maxRoughness, bsdfData.roughness.x);
bsdfData.roughness.y = max(pathIntersection.maxRoughness, bsdfData.roughness.y);
#ifdef _AXF_BRDF_TYPE_CAR_PAINT
bsdfData.roughness.z = max(pathIntersection.maxRoughness, bsdfData.roughness.z);
#endif

// One of the killer features of AxF, optional specular Fresnel...
if (!HasFresnelTerm())
bsdfData.fresnel0 = 1.0;

// Make sure we can get valid coat normal reflection directions
if (HasClearcoat())
bsdfData.clearcoatNormalWS = ComputeConsistentShadingNormal(mtlData.V, bsdfData.geomNormalWS, bsdfData.clearcoatNormalWS);

#ifdef _AXF_BRDF_TYPE_CAR_PAINT
// We hijack height_mm, as it is not used here otherwise, to store the specular coefficients sum
bsdfData.height_mm = 0.0;
UNITY_UNROLL
for (uint i = 0; i < CARPAINT2_LOBE_COUNT; i++)
bsdfData.height_mm += _CarPaint2_CTCoeffs[i];
#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.V = -WorldRayDirection();
mtlData.Nv = ComputeConsistentShadingNormal(mtlData.V, bsdfData.geomNormalWS, bsdfData.normalWS);
mtlData.bsdfData = bsdfData;
ProcessBSDFData(pathIntersection, builtinData, mtlData, mtlData.bsdfData);

mtlData.bsdfWeight = 0.0;

// First determine if our incoming direction V is above (exterior) or below (interior) the surface
if (IsAbove(mtlData))
{
float NcoatdotV = dot(GetCoatNormal(mtlData), mtlData.V);
float NspecdotV = dot(GetSpecularNormal(mtlData), mtlData.V);
float Fcoat = F_Schlick(IorToFresnel0(bsdfData.clearcoatIOR), NcoatdotV);
float Fspec = Luminance(F_Schlick(mtlData.bsdfData.fresnel0, NspecdotV));

#if defined(_AXF_BRDF_TYPE_SVBRDF)
float specularCoeff = Luminance(mtlData.bsdfData.specularColor);
#elif defined(_AXF_BRDF_TYPE_CAR_PAINT)
float specularCoeff = GetSpecularCoeffSum(mtlData);
#endif

mtlData.bsdfWeight[1] = HasClearcoat() ? Fcoat * Luminance(mtlData.bsdfData.clearcoatColor) : 0.0;
float clearcoatTransmission = HasClearcoat() ? 1.0 - Fcoat : 1.0;
mtlData.bsdfWeight[2] = clearcoatTransmission * lerp(Fspec, 0.5, GetScalarRoughness(mtlData.bsdfData.roughness)) * specularCoeff;
mtlData.bsdfWeight[0] = clearcoatTransmission * Luminance(mtlData.bsdfData.diffuseColor) * mtlData.bsdfData.ambientOcclusion;
}

// Normalize the weights
float wSum = mtlData.bsdfWeight[0] + mtlData.bsdfWeight[1] + mtlData.bsdfWeight[2];

if (wSum < BSDF_WEIGHT_EPSILON)
return false;

mtlData.bsdfWeight /= wSum;

return true;
}

#if defined(_AXF_BRDF_TYPE_SVBRDF)
# include "Packages/com.unity.render-pipelines.high-definition/Runtime/Material/AxF/AxFPathTracingSVBRDF.hlsl"
#elif defined(_AXF_BRDF_TYPE_CAR_PAINT)
# include "Packages/com.unity.render-pipelines.high-definition/Runtime/Material/AxF/AxFPathTracingCarPaint.hlsl"
#endif

float3 GetLightNormal(MaterialData mtlData)
{
// If both diffuse and specular normals are quasi-indentical, return one of them, otherwise return a null vector
return dot(GetDiffuseNormal(mtlData), GetSpecularNormal(mtlData)) > 0.99 ? GetDiffuseNormal(mtlData) : float3(0.0, 0.0, 0.0);
}

float AdjustPathRoughness(MaterialData mtlData, MaterialResult mtlResult, bool isSampleBelow, float pathRoughness)
{
// Adjust the max roughness, based on the estimated diff/spec ratio
float maxSpecRoughness = Max3(mtlData.bsdfData.roughness.x, mtlData.bsdfData.roughness.y, mtlData.bsdfData.roughness.z);
float adjustedPathRoughness = (mtlResult.specPdf * maxSpecRoughness + mtlResult.diffPdf) / (mtlResult.diffPdf + mtlResult.specPdf);

return adjustedPathRoughness;
}

float3 ApplyAbsorption(MaterialData mtlData, SurfaceData surfaceData, float dist, bool isSampleBelow, float3 value)
{
return value;
}
Loading

0 comments on commit 8387320

Please sign in to comment.