439 lines
12 KiB
HLSL
439 lines
12 KiB
HLSL
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
#pragma once
|
|
|
|
// Render modes
|
|
#define RENDER_MODE_TRANSMITTANCE 0
|
|
#define RENDER_MODE_PPLL 1
|
|
#define RENDER_MODE_MSAA_VISIBILITY 2
|
|
#define RENDER_MODE_TRANSMITTANCE_AND_HAIRCOUNT 3
|
|
#define RENDER_MODE_COMPUTE_RASTER 4
|
|
|
|
#define INVALID_TILE_OFFSET ~0
|
|
|
|
#define HAIR_VISIBILITY_GROUP_COUNT_WIDTH 64
|
|
|
|
#include "../PackUnpack.ush"
|
|
#include "/Engine/Shared/HairStrandsDefinitions.h"
|
|
#include "HairStrandsVisibilityCommonStruct.ush"
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// General util functions
|
|
|
|
float3 EncodeTangent(float3 N)
|
|
{
|
|
return N * 0.5 + 0.5;
|
|
}
|
|
|
|
float3 DecodeTangent(float3 N)
|
|
{
|
|
return N * 2 - 1;
|
|
}
|
|
|
|
uint float4ToUint(float4 v)
|
|
{
|
|
uint4 i = uint4(v.x * 255, v.y * 255, v.z * 255, v.w * 255);
|
|
return (0xFF & i.w) << 24 | (0xFF & i.z) << 16 | (0xFF & i.y) << 8 | (0xFF & i.x);
|
|
}
|
|
|
|
float4 UintToFloat4(uint In)
|
|
{
|
|
uint4 Out;
|
|
Out.x = (0xFF & In);
|
|
Out.y = (0xFF & (In >> 8));
|
|
Out.z = (0xFF & (In >> 16));
|
|
Out.w = (0xFF & (In >> 24));
|
|
return Out / 255.f;
|
|
}
|
|
|
|
uint Uint16ToUint32(uint2 In)
|
|
{
|
|
return (In.x & 0xFFFF) | ((In.y & 0xFFFF) << 16);
|
|
}
|
|
|
|
uint2 Uint32ToUint16(uint In)
|
|
{
|
|
uint2 A;
|
|
A.x = In & 0xFFFF;
|
|
A.y = (In >> 16) & 0xFFFF;
|
|
return A;
|
|
}
|
|
|
|
uint Float16ToUint32(float2 In)
|
|
{
|
|
return Uint16ToUint32(f32tof16(In));
|
|
}
|
|
|
|
float2 Uint32ToFloat16(uint In)
|
|
{
|
|
return f16tof32(Uint32ToUint16(In));
|
|
}
|
|
|
|
uint To8bitCoverage(float Coverage)
|
|
{
|
|
return min(uint(Coverage * 0x100), 0xFFu);
|
|
}
|
|
|
|
float From8bitCoverage(uint Coverage8bit)
|
|
{
|
|
return float(Coverage8bit) / 255.f;
|
|
}
|
|
|
|
uint To16bitCoverage(float Coverage)
|
|
{
|
|
return min(uint(Coverage * 0x10000u), 0xFFFFu);
|
|
}
|
|
|
|
float From16bitCoverage(uint Coverage16bit)
|
|
{
|
|
return float(Coverage16bit) / float(0xFFFF);
|
|
}
|
|
|
|
uint3 QuantizeTo8Bits(float3 T)
|
|
{
|
|
const float3 Quanta = saturate((T + float(1).xxx) * 0.5f) * 0xFF;
|
|
return uint3(Quanta.x, Quanta.y, Quanta.z);
|
|
}
|
|
|
|
float3 From8bits(float3 In8bits)
|
|
{
|
|
return (normalize(In8bits / 255.f) * 2) - float(1).xxx;
|
|
}
|
|
|
|
uint PackVelocity(float2 EncodedVelocity)
|
|
{
|
|
return f32tof16(EncodedVelocity.x) << 16 | f32tof16(EncodedVelocity.y);
|
|
}
|
|
|
|
float2 UnpackVelocity(uint PackedVelocity)
|
|
{
|
|
return float2(f16tof32(PackedVelocity >> 16), f16tof32(PackedVelocity));
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// Node Desc
|
|
//
|
|
// * Max 67,108,864 total nodes (This is 32 nodes per pixel at 1080p if all pixel were covered)
|
|
// * Max 63 nodes per pixel
|
|
// 26bits for offset | 6 bits for count (max 63 nodes)
|
|
//TODO rename FHairSamplesDesc
|
|
struct FNodeDesc
|
|
{
|
|
uint Offset;
|
|
uint Count;
|
|
};
|
|
|
|
uint EncodeNodeDesc(const FNodeDesc Desc)
|
|
{
|
|
return (Desc.Offset & 0x03FFFFFF) | ((Desc.Count & 0x3F) << 26);
|
|
}
|
|
|
|
FNodeDesc DecodeNodeDesc(uint In)
|
|
{
|
|
FNodeDesc Out;
|
|
Out.Offset = In & 0x03FFFFFF;
|
|
Out.Count = (In >> 26) & 0x3F;
|
|
return Out;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// Hair Vis
|
|
|
|
uint PackHairVisDepthCoverage(float InDepth, float InCoverage)
|
|
{
|
|
return (uint(InDepth * 0x00FFFFFF) << 8) | uint(0xFFu * saturate(InCoverage));
|
|
}
|
|
|
|
uint PackHairVisDepthCoverage(float InDepth, uint InCoverage8bits)
|
|
{
|
|
return (uint(InDepth * 0x00FFFFFF) << 8) | min(0xFFu, InCoverage8bits);
|
|
}
|
|
|
|
uint PackHairVisControlPointMaterialId(uint ControlPointId, uint MaterialId)
|
|
{
|
|
return (ControlPointId & 0x00FFFFFF) | ((MaterialId & 0xFF) << 24);
|
|
}
|
|
|
|
uint UnpackHairVisControlPointId(uint RawVis)
|
|
{
|
|
return RawVis & 0x00FFFFFF;
|
|
}
|
|
|
|
uint UnpackHairVisMaterialId(uint RawVis)
|
|
{
|
|
return (RawVis >> 24) & 0xFF;
|
|
}
|
|
|
|
float UnpackHairVisDepth(uint InPacked)
|
|
{
|
|
return float(InPacked >> 8) / float(0x00FFFFFF);
|
|
}
|
|
|
|
float UnpackHairVisCoverage(uint InPacked)
|
|
{
|
|
return float(InPacked & 0xFF) / float(0xFF);
|
|
}
|
|
|
|
uint UnpackHairVisCoverage8bits(uint InPacked)
|
|
{
|
|
return InPacked & 0xFF;
|
|
}
|
|
|
|
uint GetInvalidHairControlPointId()
|
|
{
|
|
return 0xFFFFFFFF;
|
|
}
|
|
|
|
void PatchPackedHairVisCoverage(inout FPackedHairVis Out, uint Coverage8bit)
|
|
{
|
|
Out.Depth_Coverage8bit = (Out.Depth_Coverage8bit & (0x00FFFFFFu << 8)) | (Coverage8bit & 0xFFu);
|
|
}
|
|
|
|
struct FHairVis
|
|
{
|
|
float Depth;
|
|
float Coverage; // Coverage in float point, but with 8-bits precision. For convenience.
|
|
uint Coverage8bit;
|
|
uint ControlPointId;
|
|
uint MaterialId;
|
|
};
|
|
|
|
FPackedHairVis PackHairVis(const FHairVis In)
|
|
{
|
|
FPackedHairVis Out;
|
|
Out.Depth_Coverage8bit = PackHairVisDepthCoverage(In.Depth, In.Coverage8bit);
|
|
Out.ControlPointID_MaterialID = PackHairVisControlPointMaterialId(In.ControlPointId, In.MaterialId);
|
|
return Out;
|
|
}
|
|
|
|
FHairVis UnpackHairVis(const FPackedHairVis In)
|
|
{
|
|
FHairVis Out;
|
|
Out.Depth = UnpackHairVisDepth(In.Depth_Coverage8bit);
|
|
Out.Coverage8bit = UnpackHairVisCoverage8bits(In.Depth_Coverage8bit);
|
|
Out.Coverage = float(Out.Coverage8bit) / float(0xFF);
|
|
Out.ControlPointId= UnpackHairVisControlPointId(In.ControlPointID_MaterialID);
|
|
Out.MaterialId = UnpackHairVisMaterialId(In.ControlPointID_MaterialID);
|
|
|
|
return Out;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// Hair sample
|
|
|
|
// Footprint: 128bits | 16bytes
|
|
// Option to reduce the footprint:
|
|
// * we don't use ControlPointId at the moment which is encoded on 3bytes
|
|
// * Deferred the evaluation of Coverage & material properties into some other pass?
|
|
struct FHairSample
|
|
{
|
|
float Depth;
|
|
float3 Tangent;
|
|
uint Coverage8bit;
|
|
uint ControlPointId;
|
|
uint MacroGroupId;
|
|
float3 BaseColor;
|
|
float Roughness;
|
|
float Specular;
|
|
float Backlit;
|
|
uint LightChannelMask;
|
|
float3 Emissive;
|
|
uint Flags;
|
|
};
|
|
|
|
FPackedHairSample PackHairSample(const FHairSample In)
|
|
{
|
|
FPackedHairSample Out;
|
|
|
|
// Tangent and Coverage
|
|
// Encode into 1 x 32bits uint
|
|
// Coverage is clamped o 255 as we only have 8 bits to store its value
|
|
const float3 T = saturate(EncodeTangent(In.Tangent));
|
|
Out.Tangent_Coverage8bit =
|
|
PackRGBA8(float4(T,0)) |
|
|
(min(uint(0xFF), In.Coverage8bit) << 24);
|
|
|
|
// ControlPointId and MacroGroupId
|
|
// Encode into 1 x 32bits uint
|
|
// ControlPointId is on 28bits | MacroGroupId is on 4bits
|
|
Out.ControlPointID_MacroGroupID =
|
|
((In.ControlPointId & 0x0FFFFFFF)) |
|
|
((In.MacroGroupId & 0xF) << 28); // If this encoding change, ensure it conforms to MAX_HAIR_MACROGROUP_COUNT
|
|
|
|
Out.Depth = In.Depth; // 32bits float
|
|
Out.BaseColor_Roughness = float4ToUint(float4(sqrt(In.BaseColor), In.Roughness)); // 32bits uint
|
|
Out.Data =
|
|
(PackR8(In.Specular) )|
|
|
(PackR8(In.Backlit) << 8)|
|
|
((In.Flags & 0xFF) << 16)|
|
|
((In.LightChannelMask & 0x7) << 24);
|
|
|
|
// Emissive is not packed/unpacked
|
|
return Out;
|
|
}
|
|
|
|
FHairSample UnpackHairSample(const FPackedHairSample In)
|
|
{
|
|
FHairSample Out;
|
|
Out.Depth = In.Depth;
|
|
|
|
Out.Tangent = DecodeTangent(UnpackRGBA8(In.Tangent_Coverage8bit).xyz);
|
|
Out.Coverage8bit = (In.Tangent_Coverage8bit >> 24) & 0xFF;
|
|
|
|
Out.ControlPointId = In.ControlPointID_MacroGroupID & 0x0FFFFFFF;
|
|
Out.MacroGroupId = (In.ControlPointID_MacroGroupID>>28) & 0xF; // If this encoding change, ensure it conforms to MAX_HAIR_MACROGROUP_COUNT
|
|
|
|
const float4 BaseColorAndRoughness = UintToFloat4(In.BaseColor_Roughness);
|
|
Out.BaseColor = BaseColorAndRoughness.xyz * BaseColorAndRoughness.xyz;
|
|
Out.Roughness = BaseColorAndRoughness.w;
|
|
Out.Specular = UnpackR8(In.Data & 0xFF);
|
|
Out.Backlit = UnpackR8((In.Data>>8) & 0xFF);
|
|
Out.Flags = (In.Data>>16) & 0xFF;
|
|
Out.LightChannelMask = (In.Data>>24) & 0x7;
|
|
|
|
// Emissive is not packed/unpacked
|
|
return Out;
|
|
}
|
|
|
|
void PatchPackedHairSampleCoverage(inout FPackedHairSample Out, uint Coverage8bit)
|
|
{
|
|
Out.Tangent_Coverage8bit = (Out.Tangent_Coverage8bit & 0x00FFFFFF) | (min(uint(0xFF), Coverage8bit) << 24);
|
|
}
|
|
|
|
uint GetPackedHairSampleCoverage8bit(in FPackedHairSample In)
|
|
{
|
|
return 0xFF & (In.Tangent_Coverage8bit>>24);
|
|
}
|
|
|
|
uint2 GetHairSampleResolution(uint InNodeCount)
|
|
{
|
|
uint Resolution = ceil(sqrt(InNodeCount));
|
|
return uint2(Resolution, Resolution);
|
|
}
|
|
|
|
uint2 GetHairSampleCoord(uint InLinearCoord, uint2 InResolution)
|
|
{
|
|
return uint2(InLinearCoord % InResolution.x, InLinearCoord / InResolution.x);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// Hair velocity
|
|
|
|
// Return true if the underlying pixel need to run with TAA fast resolve. This is the case when
|
|
// the velocity is larger than a certain threshold.
|
|
bool NeedFastResolve(float2 InEncodedVelocity, float2 InVelocity, float InVelocityThreshold)
|
|
{
|
|
const float VelocityMagnitude = sqrt(dot(InVelocity, InVelocity));
|
|
return InEncodedVelocity.x > 0 && VelocityMagnitude > InVelocityThreshold;
|
|
}
|
|
|
|
bool NeedFastResolve(float2 InEncodedVelocity, float InVelocityThreshold)
|
|
{
|
|
const float2 Velocity = DecodeVelocityFromTexture(float4(InEncodedVelocity, 0.0, 0.0)).xy;
|
|
const float VelocityMagnitude = sqrt(dot(Velocity, Velocity));
|
|
return InEncodedVelocity.x > 0 && VelocityMagnitude > InVelocityThreshold;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// Hair view info (used for overriding view constant/uniform buffer)
|
|
|
|
struct FHairRenderInfo
|
|
{
|
|
float RadiusAtDepth1Primary;
|
|
float RadiusAtDepth1Velocity;
|
|
float VelocityMagnitudeScale;
|
|
|
|
bool bIsGPUDriven;
|
|
bool bIsOrthoView; // Only used for shadow view
|
|
|
|
float3 ViewForward;
|
|
float3 TranslatedWorldCameraOrigin;
|
|
};
|
|
|
|
FHairRenderInfo GetHairRenderInfo(float4 ViewHairRenderInfo, uint ViewHairRenderInfoBits, bool bUseScableRasterization=false)
|
|
{
|
|
FHairRenderInfo Info;
|
|
Info.RadiusAtDepth1Primary = bUseScableRasterization ? ViewHairRenderInfo.y : ViewHairRenderInfo.x;
|
|
Info.RadiusAtDepth1Velocity = ViewHairRenderInfo.z;
|
|
Info.VelocityMagnitudeScale = ViewHairRenderInfo.w;
|
|
|
|
const uint BitField = ViewHairRenderInfoBits;
|
|
Info.bIsOrthoView = (BitField & 0x1) != 0;
|
|
Info.bIsGPUDriven = (BitField & 0x2) != 0;
|
|
|
|
return Info;
|
|
}
|
|
|
|
struct FHairViewInfo
|
|
{
|
|
float3 TranslatedWorldCameraOrigin;
|
|
float3 ViewForward;
|
|
float RadiusAtDepth1;
|
|
bool bIsOrthoView;
|
|
};
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// Hair transmittance/coverage
|
|
|
|
float TransmittanceToCoverage(float InTransmittance, float InCoverageThreashold)
|
|
{
|
|
return saturate(min(1 - InTransmittance, 1) / InCoverageThreashold);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// Hair PPLL
|
|
|
|
FPackedHairVisPPLL PackHairVisPPLL(
|
|
float Depth,
|
|
float Coverage,
|
|
uint ControlPointId,
|
|
uint MaterialId,
|
|
uint NextNodeIndex)
|
|
{
|
|
FHairVis S;
|
|
S.Depth = Depth;
|
|
S.Coverage8bit = To8bitCoverage(Coverage); // To16bitCoverage(CoverageFullPrecision)
|
|
S.ControlPointId = ControlPointId;
|
|
S.MaterialId = MaterialId;
|
|
const FPackedHairVis PackedS = PackHairVis(S);
|
|
|
|
FPackedHairVisPPLL Out;
|
|
Out.Depth_Coverage8bit = PackedS.Depth_Coverage8bit;
|
|
Out.ControlPointID_MaterialID = PackedS.ControlPointID_MaterialID;
|
|
Out.NextNodeIndex = NextNodeIndex;
|
|
return Out;
|
|
}
|
|
|
|
FHairVis UnpackHairVisPPLL(FPackedHairVisPPLL In)
|
|
{
|
|
FPackedHairVis Temp;
|
|
Temp.Depth_Coverage8bit = In.Depth_Coverage8bit;
|
|
Temp.ControlPointID_MaterialID = In.ControlPointID_MaterialID;
|
|
return UnpackHairVis(Temp);
|
|
}
|
|
|
|
FPackedHairVis ConvertToPackedHairVis(FPackedHairVisPPLL In)
|
|
{
|
|
FPackedHairVis Out;
|
|
Out.Depth_Coverage8bit = In.Depth_Coverage8bit;
|
|
Out.ControlPointID_MaterialID = In.ControlPointID_MaterialID;
|
|
return Out;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// Coverage and hair count
|
|
|
|
uint PackCoverageAndHairCount(float InCoverage)
|
|
{
|
|
// Assume the storage is enough for not overflowing
|
|
// Encode the strands_radius/pixel_radius ratio into 8bits, and this can accumulate up to 256 element with coverage of 1
|
|
//return (InHairCount<<16) | uint(saturate(InCoverage) * 0xFF);
|
|
return uint(saturate(InCoverage) * 1000.f);
|
|
}
|
|
|
|
float UnpackCoverageAndHairCount(uint InPacked)
|
|
{
|
|
return float(InPacked) / 1000.f;
|
|
}
|
|
|