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

154 lines
5.7 KiB
HLSL

// Copyright Epic Games, Inc. All Rights Reserved.
#include "../Common.ush"
#include "../MonteCarlo.ush"
#include "../DeferredShadingCommon.ush"
#include "../PositionReconstructionCommon.ush"
#include "../HairShadingCommon.ush"
#include "../HairBsdf.ush"
#include "HairStrandsEnvironmentLightingCommon.ush"
#include "../Substrate/Substrate.ush"
#define VOXEL_TRAVERSAL_TYPE VOXEL_TRAVERSAL_LINEAR_MIPMAP
#define VOXEL_TRAVERSAL_DEBUG 0
#include "HairStrandsVoxelPageTraversal.ush"
Texture2D<uint4> HairCategorizationTexture;
uint Voxel_MacroGroupId;
float Voxel_TanConeAngle;
float AO_Power;
float AO_Intensity;
uint AO_SampleCount;
float AO_DistanceThreshold;
uint Output_bHalfRes;
float2 Output_InvResolution;
float TraceAO(float3 TranslatedWorldPosition, float3 WorldNormal, float2 SvPosition, float PixelRadius)
{
const bool bDebugEnabled = all(uint2(SvPosition) == uint2(GetCursorPos()));
float3 UnoccludedN = 0;
uint TotalHairCount = 0;
#if PERMUTATION_SAMPLESET == 0
// Poisson disk position http://developer.download.nvidia.com/whitepapers/2008/PCSS_Integration.pdf
float2 PoissonDisk[16] =
{
float2(-0.94201624, -0.39906216),
float2(0.94558609, -0.76890725),
float2(-0.094184101, -0.92938870),
float2(0.34495938, 0.29387760),
float2(-0.91588581, 0.45771432),
float2(-0.81544232, -0.87912464),
float2(-0.38277543, 0.27676845),
float2(0.97484398, 0.75648379),
float2(0.44323325, -0.97511554),
float2(0.53742981, -0.47373420),
float2(-0.26496911, -0.41893023),
float2(0.79197514, 0.19090188),
float2(-0.24188840, 0.99706507),
float2(-0.81409955, 0.91437590),
float2(0.19984126, 0.78641367),
float2(0.14383161, -0.14100790)
};
const uint SampleCount = clamp(AO_SampleCount, 1u, 16u);
#else
const uint SampleCount = max(AO_SampleCount, 1u);
#endif
FVirtualVoxelCommonDesc CommonDesc;
CommonDesc.PageCountResolution = VirtualVoxel.PageCountResolution;
CommonDesc.PageTextureResolution = VirtualVoxel.PageTextureResolution;
CommonDesc.PageResolution = VirtualVoxel.PageResolution;
CommonDesc.PageResolutionLog2 = VirtualVoxel.PageResolutionLog2;
const FVirtualVoxelNodeDesc NodeDesc = UnpackVoxelNode(VirtualVoxel.NodeDescBuffer[Voxel_MacroGroupId], VirtualVoxel.PageResolution);
FHairTraversalSettings TraversalSettings = InitHairTraversalSettings();
TraversalSettings.DensityScale = VirtualVoxel.DensityScale_AO;
TraversalSettings.CountThreshold = 1;
TraversalSettings.DistanceThreshold = AO_DistanceThreshold;
TraversalSettings.bDebugEnabled = bDebugEnabled;
TraversalSettings.SteppingScale = VirtualVoxel.SteppingScale_Environment;
TraversalSettings.bUseOpaqueVisibility = false;
const float VoxelAOMaxDistance = 1000.0;
float3 BentNormal = 0;
float Visibility = 0;
for (uint SampleIt = 0; SampleIt < SampleCount; ++SampleIt)
{
const float4 Random4 = ComputeRandom4(SvPosition, SampleIt, SampleCount, View.StateFrameIndexMod8);
const float3 LocalL = CosineSampleHemisphere(frac(Random4.xy)).xyz;
const float3 SampleL = TangentToWorld(LocalL.xyz, WorldNormal);
const float3 SampleBias = ComputePositionBias(SampleL, SampleL * Random4.z, NodeDesc.VoxelWorldSize, VirtualVoxel.DepthBiasScale_Environment);
// Depth bias
const float3 SampleTranslatedWorldPosition = TranslatedWorldPosition + SampleBias;
const float3 SampleTranslatedLightPosition = SampleTranslatedWorldPosition + SampleL * VoxelAOMaxDistance;
// Compute the number of hair count between light & shading point
const FHairTraversalResult Result = ComputeHairCountVirtualVoxel(
SampleTranslatedWorldPosition,
SampleTranslatedLightPosition,
CommonDesc,
NodeDesc,
VirtualVoxel.PageIndexBuffer,
VirtualVoxel.PageTexture,
TraversalSettings);
const float StepVisibility = saturate(1 - Result.HairCount) * Result.Visibility;
BentNormal += SampleL * StepVisibility;
Visibility += StepVisibility;
}
Visibility /= SampleCount;
BentNormal /= SampleCount;
// User adjust AO
const float AO = 1 - (1 - pow(saturate(Visibility), AO_Power)) * AO_Intensity;
return saturate(AO);
}
void MainPS(
in float4 SvPosition : SV_Position,
out float4 OutAO : SV_Target0)
{
const bool bHalfRes = true; // Output_bHalfRes > 0;
float2 BufferUV = SvPositionToBufferUV(SvPosition);
if (bHalfRes > 0)
{
BufferUV = SvPosition.xy * Output_InvResolution;
}
const uint2 PixelCoord = floor(SvPosition.xy);
#if SUBTRATE_GBUFFER_FORMAT==1
FSubstrateAddressing SubstrateAddressing = GetSubstratePixelDataByteOffset(PixelCoord, uint2(View.BufferSizeAndInvSize.xy), Substrate.MaxBytesPerPixel);
FSubstratePixelHeader SubstratePixelHeader = UnpackSubstrateHeaderIn(Substrate.MaterialTextureArray, SubstrateAddressing, Substrate.TopLayerTexture);
const FSubstrateTopLayerData TopLayerData = SubstrateUnpackTopLayerData(Substrate.TopLayerTexture.Load(uint3(PixelCoord, 0)));
const bool bIsValid = SubstratePixelHeader.ClosureCount > 0;
const float3 WorldNormal = TopLayerData.WorldNormal;
#else
// Trace AO for all pixel which are partially covered or not covered at all.
// Fully covered pixel are marked as unlit
const FGBufferData GBuffer = GetGBufferDataFromSceneTextures(BufferUV);
const bool bIsValid = GBuffer.ShadingModelID != SHADINGMODELID_UNLIT;
const float3 WorldNormal = GBuffer.WorldNormal;
#endif
OutAO = 1;
if (bIsValid)
{
const float DeviceZ = SampleDeviceZFromSceneTextures(BufferUV);
const float SceneDepth = ConvertFromDeviceZ(DeviceZ);
const float3 TranslatedWorldPosition = ReconstructTranslatedWorldPositionFromDepth(BufferUV, SceneDepth);
const float PixelRadius = ConvertGivenDepthRadiusForProjectionType(VirtualVoxel.HairCoveragePixelRadiusAtDepth1, SceneDepth);
OutAO = TraceAO(TranslatedWorldPosition, WorldNormal, SvPosition.xy, PixelRadius);
}
else
{
discard;
}
}