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

408 lines
11 KiB
HLSL

// Copyright Epic Games, Inc. All Rights Reserved.
#include "../Common.ush"
#include "../SceneTexturesCommon.ush"
#include "../DeferredShadingCommon.ush"
#include "HairStrandsVisibilityCommon.ush"
////////////////////////////////////////////////////////////////////////////////////////////////
#if SHADER_FASTRESOLVE_MASK
Texture2D<uint> ResolveMaskTexture;
void FastResolvePS(in FScreenVertexOutput Input)
{
const uint2 PixelCoord = floor(Input.Position.xy);
const bool bNeedFastResolve = ResolveMaskTexture.Load(uint3(PixelCoord, 0)) > 0;
if (!bNeedFastResolve)
discard;
}
#endif // SHADER_FASTRESOLVE_MASK
////////////////////////////////////////////////////////////////////////////////////////////////
#if SHADER_COMPOSE_SAMPLE || SHADER_HOLDOUT
#include "../HeightFogCommon.ush"
#include "../SkyAtmosphereCommon.ush"
float4 EvaluateVolumetric(float3 WorldPosition, float SceneDepth)
{
const float3 CameraRelative_WorldPosition = WorldPosition - DFHackToFloat(PrimaryView.WorldCameraOrigin);
float4 HeightFogging = CalculateHeightFog(CameraRelative_WorldPosition);
float4 Fogging = HeightFogging;
if (FogStruct.ApplyVolumetricFog > 0)
{
const uint EyeIndex = 0;
float3 VolumeUV = ComputeVolumeUV_DEPRECATED(WorldPosition, DFHackToFloat(PrimaryView.WorldToClip));
Fogging = CombineVolumetricFog(HeightFogging, VolumeUV, EyeIndex, SceneDepth);
}
Fogging.rgb *= View.PreExposure;
if (View.SkyAtmosphereApplyCameraAerialPerspectiveVolume > 0.0f)
{
float4 NDCPosition = mul(float4(WorldPosition.xyz, 1), DFHackToFloat(PrimaryView.WorldToClip));
// Sample the aerial perspective (AP).
Fogging = GetAerialPerspectiveLuminanceTransmittanceWithFogOver(
View.RealTimeReflectionCapture,
View.SkyAtmosphereCameraAerialPerspectiveVolumeSizeAndInvSize,
NDCPosition,
CameraRelative_WorldPosition * CM_TO_SKY_UNIT,
View.CameraAerialPerspectiveVolume,
View.CameraAerialPerspectiveVolumeSampler,
View.SkyAtmosphereCameraAerialPerspectiveVolumeDepthResolutionInv,
View.SkyAtmosphereCameraAerialPerspectiveVolumeDepthResolution,
View.SkyAtmosphereAerialPerspectiveStartDepthKm,
View.SkyAtmosphereCameraAerialPerspectiveVolumeDepthSliceLengthKm,
View.SkyAtmosphereCameraAerialPerspectiveVolumeDepthSliceLengthKmInv,
View.OneOverPreExposure,
Fogging);
}
return Fogging;
}
#endif // SHADER_COMPOSE_SAMPLE || SHADER_HOLDOUT
////////////////////////////////////////////////////////////////////////////////////////////////
#if SHADER_COMPOSE_SAMPLE
int2 OutputResolution;
uint bComposeDofDepth;
uint bHasHoldout;
Texture2D<float> HairDOFDepthTexture;
Texture2D<float4> HairLightingSampleBuffer;
void ComposeSamplePS(
in FScreenVertexOutput Input,
out float4 OutColor : SV_Target0,
out float OutDepth : SV_DEPTH)
#if PERMUTATION_DEBUG == 0
{
OutColor = 0.0f;
const uint3 PixelCoord = uint3(floor(Input.Position.xy), 0);
const float HairDeviceZ = HairStrands.HairOnlyDepthTexture.Load(PixelCoord).x;
const FNodeDesc NodeDesc = DecodeNodeDesc(HairStrands.HairSampleOffset.Load(PixelCoord));
if (NodeDesc.Count == 0)
{
discard;
}
const float PixelCoverage = min(HairStrands.HairCoverageTexture.Load(PixelCoord), 1);
if (PixelCoverage == 0)
{
discard;
}
const uint TotalNodeCount = HairStrands.HairSampleCount[0];
const uint2 Resolution = GetHairSampleResolution(TotalNodeCount);
const float3 ClosetPointWorldPosition = DFHackToFloat(SvPositionToWorld(float4(Input.Position.xy, HairDeviceZ, 1.0)));
const float4 Volumetric = EvaluateVolumetric(ClosetPointWorldPosition, ConvertFromDeviceZ(HairDeviceZ));
bool bHoldout = false;
float3 LocalAccColor = 0;
LOOP
for (uint SampleIt = 0; SampleIt < NodeDesc.Count; SampleIt++)
{
#if SUPPORT_PRIMITIVE_ALPHA_HOLDOUT
if (bHasHoldout)
{
const FHairSample Sample = UnpackHairSample(HairStrands.HairSampleData[NodeDesc.Offset + SampleIt]);
bHoldout = bHoldout || HasHairFlags(Sample.Flags, HAIR_FLAGS_HOLDOUT);
}
#endif
const uint LinearCoord = NodeDesc.Offset + SampleIt;
const uint2 Coord = GetHairSampleCoord(LinearCoord, Resolution);
LocalAccColor += HairLightingSampleBuffer.Load(uint3(Coord, 0)).xyz;
}
// If any sample support holdout, then the lighting contribution is set to 0 to mask out the current hair pixel
// but fog from camera to hair pixel needs to be accounted for
if (bHoldout)
{
LocalAccColor = 0;
}
OutColor.rgb = (LocalAccColor * Volumetric.a + Volumetric.rgb) * PixelCoverage;
OutColor.a = PixelCoverage;
OutDepth = HairDeviceZ;
if (bComposeDofDepth)
{
OutDepth = HairDOFDepthTexture.Load(PixelCoord);
}
}
#else // PERMUTATION_DEBUG == 1
{
OutColor = 0.0f;
const uint3 PixelCoord = uint3(floor(Input.Position.xy), 0);
const FNodeDesc NodeDesc = DecodeNodeDesc(HairStrands.HairSampleOffset.Load(PixelCoord));
if (NodeDesc.Count == 0)
{
discard;
}
const float PixelCoverage = min(HairStrands.HairCoverageTexture.Load(PixelCoord), 1);
if (PixelCoverage == 0)
discard;
const float HairDeviceZ = HairStrands.HairOnlyDepthTexture.Load(PixelCoord).x;
float3 LocalAccColor = 0;
LOOP
for (uint SampleIt = 0; SampleIt < NodeDesc.Count; SampleIt++)
{
const FPackedHairSample PackedSample = HairStrands.HairSampleData[NodeDesc.Offset + SampleIt];
const FHairSample Sample = UnpackHairSample(PackedSample);
const float LocalCoverage = From8bitCoverage(Sample.Coverage8bit);
LocalAccColor += LocalCoverage * Sample.BaseColor;
}
OutColor.rgb = LocalAccColor;
OutColor.rgb *= PixelCoverage;
OutColor.a = PixelCoverage;
OutDepth = HairDeviceZ;
}
#endif
#endif // SHADER_COMPOSE_SAMPLE
////////////////////////////////////////////////////////////////////////////////////////////////
#if SHADER_HOLDOUT
// PassType:
// * 0: Output attenuation for the scene pixel (alpha only)
// * 1: Output hair contribution to the scene pixel (alpha value only)
uint PassType;
uint bComposeDofDepth;
Texture2D<float> HairDOFDepthTexture;
void HoldoutPS(
in FScreenVertexOutput Input,
out float4 OutColor : SV_Target0,
out float OutDepth : SV_DEPTH)
{
#if SUPPORT_PRIMITIVE_ALPHA_HOLDOUT
OutColor = 0.0f;
const uint3 PixelCoord = uint3(floor(Input.Position.xy), 0);
const float HairDeviceZ = HairStrands.HairOnlyDepthTexture.Load(PixelCoord).x;
const FNodeDesc NodeDesc = DecodeNodeDesc(HairStrands.HairSampleOffset.Load(PixelCoord));
if (NodeDesc.Count == 0)
{
discard;
}
const float PixelCoverage = min(HairStrands.HairCoverageTexture.Load(PixelCoord), 1);
if (PixelCoverage == 0)
{
discard;
}
bool bHoldout = false;
LOOP
for (uint SampleIt = 0; SampleIt < NodeDesc.Count; SampleIt++)
{
const FHairSample Sample = UnpackHairSample(HairStrands.HairSampleData[NodeDesc.Offset + SampleIt]);
bHoldout = bHoldout || HasHairFlags(Sample.Flags, HAIR_FLAGS_HOLDOUT);
}
const float3 ClosetPointWorldPosition = DFHackToFloat(SvPositionToWorld(float4(Input.Position.xy, HairDeviceZ, 1.0)));
const float4 Volumetric = EvaluateVolumetric(ClosetPointWorldPosition, ConvertFromDeviceZ(HairDeviceZ));
if (!bHoldout)
{
discard;
}
//OutColor.rgb = (LocalAccColor * Volumetric.a + Volumetric.rgb) * PixelCoverage;
OutColor.rgb = 0;
OutColor.a = PassType == 0 ? PixelCoverage : Volumetric.a * PixelCoverage;
OutDepth = HairDeviceZ;
if (bComposeDofDepth)
{
OutDepth = HairDOFDepthTexture.Load(PixelCoord);
}
#else
OutColor = 0;
OutDepth = 0;
discard;
#endif
}
#endif // SHADER_HOLDOUT
////////////////////////////////////////////////////////////////////////////////////////////////
#if SHADER_DOFDEPTH
#include "../CircleDOFCommon.ush"
// Included from DeferredLightingCommon now
//Texture2D<float> SceneDepthTexture;
void DOFDepthPS(
in FScreenVertexOutput Input,
out float OutTexture : SV_Target0)
{
OutTexture = 0.0f;
const uint3 PixelCoord = uint3(floor(Input.Position.xy), 0);
const float PixelCoverage = min(HairStrands.HairCoverageTexture.Load(PixelCoord), 1);
if (PixelCoverage == 0)
{
return;
}
const float HalfResToFullRes = 2;
float SceneDeviceZ = 0;
float SceneDepth = 0;
float SceneCoC = 0; // Radius in pixel
{
SceneDeviceZ = SceneDepthTexture.Load(PixelCoord).x;
SceneDepth = ConvertFromDeviceZ(SceneDeviceZ);
SceneDeviceZ = 1 - SceneDeviceZ;
SceneCoC = DepthToCoc(SceneDepth) * HalfResToFullRes;
}
float HairLuminance = 0;
float HairDeviceZ = 0;
float HairDepth = 0;
float HairCoC = 0; // Radius in pixel
{
HairDeviceZ = HairStrands.HairOnlyDepthTexture.Load(PixelCoord).x;
HairDepth = ConvertFromDeviceZ(HairDeviceZ);
HairDeviceZ = 1 - HairDeviceZ;
HairCoC = DepthToCoc(HairDepth) * HalfResToFullRes;
}
const float OutDeviceZ = lerp(SceneDeviceZ, HairDeviceZ, PixelCoverage);
OutTexture = 1 - OutDeviceZ;
}
#endif // SHADER_DOFDEPTH
////////////////////////////////////////////////////////////////////////////////////////////////
#if SHADER_WRITE_GBUFFER
#include "HairStrandsGBufferCommon.ush"
uint bWriteDummyData;
void MainPS(
in float2 UV : TEXCOORD
, in float4 SvPosition: SV_Position
, out float4 OutGBufferA : SV_Target0
, out float4 OutGBufferB : SV_Target1
#if PERMUTATION_OUTPUT_TYPE == 1
, out float4 OutGBufferC : SV_Target2
, out float4 OutGBufferD : SV_Target3
, out float4 OutGBufferE : SV_Target4
, out float OutDepth : SV_DEPTH
#endif
)
{
ResolvedView = ResolveView();
OutGBufferA = 0;
OutGBufferB = 0;
#if PERMUTATION_OUTPUT_TYPE == 0
float4 OutGBufferC = 0;
float4 OutGBufferD = 0;
float4 OutGBufferE = 0;
float OutDepth = 0;
#elif PERMUTATION_OUTPUT_TYPE == 1
OutGBufferC = 0;
OutGBufferD = 0;
OutGBufferE = 0;
OutDepth = 0;
#endif
const uint3 PixelCoord = uint3(floor(SvPosition.xy), 0);
const bool bIsValid = HairStrands.HairOnlyDepthTexture.Load(PixelCoord).x > 0;
if (!bIsValid)
{
discard;
}
const FNodeDesc NodeDesc = DecodeNodeDesc(HairStrands.HairSampleOffset.Load(PixelCoord));
if (NodeDesc.Count == 0)
{
discard;
}
if (bWriteDummyData > 0)
{
const float3 Tangent = float3(0, 0, 1);
OutGBufferA = float4(Tangent, 0);
OutGBufferB = float4(0, 0, 0, EncodeShadingModelIdAndSelectiveOutputMask(SHADINGMODELID_HAIR, 0));
}
else
{
float3 Samples_Tangent = 0;
float Samples_Specular = 0;
float3 Samples_BaseColor = 0;
float Samples_Roughnesss = 0;
uint Samples_LightChannelMask = 0;
float Samples_Backlit = 0;
float Samples_Depth = 0;
LOOP
for (uint NodeIt = 0; NodeIt < NodeDesc.Count; ++NodeIt)
{
const uint NodeOffset = NodeDesc.Offset + NodeIt;
const FPackedHairSample Data = HairStrands.HairSampleData[NodeOffset];
const FHairSample Sample = UnpackHairSample(Data);
Samples_Tangent += Sample.Tangent;
Samples_Specular += Sample.Specular;
Samples_BaseColor += Sample.BaseColor;
Samples_Roughnesss += Sample.Roughness;
Samples_Backlit += Sample.Backlit;
if (NodeIt == 0)
{
Samples_LightChannelMask = Sample.LightChannelMask;
}
}
Samples_Depth = HairStrands.HairOnlyDepthTexture.Load(PixelCoord).x;
const float InvSampleCount = 1.f / max(1u, NodeDesc.Count);
Samples_Tangent = normalize(Samples_Tangent);
Samples_Specular *= InvSampleCount;
Samples_BaseColor *= InvSampleCount;
Samples_Roughnesss *= InvSampleCount;
Samples_Backlit *= InvSampleCount;
WriteGBuffer(
Samples_Tangent,
Samples_Specular,
Samples_BaseColor,
Samples_Roughnesss,
Samples_LightChannelMask,
Samples_Backlit,
Samples_Depth,
OutGBufferA,
OutGBufferB,
OutGBufferC,
OutGBufferD,
OutGBufferE,
OutDepth);
}
}
#endif // SHADER_WRITE_GBUFFER