You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

180 lines
7.1 KiB

// Dual scattering was added to the path tracer to supplement the development of our rasterized approximation of it.
// Currently we compile all of it away like this in the shipped HDRP as we do not actually recommend it for achieving
// multiple scattering for hair in the path tracer. For that, the user should instead set a high bounce count on the volume
// setting and ensure that their hair strands are a typical width (~0.12 millimeters).
// #define _PATH_TRACED_DUAL_SCATTERING
#include "Packages/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/PathTracing/Shaders/PathTracingPayload.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"
#include "Packages/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/PathTracing/Shaders/PathTracingAOV.hlsl"
#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/SpaceFillingCurves.hlsl"
#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Random.hlsl"
// https://www.pbrt.org/hair.pdf
float2 DemuxFloat(float x)
{
uint64_t v = x * (((uint64_t)1) << 32);
uint2 bits = uint2(Compact1By1((uint)v), Compact1By1((uint)(v >> 1)));
return float2(bits.x / float(1 << 16),
bits.y / float(1 << 16));
}
// --------------------------------------------------------------------------------------
void ProcessBSDFData(PathPayload payload, BuiltinData builtinData, inout BSDFData bsdfData)
{
// NOTE: Currently we don't support ray-aligned ribbons in the acceleration structure, so our only H-calculation routines
// are either stochastic or derived from a tube intersection.
#if 0
bsdfData.h = GetHFromTube(-WorldRayDirection(), bsdfData.normalWS, bsdfData.hairStrandDirectionWS);
#else
bsdfData.h = -1 + 2 * InterleavedGradientNoise(payload.pixelCoord, _RaytracingSampleIndex);
#endif
// Do not use the spline visibility term when path tracing.
bsdfData.visibility = -1;
}
bool CreateMaterialData(PathPayload payload, BuiltinData builtinData, BSDFData bsdfData, inout float3 shadingPosition, inout float theSample, out MaterialData mtlData)
{
// Kajiya not supported.
if (HasFlag(bsdfData.materialFeatures, MATERIALFEATUREFLAGS_HAIR_KAJIYA_KAY))
return false;
// Alter values in the material's bsdfData struct, to better suit path tracing
mtlData.bsdfData = bsdfData;
ProcessBSDFData(payload, builtinData, mtlData.bsdfData);
mtlData.bsdfWeight = 0.0;
mtlData.V = -WorldRayDirection();
#ifdef _PATH_TRACED_DUAL_SCATTERING
mtlData.positionWS = shadingPosition;
#endif
return true;
}
#ifdef _PATH_TRACED_DUAL_SCATTERING
float3 ComputeDualScatteringRayShooting(MaterialData mtlData, float3 sampleDir, inout float3 Fs)
{
// Initialize the strand count payload
PathPayload strandCountPayload;
strandCountPayload.segmentID = SEGMENT_ID_DUAL_SCATTERING;
strandCountPayload.alpha = 0.0; // Encode the intersected strand count in alpha.
// Initialize the shadow ray
RayDesc ray;
ray.Origin = mtlData.positionWS + mtlData.bsdfData.geomNormalWS * _RayTracingRayBias;
ray.Direction = sampleDir;
ray.TMin = 0.0;
ray.TMax = 10.0; // TODO: Dist to light
// Shoot a ray that counts the number of intersected strands
TraceRay(_RaytracingAccelerationStructure, RAY_FLAG_FORCE_NON_OPAQUE | RAY_FLAG_SKIP_CLOSEST_HIT_SHADER,
RAYTRACINGRENDERERFLAG_PATH_TRACING, 0, 1, 1, ray, strandCountPayload);
// Retrieve angles via spherical coordinates in the hair shading space.
HairAngle angles;
ZERO_INITIALIZE(HairAngle, angles);
// Transform to the local frame for spherical coordinates,
// Note that the strand direction is assumed to lie pointing down the +X axis.
const float3x3 frame = GetLocalFrame(mtlData.bsdfData.normalWS, mtlData.bsdfData.hairStrandDirectionWS);
const float3 wi = mul(sampleDir, transpose(frame));
const float3 wo = mul(mtlData.V, transpose(frame));
GetHairAngleLocal(wo, wi, angles);
// Pass along the strand count to the general dual scattering computation
return ComputeDualScattering(mtlData.bsdfData, angles, strandCountPayload.alpha, Fs);
}
#endif
void EvaluateMaterial(MaterialData mtlData, float3 sampleDir, out MaterialResult result)
{
Init(result);
// Transform to the local frame for spherical coordinates,
// Note that the strand direction is assumed to lie pointing down the X axis, as this is expected by the BSDF.
float3x3 frame = GetLocalFrame(mtlData.bsdfData.normalWS, mtlData.bsdfData.hairStrandDirectionWS);
float3 wo = mul(sampleDir, transpose(frame));
float3 wi = mul(mtlData.V, transpose(frame));
CBSDF cbsdf = EvaluateHairReference(wo, wi, mtlData.bsdfData);
result.specValue = cbsdf.specR * abs(wi.z);
#ifdef _PATH_TRACED_DUAL_SCATTERING
// Dual-scattering approximation rather than brute-forcing with a high path depth.
result.specValue = ComputeDualScatteringRayShooting(mtlData, sampleDir, result.specValue);
#endif
}
bool SampleMaterial(MaterialData mtlData, float3 inputSample, out float3 sampleDir, out MaterialResult result)
{
Init(result);
#ifdef _PATH_TRACED_DUAL_SCATTERING
// Do not evaluate more bounces if we are computing dual scattering for hair.
return false;
#endif
#if 0
// We sample the sphere due to reflective and transmittive events.
sampleDir = SampleSphereUniform(inputSample.x, inputSample.y);
EvaluateMaterial(mtlData, sampleDir, result);
result.specPdf = INV_FOUR_PI;
#else
// Transform to the local frame for spherical coordinates,
// Note that the strand direction is assumed to lie pointing down the X axis, as this is expected by the BSDF.
float3x3 frame = GetLocalFrame(mtlData.bsdfData.normalWS, mtlData.bsdfData.hairStrandDirectionWS);
float3 wo = mul(mtlData.V, transpose(frame));
// Need four random samples, derive two extra ones from the given third.
float4 u = float4(
inputSample.xy,
DemuxFloat(inputSample.z)
);
CBSDF cbsdf = SampleHairReference(wo, sampleDir, result.specPdf, u, mtlData.bsdfData);
result.specValue = cbsdf.specR * abs(sampleDir.z);
// Transform back into world space.
sampleDir = mul(sampleDir, frame);
#endif
return true;
}
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)
{
// TODO
return pathRoughness;
}
float3 GetMaterialAbsorption(MaterialData mtlData, SurfaceData surfaceData, float dist, bool isSampleBelow)
{
// TODO
return 1.0;
}
void GetAOVData(BSDFData bsdfData, out AOVData aovData)
{
aovData.albedo = bsdfData.diffuseColor;
aovData.normal = bsdfData.normalWS;
}