Files
UnrealEngine/Engine/Shaders/Private/HairStrands/HairStrandsEnvironmentLightingCommon.ush
2025-05-18 13:04:45 +08:00

92 lines
3.1 KiB
HLSL

// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
// Roughness remapping functions of each term of the hair BSDF to the default lit model (GGX based)
// This remapping is done based on the hair longitudinal component. This means it does not take into
// around the anistotropic streatching which happen along the azimuthal plane
float HairToGGXRoughness_R(float R_Roughness)
{
const float X = saturate(R_Roughness) - 0.51f;
return clamp(X, 0.1f, 1);
}
float HairToGGXRoughness_TRT(float R_Roughness)
{
const float X = saturate(R_Roughness);
const float X2 = X * X;
const float X3 = X2 * X;
const float X4 = X2 * X2;
const float A = -0.207704f;
const float B = 6.95267f;
const float C = -10.1783f;
const float D = 4.24751f;
const float F = 3.37235f;
const float G = -1.96947f;
return (A * X + B * X2 + C * X3 + D * X4) / (G * X + F * X2);
}
float HairToGGXRoughness_TT(float TT_Roughness)
{
const float X = saturate(TT_Roughness);
const float X2 = X * X;
const float X3 = X2 * X;
const float A = 0.70505f;
const float B = -0.117104f;
const float C = 0.0259047f;
return A * X + B * X2 + C * X3;
}
float4 ComputeRandom4(uint2 PixelPosition, uint SampleIt, uint SampleCount, uint FrameIdMod8)
{
// Shader compiler code is pretty good at moving this out of any for loops.
const uint2 Seed0 = Rand3DPCG16(int3(PixelPosition, FrameIdMod8)).xy;
const uint2 Seed1 = Rand3DPCG16(int3(PixelPosition + 17, FrameIdMod8)).xy;
return float4(
Hammersley16(SampleIt, SampleCount, Seed0),
Hammersley16(SampleIt, SampleCount, Seed1));
}
// Compute a sample bias by adding a constant bias (3d jitter within a voxel) + a directional offset (jitter along the direction).
// Both constant & directional bias are scaled with the size of a voxel
float3 ComputePositionBias(float3 SampleDirection, float3 SampleOffset, float VoxelWorldSize, float DepthBiasScale)
{
const float3 Bias_Constant = SampleOffset;
const float3 Bias_Directional = SampleDirection * DepthBiasScale;
return VoxelWorldSize * (Bias_Constant + Bias_Directional);
}
#ifndef USE_HAIR_COMPLEX_TRANSMITTANCE
#define USE_HAIR_COMPLEX_TRANSMITTANCE 0
#endif
#if USE_HAIR_COMPLEX_TRANSMITTANCE
#include "HairStrandsCommon.ush"
#include "HairStrandsDeepTransmittanceCommon.ush"
#include "HairStrandsDeepTransmittanceDualScattering.ush"
#endif
float3 EvaluateEnvHair(FGBufferData GBuffer, float3 V, float3 N, inout float3 OutL)
{
const float Shadow = 1;
const float Backlit = 0;
const float Area = 0.2f;
const uint2 Random = uint2(0, 0);
// Evaluate the hair BSDF for a imaginary reflected direction, and uses it a measure of the directional albedo
// Use this value to reweight the spherical integration. This is not correct, but give plausible result.
// To ensure there is no energy added, clamp the BSDF evaluation to 1.
OutL = normalize(V - N * dot(V, N));
FHairTransmittanceData TransmittanceData = InitHairTransmittanceData(true);
#if USE_HAIR_COMPLEX_TRANSMITTANCE
if (ShouldUseHairComplexTransmittance(GBuffer))
{
TransmittanceData = EvaluateDualScattering(GBuffer, V, OutL.xyz);
}
#endif
return min(1.f, 2 * PI * HairShading(GBuffer, OutL, V, N, Shadow, TransmittanceData, Backlit, Area, Random));
}