Files
UnrealEngine/Engine/Shaders/Private/VelocityShader.usf
Brandyn / Techy fcc1b09210 init
2026-04-04 15:40:51 -05:00

272 lines
11 KiB
HLSL

// Copyright Epic Games, Inc. All Rights Reserved.
/*=============================================================================
VelocityShader.usf: Calculates velocity vectors.
=============================================================================*/
#include "Common.ush"
#include "/Engine/Generated/Material.ush"
#include "/Engine/Generated/VertexFactory.ush"
#include "VelocityCommon.ush"
#ifndef VELOCITY_CLIPPED_DEPTH_PASS
#define VELOCITY_CLIPPED_DEPTH_PASS 0
#endif
// Move all geometry a little bit towards the camera to not get z fighting with existing depth
// buffer of the same meshes rendered with slightly different float computations.
// This is a positive number as our z buffer is inverted for better precision.
// 0.0f as the isolate code should do a good job to prevent this case (can be increased if that is not the case e.g. if the platform ignores the isolate)
static const float GDepthBias = 0.001f;
struct FVelocityInterpsVSToPS
{
#if !TRANSLUCENCY_VELOCITY_FROM_DEPTH
#if STEREO_MOTION_VECTORS
// float3(ScreenPos.xyzw)
float4 PackedVelocityA : TEXCOORD6;
#endif
// float4(PrevScreenPos.xyzw)
float4 PackedVelocityC : TEXCOORD7;
#endif
FVertexFactoryInterpolantsVSToPS FactoryInterpolants;
#if USE_WORLD_POSITION_EXCLUDING_SHADER_OFFSETS && !IS_NANITE_PASS
float3 PixelPositionExcludingWPO : TEXCOORD8;
#endif
};
struct FVelocityVSToPS
{
INVARIANT_OUTPUT float4 Position : SV_POSITION;
FVelocityInterpsVSToPS Interps;
FStereoVSOutput StereoOutput;
};
#define FVertexOutput FVelocityVSToPS
#define VertexFactoryGetInterpolants VertexFactoryGetInterpolantsVSToPS
/*=============================================================================
* Vertex Shader
*============================================================================*/
#if VERTEXSHADER
void MainVertexShader(
FVertexFactoryInput Input,
out FVertexOutput Output
#if USE_GLOBAL_CLIP_PLANE
, out float OutGlobalClipPlaneDistance : SV_ClipDistance
#endif
)
{
StereoSetupVF(Input, Output.StereoOutput);
FVertexFactoryIntermediates VFIntermediates = GetVertexFactoryIntermediates(Input);
float4 TranslatedWorldPosition = VertexFactoryGetWorldPosition( Input, VFIntermediates );
#if USE_WORLD_POSITION_EXCLUDING_SHADER_OFFSETS && !IS_NANITE_PASS
Output.Interps.PixelPositionExcludingWPO = TranslatedWorldPosition.xyz;
#endif
half3x3 TangentToLocal = VertexFactoryGetTangentToLocal( Input, VFIntermediates );
FMaterialVertexParameters VertexParameters = GetMaterialVertexParameters(Input, VFIntermediates, TranslatedWorldPosition.xyz, TangentToLocal);
// Isolate instructions used for world position offset on xbox 360,
// As these cause the optimizer to generate different position calculating instructions in each pass, resulting in self-z-fighting.
// This is only necessary for shaders used in passes that have depth testing enabled.
{
TranslatedWorldPosition.xyz += GetMaterialWorldPositionOffset(VertexParameters);
ApplyMaterialFirstPersonTransform(VertexParameters, TranslatedWorldPosition.xyz);
}
Output.Interps.FactoryInterpolants = VertexFactoryGetInterpolants( Input, VFIntermediates, VertexParameters );
float4 PrevTranslatedWorldPosition = VertexFactoryGetPreviousWorldPosition( Input, VFIntermediates );
VertexParameters = GetMaterialVertexParameters(Input, VFIntermediates, PrevTranslatedWorldPosition.xyz, TangentToLocal, true);
PrevTranslatedWorldPosition.xyz += GetMaterialPreviousWorldPositionOffset(VertexParameters);
ApplyMaterialPreviousFirstPersonTransform(VertexParameters, PrevTranslatedWorldPosition.xyz);
{
// compute the old screen pos with the old world position and the old camera matrix
float4 PrevScreenPosObj = mul(float4(PrevTranslatedWorldPosition.xyz, 1), ResolvedView.PrevTranslatedWorldToClip);
Output.Position = INVARIANT(mul(float4(TranslatedWorldPosition.xyz, 1), ResolvedView.TranslatedWorldToClip));
#if STEREO_MOTION_VECTORS
Output.Interps.PackedVelocityA = Output.Position;
#endif
#if USE_GLOBAL_CLIP_PLANE
OutGlobalClipPlaneDistance = dot(ResolvedView.GlobalClippingPlane, float4(TranslatedWorldPosition.xyz, 1));
#endif
#if !TRANSLUCENCY_VELOCITY_FROM_DEPTH
Output.Interps.PackedVelocityC = INVARIANT(PrevScreenPosObj);
#endif
}
// Move all geometry a little bit towards the camera (to not get z fighting with existing zbuffer of the same meshes rendered with slightly different float computations).
Output.Position.z += View.NumSceneColorMSAASamples > 1 ? GDepthBias * Output.Position.w : 0.0;
#if !TRANSLUCENCY_VELOCITY_FROM_DEPTH
Output.Interps.PackedVelocityC.z += View.NumSceneColorMSAASamples > 1 ? GDepthBias * Output.Position.w : 0.0;
#endif
}
#endif // VERTEXSHADER
/*=============================================================================
* Pixel Shader
*============================================================================*/
#ifndef VELOCITY_THIN_TRANSLUCENT_MODE
#define VELOCITY_THIN_TRANSLUCENT_MODE 0
#endif
#define OUPUT_MOTION_VECTOR_WORLD_OFFSET (!TRANSLUCENCY_VELOCITY_FROM_DEPTH && !VELOCITY_CLIPPED_DEPTH_PASS && VELOCITY_SUPPORT_PS_MOTION_VECTOR_WORLD_OFFSET)
void MainPixelShader(
in INPUT_POSITION_QUALIFIERS float4 SvPosition : SV_Position,
FVelocityInterpsVSToPS Input,
FStereoPSInput StereoInput
OPTIONAL_IsFrontFace
OPTIONAL_OutDepthConservative
,out ENCODED_VELOCITY_TYPE OutColor : SV_Target0
#if OUPUT_MOTION_VECTOR_WORLD_OFFSET
,out ENCODED_VELOCITY_TYPE OutMotionVectorWorldOffset : SV_Target1
#endif
#if MATERIALBLENDING_MASKED_USING_COVERAGE
, out uint OutCoverage : SV_Coverage
#endif
)
{
StereoSetupPS(StereoInput);
// Manual clipping here (alpha-test, etc)
FMaterialPixelParameters MaterialParameters = GetMaterialPixelParameters(Input.FactoryInterpolants, SvPosition);
FPixelMaterialInputs PixelMaterialInputs;
#if STEREO_MOTION_VECTORS
float4 ScreenPosition = Input.PackedVelocityA;
#else
float4 ScreenPosition = SvPositionToResolvedScreenPosition(SvPosition);
#endif
#if USE_WORLD_POSITION_EXCLUDING_SHADER_OFFSETS && !IS_NANITE_PASS
float3 TranslatedWorldPosition = SvPositionToResolvedTranslatedWorld(SvPosition);
CalcMaterialParametersEx(MaterialParameters, PixelMaterialInputs, SvPosition, ScreenPosition, bIsFrontFace, TranslatedWorldPosition, Input.PixelPositionExcludingWPO);
#else
CalcMaterialParameters(MaterialParameters, PixelMaterialInputs, SvPosition, bIsFrontFace);
#endif
#if OUTPUT_PIXEL_DEPTH_OFFSET
ApplyPixelDepthOffsetToMaterialParameters(MaterialParameters, PixelMaterialInputs, OutDepth);
#if APPLE_DEPTH_BIAS_HACK
OutDepth -= APPLE_DEPTH_BIAS_VALUE;
#endif
#endif
#if MATERIALBLENDING_MASKED_USING_COVERAGE
OutCoverage = DiscardMaterialWithPixelCoverage(MaterialParameters, PixelMaterialInputs);
#else
#if SUBSTRATE_ENABLED && TEMPLATE_USES_SUBSTRATE
const bool bThinTranslucentMaterial = false;
FSubstrateData SubstrateData = PixelMaterialInputs.GetFrontSubstrateData();
FSubstratePixelHeader SubstratePixelHeader = MaterialParameters.GetFrontSubstrateHeader();
float MaterialOpacity = 1.0f;
if (SubstratePixelHeader.ClosureCount > 0)
{
// Update tree (coverage/transmittance/luminace weights)
const FSubstrateIntegrationSettings Settings = InitSubstrateIntegrationSettings();
const float3 V = MaterialParameters.CameraVector;
SubstratePixelHeader.SubstrateUpdateTree(V, Settings);
MaterialOpacity = SubstratePixelHeader.SubstrateTree.Operators[SubstrateData.OperatorIndex].Coverage;
}
#if SUBSTRATE_USE_PREMULTALPHA_OVERRIDE // AlphaComposite - Premultiplied alpha blending
MaterialOpacity = GetMaterialOpacity(PixelMaterialInputs);
#endif
#else
const bool bThinTranslucentMaterial = VELOCITY_THIN_TRANSLUCENT_MODE > 0;
const float MaterialOpacity = GetMaterialOpacity(PixelMaterialInputs);
#endif
#if VELOCITY_CLIPPED_DEPTH_PASS && (!TRANSLUCENCY_VELOCITY_FROM_DEPTH)
// Set a bit for those BeforeDoF translucent pixels without writing velocity
#if MATERIALBLENDING_TRANSLUCENT || MATERIALBLENDING_ADDITIVE || MATERIALBLENDING_MODULATE || SUBSTRATE_TRANSLUCENT_MATERIAL
#if COMPILER_SUPPORTS_UINT64_IMAGE_ATOMICS
// We set the bit for those with Opacity value < Opacity Mask Clip Value.
ClipLODTransition(MaterialParameters);
float ActualMaterialOpacity = bThinTranslucentMaterial ? 1.0f : MaterialOpacity;
const bool bWriteFlag = (ActualMaterialOpacity > 0 && ActualMaterialOpacity < (1.0 / 255.0 + GetMaterialOpacityMaskClipValue()));
clip(select(bWriteFlag, 1.0f, -1.0f));
uint2 Position = uint2(SvPosition.xy - 0.5f) + View.ViewRectMin.xy;
// Only consider if the current translucent pixel is in front.
float OpaqueDepth = CalcSceneDepth(Position);
float CurrentTranslucencyDepth = 0;
ApplyPixelDepthOffsetToMaterialParameters(MaterialParameters, PixelMaterialInputs, CurrentTranslucencyDepth);
CurrentTranslucencyDepth = ConvertFromDeviceZ(CurrentTranslucencyDepth);
// Use Temporal Responsiveness node to provide more flexible control.
#if VELOCITY_ENCODE_TEMPORAL_RESPONSIVENESS
float TranslucentTemporalResponsiveness = GetMaterialTemporalResponsiveness(MaterialParameters);
uint TemporalResponsivenessMask = select(TranslucentTemporalResponsiveness > 0.5f, 3U, uint(TranslucentTemporalResponsiveness>0)) & TEMPORAL_RESPONSIVENESS_MASK;
#else
uint TemporalResponsivenessMask = 0;
#endif
uint WriteFlag = select(CurrentTranslucencyDepth < OpaqueDepth, TemporalResponsivenessMask, 0);
BRANCH
if (WriteFlag != 0)
{
const uint2 EncodedOutputBit = uint2(0u, WriteFlag << 16u); // Mask the lowest bits of alpha.
ImageInterlockedOrUInt64(VelocityClippedDepth.RWVelocity, Position, PackUlongType(EncodedOutputBit));
}
#endif
#endif
clip(-1);
#else
GetMaterialClippingVelocity(MaterialParameters, PixelMaterialInputs, bThinTranslucentMaterial, MaterialOpacity);
#endif //VELOCITY_CLIPPED_DEPTH_PASS
#endif //MATERIALBLENDING_MASKED_USING_COVERAGE
#if !TRANSLUCENCY_VELOCITY_FROM_DEPTH
float3 Velocity;
float TemporalResponsiveness = GetMaterialTemporalResponsiveness(MaterialParameters);
bool bHasPixelAnimationFlag = (GetPrimitiveData(MaterialParameters).Flags & PRIMITIVE_SCENE_DATA_FLAG_HAS_PIXEL_ANIMATION) != 0;
#if (!VELOCITY_CLIPPED_DEPTH_PASS && VELOCITY_SUPPORT_PS_MOTION_VECTOR_WORLD_OFFSET) // Opaque pass that uses motion vector offset in PS.
float3 OffsetTranslatedWorldPosition = GetMaterialMotionVectorWorldOffset(MaterialParameters) + GetTranslatedWorldPosition(MaterialParameters);
float4 ClipPosition = mul(float4(OffsetTranslatedWorldPosition, 1.0f), ResolvedView.TranslatedWorldToClip);
{
// Write the offset at the same time so that we can recover the actual velocity to previous frame.
OutMotionVectorWorldOffset = EncodeVelocityToTexture(Calculate3DVelocityOffset(ScreenPosition, ClipPosition), bHasPixelAnimationFlag, TemporalResponsiveness);
Velocity = Calculate3DVelocity(ScreenPosition, Input.PackedVelocityC);
}
#else
Velocity = Calculate3DVelocity(ScreenPosition, Input.PackedVelocityC);
#endif // !VELOCITY_CLIPPED_DEPTH_PASS
#if STEREO_MOTION_VECTORS
OutColor.xyz = Velocity;
OutColor.w = 0.0f;
#else
OutColor = EncodeVelocityToTexture(Velocity, bHasPixelAnimationFlag, TemporalResponsiveness);
#endif
#else
// We only want velocity generated from camera movement and depth so write zero which is the special clear color value.
OutColor = 0;
#endif
}