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

219 lines
7.0 KiB
HLSL

// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "HairStrandsVoxelPageCommonStruct.ush"
///////////////////////////////////////////////////////////////////////////////////////////////////
// Define voxel traversal type
#define VOXEL_TRAVERSAL_NONE 0
#define VOXEL_TRAVERSAL_LINEAR 1
#define VOXEL_TRAVERSAL_SPARSE_LINEAR 2
#define VOXEL_TRAVERSAL_SPARSE_MIPMAP 3
#define VOXEL_TRAVERSAL_LINEAR_MIPMAP 4
///////////////////////////////////////////////////////////////////////////////////////////////////
uint3 PositionToCoord(float3 P, float3 InMinAABB, float3 InMaxAABB, uint3 InResolution)
{
return clamp(
uint3(saturate((P - InMinAABB) / (InMaxAABB - InMinAABB)) * InResolution),
uint3(0,0,0),
InResolution -1);
}
uint3 PositionToCoordUnclampled(float3 P, float3 InMinAABB, float3 InMaxAABB, uint3 InResolution)
{
return uint3(saturate((P - InMinAABB) / (InMaxAABB - InMinAABB)) * float3(InResolution));
}
uint CoordToIndex(uint3 InCoord, uint3 InResolution, uint LinearOffset)
{
return
InCoord.x +
InCoord.y * InResolution.x +
InCoord.z * InResolution.x * InResolution.y +
LinearOffset;
}
uint3 IndexToCoord(uint InIndex, uint3 InResolution)
{
const uint SliceSize = (InResolution.x * InResolution.y);
const uint SliceIndex = InIndex % SliceSize;
uint3 OutCoord = 0;
OutCoord.x = SliceIndex % InResolution.x;
OutCoord.y = SliceIndex / InResolution.x;
OutCoord.z = InIndex / SliceSize;
return OutCoord;
}
bool IsInVoxelBounds(float3 P, float3 MinP, float3 MaxP)
{
return
P.x >= MinP.x && P.y >= MinP.y && P.z >= MinP.z &&
P.x <= MaxP.x && P.y <= MaxP.y && P.z <= MaxP.z;
}
///////////////////////////////////////////////////////////////////////////////////////////////////
#define INVALID_VOXEL_PAGE_INDEX 0xFFFFFFFF
#define INVALID_MACRO_GROUP_ID 0xFF
#define VOXEL_CAST_NO_SHADOW_MASK 0x80000000 // Bit mask for holding if the voxel does *not* cast hair shadow (i.e. cast only transmission). The invert/negative logic is for making hair voxelization more efficient.
#define VOXEL_HAIR_MASK 0x00FFFFFF // Bit mask for holding hair count/density
#define VOXEL_OPAQUE_MASK 0x7F000000 // Bit mask for holding opaque geometry
#define VOXEL_OPAQUE_ADD 0x7F000000
#define VOXEL_OPAQUE_SHIFT 24
struct FVirtualVoxelCommonDesc
{
uint3 PageCountResolution;
uint3 PageTextureResolution;
uint PageResolution;
uint PageResolutionLog2;
};
struct FVirtualVoxelNodeDesc
{
float3 TranslatedWorldMinAABB; // In translated world space
float3 TranslatedWorldMaxAABB; // In translated world space
uint3 PageIndexResolution;
uint3 VirtualResolution;
uint PageIndexOffset;
float VoxelWorldSize;
bool bIsValid;
};
uint PackVoxelWorldSize(float In)
{
// Encode voxel world size [0..10] onto 10bits, i.e.: 10.f / 1023u
const float NormalizedVoxelWorldSize = min(In, 10.f) * 0.1f;
return PackUnorm10(NormalizedVoxelWorldSize);
}
float UnpackVoxelWorldSize(uint In)
{
return UnpackUnorm10(In) * 10.f;
}
float QuantizeVoxelWorldSize(float In)
{
return UnpackVoxelWorldSize(PackVoxelWorldSize(In));
}
FVirtualVoxelNodeDesc UnpackVoxelNode(FPackedVirtualVoxelNodeDesc In, uint InPageResolution)
{
FVirtualVoxelNodeDesc Out;
Out.TranslatedWorldMaxAABB = In.TranslatedWorldMaxAABB;
Out.TranslatedWorldMinAABB = In.TranslatedWorldMinAABB;
Out.PageIndexResolution = uint3(In.PackedPageIndexResolution & 0xFF, (In.PackedPageIndexResolution >> 8) & 0xFF, (In.PackedPageIndexResolution >> 16) & 0xFF);
Out.VirtualResolution = Out.PageIndexResolution * InPageResolution;
Out.PageIndexOffset = (In.PageIndexOffset_VoxelWorldSize & 0x3FFFFFu);
Out.VoxelWorldSize = UnpackVoxelWorldSize(In.PageIndexOffset_VoxelWorldSize >> 22u);
Out.bIsValid = all(Out.PageIndexResolution != 0);
return Out;
}
FPackedVirtualVoxelNodeDesc PackVoxelNode(FVirtualVoxelNodeDesc In)
{
FPackedVirtualVoxelNodeDesc Out;
Out.TranslatedWorldMinAABB = In.TranslatedWorldMinAABB;
Out.TranslatedWorldMaxAABB = In.TranslatedWorldMaxAABB;
Out.PackedPageIndexResolution =
(In.PageIndexResolution.x & 0xFF) |
((In.PageIndexResolution.y & 0xFF)<<8) |
((In.PageIndexResolution.z & 0xFF)<<16);
Out.PageIndexOffset_VoxelWorldSize =
(In.PageIndexOffset & 0x3FFFFFu) |
(PackVoxelWorldSize(In.VoxelWorldSize) << 22u);
return Out;
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
struct FHairTraversalResult
{
float HairCount;
float Visibility;
float HitT;
};
FHairTraversalResult InitHairTraversalResult()
{
FHairTraversalResult Out;
Out.HairCount = 0;
Out.Visibility = 1;
Out.HitT = -1;
return Out;
}
void Acc(inout FHairTraversalResult Out, const FHairTraversalResult In)
{
Out.HairCount += In.HairCount;
Out.Visibility = min(Out.Visibility, In.Visibility);
Out.HitT = In.HitT >= 0 && Out.HitT >= 0 ? min(In.HitT, Out.HitT) : (In.HitT >= 0 ? In.HitT : Out.HitT);
}
///////////////////////////////////////////////////////////////////////////////////////////////////
float GetVoxelDensityFixPointScale()
{
// Constant for scaling the hair coverage during the voxelization (done with atomic integer)
// TODO share or make common with the version in VisibilityCommon
return 1000.f;
}
float GetOpaqueVoxelValue()
{
// Constant for scaling the hair coverage during the voxelization (done with atomic integer)
// TODO share or make common with the version in VisibilityCommon
return VOXEL_OPAQUE_MASK / GetVoxelDensityFixPointScale() - 1;
}
// Return a value in [0..1] 0:no opaque/occlusion 1:fully occluded
float GetInternalVoxelOpaqueVisibility(uint RawDensity)
{
const uint Raw = (RawDensity & VOXEL_OPAQUE_MASK) >> VOXEL_OPAQUE_SHIFT;
return 1.f - saturate(Raw / 255.f);
}
float GetInternalVoxelHairCount(uint RawDensity)
{
const float VoxelFixPointScale = GetVoxelDensityFixPointScale();
const uint Raw = (RawDensity & VOXEL_HAIR_MASK);
return Raw / VoxelFixPointScale;
}
FHairTraversalResult GetHairVirtualVoxelDensity(uint3 InVoxelPageCoord, Texture3D<uint> InPageTexture, uint MipIndex, float DensityScale, bool bCastShadow)
{
FHairTraversalResult Out = InitHairTraversalResult();
const uint RawDensity = InPageTexture.Load(uint4(InVoxelPageCoord >> MipIndex, MipIndex));
const bool bValid = !bCastShadow || (bCastShadow && (RawDensity & VOXEL_CAST_NO_SHADOW_MASK) == 0);
if (bValid)
{
Out.HairCount = GetInternalVoxelHairCount(RawDensity) * DensityScale;
Out.Visibility = GetInternalVoxelOpaqueVisibility(RawDensity);
}
return Out;
}
///////////////////////////////////////////////////////////////////////////////////////////////////
uint EncodeBaseColorAndRoughness(float3 BaseColor, float Roughness)
{
return
(uint(BaseColor.x * 0xFF) & 0xFF) |
(uint(BaseColor.y * 0xFF) & 0xFF) << 8 |
(uint(BaseColor.z * 0xFF) & 0xFF) << 16 |
(uint(Roughness * 0xFF) & 0xFF) << 24;
}
void DecodeBaseColorAndRoughness(uint Encoded, inout float3 BaseColor, out float Roughness)
{
BaseColor.x = (float((Encoded) & 0xFF) / 255.f);
BaseColor.y = (float((Encoded >> 8) & 0xFF) / 255.f);
BaseColor.z = (float((Encoded >> 16) & 0xFF) / 255.f);
Roughness = (float((Encoded >> 24) & 0xFF) / 255.f);
}