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

564 lines
18 KiB
HLSL

// Copyright Epic Games, Inc. All Rights Reserved.
#include "Common.ush"
#include "GammaCorrectionCommon.ush"
#include "SlateShaderCommon.ush"
#ifndef SAMPLE_VIRTUAL_TEXTURE
#define SAMPLE_VIRTUAL_TEXTURE 0
#endif
#if SAMPLE_VIRTUAL_TEXTURE
#define NUM_VIRTUALTEXTURE_SAMPLES 1
// Workaround for unbound View uniform buffer
#define VT_DISABLE_VIEW_UNIFORM_BUFFER 1
#include "VirtualTextureCommon.ush"
#endif
/** Display gamma x:gamma curve adjustment, y:inverse gamma (1/GEngine->DisplayGamma), z:InvertAlpha, w:Contrast */
half4 GammaAndAlphaValues;
/** x:DrawDisabledEffect */
/** Per element params */
float4 ShaderParams;
float4 ShaderParams2;
#define USE_LEGACY_DISABLED_EFFECT 0
#if USE_MATERIALS
half4 DrawFlags;
#endif
/** Texture for the element */
Texture2D ElementTexture;
SamplerState ElementTextureSampler;
/**
* Applys a contrast about a midpoint squeezing the colors towards dark or light depending on
* the midpoint. Additionally - it increases the opaqueness of colors to try and seperate them from
* the background.
*/
half4 ApplyContrast(half4 InColor)
{
FLATTEN if( GammaAndAlphaValues.w != 1.0f )
{
float ContrastMidpoint = 0.25; //TODO We may want to make the midpoint configurable so that users can pick a different one, based on the overall saturation of their UI.
InColor.rgb = saturate(((InColor.rgb - ContrastMidpoint) * GammaAndAlphaValues.w) + ContrastMidpoint);
}
return InColor;
}
half3 GammaCorrect(half3 InColor)
{
half3 CorrectedColor = InColor;
#if SOURCE_IN_LINEAR_SPACE
FLATTEN if( GammaAndAlphaValues.y != 1.0f )
{
CorrectedColor = ApplyGammaCorrection(CorrectedColor, GammaAndAlphaValues.x);
}
#endif
return CorrectedColor;
}
void GetSlateUVs(VertexToPixelInterpolants Interpolants, inout float2 UVs[8])
{
#if NUM_MATERIAL_TEXCOORDS > 0
UVs[0] = SLATE_UV0( Interpolants );
#endif
#if NUM_MATERIAL_TEXCOORDS > 1
UVs[1] = SLATE_UV1( Interpolants );
#endif
#if NUM_MATERIAL_TEXCOORDS > 2
UVs[2] = SLATE_UV2( Interpolants );
#endif
#if NUM_MATERIAL_TEXCOORDS > 3
UVs[3] = SLATE_UV3( Interpolants );
#endif
#if NUM_MATERIAL_TEXCOORDS > 4
UVs[4] = SLATE_UV4( Interpolants );
#endif
#if NUM_MATERIAL_TEXCOORDS > 5
UVs[5] = SLATE_UV5( Interpolants );
#endif
#if NUM_MATERIAL_TEXCOORDS > 6
UVs[6] = SLATE_UV6( Interpolants );
#endif
#if NUM_MATERIAL_TEXCOORDS > 7
UVs[7] = SLATE_UV7( Interpolants );
#endif
}
#if USE_MATERIALS
void FillTexCoords( inout FMaterialPixelParameters Parameters, VertexToPixelInterpolants InVertex, float2 InDefaultUV )
{
#if SHADER_TYPE == ST_GrayscaleFont || SHADER_TYPE == ST_ColorFont || SHADER_TYPE == ST_SdfFont || SHADER_TYPE == ST_MsdfFont
#if NUM_MATERIAL_TEXCOORDS == 1
// Note: The first channel is used to look up into the texture atlas. The second channel is the custom 0-1 UV set for the text run
Parameters.TexCoords[0] = SLATE_UV1( InVertex );
#elif NUM_MATERIAL_TEXCOORDS == 2
// Note: The first channel is used to look up into the texture atlas. The second channel is the custom 0-1 UV set for the text run
Parameters.TexCoords[0] = SLATE_UV1( InVertex );
Parameters.TexCoords[1] = InVertex.MaterialTexCoords;
#endif
#elif (SHADER_TYPE == ST_Custom || NUM_CUSTOMIZED_UVS != 0) && NUM_MATERIAL_TEXCOORDS > 0
// note: We assume users who customize UVs or are using completely custom elements know what they are doing with UVs and want no assumptions made about how uvs are used
float2 UVs[8]; GetSlateUVs(InVertex, UVs);
UNROLL
for( int CoordinateIndex = 0; CoordinateIndex < NUM_MATERIAL_TEXCOORDS; CoordinateIndex++ )
{
Parameters.TexCoords[CoordinateIndex] = UVs[CoordinateIndex];
}
#else
#if NUM_MATERIAL_TEXCOORDS == 1
Parameters.TexCoords[0] = InDefaultUV;
#elif NUM_MATERIAL_TEXCOORDS == 2
Parameters.TexCoords[0] = InDefaultUV;
Parameters.TexCoords[1] = SLATE_UV0( InVertex );
#elif NUM_MATERIAL_TEXCOORDS == 3
Parameters.TexCoords[0] = InDefaultUV;
Parameters.TexCoords[1] = SLATE_UV0( InVertex );
Parameters.TexCoords[2] = SLATE_UV1( InVertex );
#elif NUM_MATERIAL_TEXCOORDS >= 4
Parameters.TexCoords[0] = InDefaultUV;
Parameters.TexCoords[1] = SLATE_UV0( InVertex );
Parameters.TexCoords[2] = SLATE_UV1( InVertex );
float2 UVs[8]; GetSlateUVs(InVertex, UVs);
UNROLL
for( int CoordinateIndex = 3; CoordinateIndex < NUM_MATERIAL_TEXCOORDS; CoordinateIndex++ )
{
Parameters.TexCoords[CoordinateIndex] = UVs[CoordinateIndex];
}
#endif // NUM_MATERIAL_TEXCOORDS == 1
#endif // SHADER_TYPE
}
half4 GetMaterialColor( VertexToPixelInterpolants InVertex, float2 MeshUV, float4 InFontSignedDistanceData = float4(0.0, 0.0, 0.0, 0.0) )
{
ResolvedView = ResolveView();
FMaterialPixelParameters Parameters = MakeInitializedMaterialPixelParameters();
// Inputs might require texcoords, so fill them in first
FillTexCoords( Parameters, InVertex, MeshUV );
Parameters.VertexColor = InVertex.Color;
#if SHADER_TYPE == ST_GrayscaleFont || SHADER_TYPE == ST_ColorFont || SHADER_TYPE == ST_SdfFont || SHADER_TYPE == ST_MsdfFont
Parameters.FontSignedDistanceData = InFontSignedDistanceData;
#endif
FPixelMaterialInputs PixelMaterialInputs;
CalcMaterialParameters( Parameters, PixelMaterialInputs, InVertex.Position, true );
// Clip if masked
GetMaterialCoverageAndClipping( Parameters, PixelMaterialInputs );
half4 OutColor;
#if SUBSTRATE_ENABLED
// UI node forces a single unlit BSDF. We get the raw data from the Substrate UI node from the raw BSDF on the tree.
FSubstratePixelHeader SubstratePixelHeader = Parameters.GetFrontSubstrateHeader();
FSubstrateBSDF UnlitBSDF = SubstratePixelHeader.SubstrateTree.BSDFs[0];
UnlitBSDF.SubstrateSanitizeBSDF(); // required wehen not calling SubstrateUpdateTreeUnlit
float3 UnlitLuminance = BSDF_GETEMISSIVE(UnlitBSDF);
float3 UnlitTransmittance= UNLIT_TRANSMITTANCE(UnlitBSDF);
float UnlitGreyCoverage = 1.0 - UnlitTransmittance.x;
#if ((SUBSTRATE_OPAQUE_DEFERRED || (SUBSTRATE_OPAQUE_MATERIAL && SUBSTRATE_FORWARD_SHADING)))
OutColor = half4(UnlitLuminance, 1.0f);
#else // SUBSTRATE_OPAQUE_DEFERRED
#if MATERIALBLENDING_ALPHACOMPOSITE
#if SUBSTRATE_USE_PREMULTALPHA_OVERRIDE // AlphaComposite - Premultiplied alpha blending
UnlitGreyCoverage = GetMaterialOpacity(PixelMaterialInputs);
#endif
OutColor = half4(UnlitLuminance, UnlitGreyCoverage);
#elif MATERIALBLENDING_MODULATE
OutColor = half4(UnlitTransmittance, 0.0f);
#elif MATERIALBLENDING_ALPHAHOLDOUT
OutColor = half4(0.0f, 0.0f, 0.0f, UnlitGreyCoverage);
#elif MATERIALBLENDING_ADDITIVE
OutColor = half4(UnlitLuminance, 1.0);
#else // MATERIALBLENDING_TRANSLUCENT
OutColor = half4(UnlitLuminance, UnlitBSDF.Coverage);
#endif // MATERIALBLENDING_xxx
#endif// SUBSTRATE_OPAQUE_DEFERRED
#else // SUBSTRATE_ENABLED
half Opacity = GetMaterialOpacity(PixelMaterialInputs);
half3 Color = GetMaterialEmissive(PixelMaterialInputs);
#if MATERIALBLENDING_ADDITIVE
OutColor = half4(Color * Opacity, 0.0f);
#else
OutColor = half4(Color, Opacity);
#endif
#endif // SUBSTRATE_ENABLED
#if MATERIAL_VIRTUALTEXTURE_FEEDBACK
FinalizeVirtualTextureFeedback(Parameters.VirtualTextureFeedback, Parameters.SvPosition, View.VTFeedbackBuffer);
#endif
return OutColor;
}
#endif
#if !USE_MATERIALS
half4 GetTextureColor( VertexToPixelInterpolants InVertex, float2 UV )
{
half4 BaseColor;
#if SAMPLE_VIRTUAL_TEXTURE
FVirtualTextureFeedbackParams VirtualTextureFeedback;
InitializeVirtualTextureFeedback(VirtualTextureFeedback);
VTPageTableUniform PageTableUniform = VTPageTableUniform_Unpack(SlateElementVirtualTextureParams.PackedPageTableUniform0, SlateElementVirtualTextureParams.PackedPageTableUniform1);
VTPageTableResult PageTableResult = TextureLoadVirtualPageTable(SlateElementVirtualTextureParams.PageTableTexture, PageTableUniform, UV, VTADDRESSMODE_WRAP, VTADDRESSMODE_WRAP, 0.0f, InVertex.Position.xy, 0, VirtualTextureFeedback);
BaseColor = TextureVirtualSampleLevel(ElementTexture, ElementTextureSampler, PageTableResult, SlateElementVirtualTextureParams.LayerIndex, VTUniform_Unpack(SlateElementVirtualTextureParams.PackedUniform));
FinalizeVirtualTextureFeedback(VirtualTextureFeedback, SlateElementVirtualTextureParams.FeedbackParams, InVertex.Position, SlateElementVirtualTextureParams.FeedbackBuffer);
#else
BaseColor = Texture2DSample(ElementTexture, ElementTextureSampler, UV );
#endif
#if USE_TEXTURE_GRAYSCALE
#if !USE_TEXTURE_ALPHA
BaseColor.rgb = BaseColor.rrr;
BaseColor.a = 1;
#else
BaseColor.a = BaseColor.r;
BaseColor.rgb = half3(1,1,1);
#endif
#else
#if !USE_TEXTURE_ALPHA
BaseColor.a = 1.0f;
#endif
#endif
return BaseColor;
}
#endif
/** Gets a color from a texture and supplied vertex color. Adjusting for tint masking if necessary */
half4 GetColor( VertexToPixelInterpolants InVertex, float2 UV )
{
half4 FinalColor;
#if USE_MATERIALS
half4 BaseColor = GetMaterialColor( InVertex, UV );
#else
half4 BaseColor = GetTextureColor( InVertex, UV );
#endif
#if SHADER_TYPE == ST_Custom
// Make no assumptionn of what vertex color does in a custom material shader
FinalColor = BaseColor;
#else
FinalColor = BaseColor * InVertex.Color;
#if USE_MATERIALS && MATERIALBLENDING_ADDITIVE
FinalColor *= InVertex.Color.a;
#endif
#endif
return FinalColor;
}
#if SHADER_TYPE == ST_SdfFont || SHADER_TYPE == ST_MsdfFont
struct FontSignedDistanceSample
{
MaterialFloat DistanceSample;
MaterialFloat SmoothDistanceSample;
float ScreenSpaceSpread;
};
FontSignedDistanceSample SampleFontSignedDistance(float2 TextureCoordinate)
{
FontSignedDistanceSample OutSample;
const MaterialFloat4 MultiDistance = Texture2DSample(ElementTexture, ElementTextureSampler, TextureCoordinate);
#if SHADER_TYPE == ST_MsdfFont
OutSample.DistanceSample = max(min(MultiDistance.r, MultiDistance.g), min(max(MultiDistance.r, MultiDistance.g), MultiDistance.b));
OutSample.SmoothDistanceSample = MultiDistance.a;
#else // SHADER_TYPE == ST_SdfFont
OutSample.DistanceSample = lerp(MultiDistance A8_SAMPLE_MASK, MultiDistance.a, ShaderParams2.x);
OutSample.SmoothDistanceSample = OutSample.DistanceSample;
#endif
const float2 ScreenSpaceTextureSize = float2(1.0, 1.0) / fwidth(TextureCoordinate);
OutSample.ScreenSpaceSpread = max(dot(ShaderParams.xy, ScreenSpaceTextureSize), 1.0);
return OutSample;
}
float GetFontSignedDistanceOpacity(FontSignedDistanceSample InSample)
{
const float ScreenSpaceDistance = (InSample.DistanceSample - ShaderParams.z) * InSample.ScreenSpaceSpread;
return saturate(ScreenSpaceDistance + 0.5);
}
#if USE_MATERIALS
float4 GetFontSignedDistanceData(FontSignedDistanceSample InSample)
{
const float EmSignedDistance = (InSample.DistanceSample - ShaderParams.z) * ShaderParams.w;
const float EmSmoothDistance = (InSample.SmoothDistanceSample - ShaderParams.z) * ShaderParams.w;
const float EmPxFactor = InSample.ScreenSpaceSpread / ShaderParams.w;
return float4(EmSignedDistance, EmSmoothDistance, EmPxFactor, GetFontSignedDistanceOpacity(InSample));
}
#endif
half4 GetSdfTextElementColor(VertexToPixelInterpolants InVertex)
{
const FontSignedDistanceSample SdfSample = SampleFontSignedDistance(SLATE_UV0(InVertex));
half4 OutColor = InVertex.Color;
#if USE_MATERIALS
const float4 SdfData = GetFontSignedDistanceData(SdfSample);
OutColor *= GetMaterialColor(InVertex, float2(0, 0), SdfData);
if (ShaderParams2.y < 0.5) // ShaderParams2.y is bMaterialIsStencil
{
#if MATERIALBLENDING_ADDITIVE || MATERIALBLENDING_ALPHACOMPOSITE
OutColor *= SdfData.a;
#else
OutColor.a *= SdfData.a;
#endif
}
#else // !USE_MATERIALS
OutColor.a *= GetFontSignedDistanceOpacity(SdfSample);
#endif
return OutColor;
}
#endif
#if SHADER_TYPE == ST_GrayscaleFont || SHADER_TYPE == ST_ColorFont
half4 GetFontElementColor( VertexToPixelInterpolants InVertex )
{
float2 AtlasTextureCoordinates = SLATE_UV0( InVertex );
MaterialFloat4 SampledColor = MaterialFloat4(1,1,1,1);
#if SHADER_TYPE == ST_GrayscaleFont
#if USE_MATERIALS && (MATERIALBLENDING_ADDITIVE || MATERIALBLENDING_ALPHACOMPOSITE)
SampledColor *= Texture2DSample_A8( ElementTexture, ElementTextureSampler, AtlasTextureCoordinates );
#else
SampledColor.a = Texture2DSample_A8( ElementTexture, ElementTextureSampler, AtlasTextureCoordinates );
#endif
#else
SampledColor = Texture2DSample( ElementTexture, ElementTextureSampler, AtlasTextureCoordinates );
#endif
half4 OutColor = InVertex.Color;
#if USE_MATERIALS
OutColor *= GetMaterialColor( InVertex, float2(0,0), float4(0, 0, 0, SampledColor.a) );
if (ShaderParams2.y < 0.5) // ShaderParams2.y is bMaterialIsStencil
{
OutColor *= half4(SampledColor);
}
#else
OutColor *= half4(SampledColor);
#endif
return OutColor;
}
#endif
#if SHADER_TYPE == ST_Default || SHADER_TYPE == ST_Custom
half4 GetDefaultElementColor( VertexToPixelInterpolants InVertex )
{
return GetColor( InVertex, SLATE_UV0( InVertex ) * SLATE_UV1( InVertex ) );
}
#endif
#if SHADER_TYPE == ST_Border
half4 GetBorderElementColor( VertexToPixelInterpolants InVertex )
{
float4 TextureCoordinates = InVertex.TextureCoordinates0;
float2 NewUV;
if( TextureCoordinates.z == 0.0f && TextureCoordinates.w == 0.0f )
{
NewUV = TextureCoordinates.xy;
}
else
{
float2 MinUV;
float2 MaxUV;
if( TextureCoordinates.z > 0 )
{
MinUV = float2(ShaderParams.x,0);
MaxUV = float2(ShaderParams.y,1);
TextureCoordinates.w = 1.0f;
}
else
{
MinUV = float2(0,ShaderParams.z);
MaxUV = float2(1,ShaderParams.w);
TextureCoordinates.z = 1.0f;
}
NewUV = TextureCoordinates.xy*TextureCoordinates.zw;
NewUV = frac(NewUV);
NewUV = lerp(MinUV,MaxUV,NewUV);
}
return GetColor( InVertex, NewUV );
}
#endif
#if SHADER_TYPE == ST_RoundedBox
half4 GetRoundedBoxElementColor( VertexToPixelInterpolants InVertex )
{
return GetRoundedBoxElementColorInternal(ShaderParams.zw, InVertex.MaterialTexCoords.xy, ShaderParams.y, ShaderParams2, GetColor(InVertex, SLATE_UV0(InVertex) * SLATE_UV1(InVertex)), InVertex.SecondaryColor);
}
#endif
#if SHADER_TYPE == ST_LineSegment
/**
* Computes 0-1 value that interpolates between dashes of length DashLength based on Distance with 1px of antialiasing between dashes and gaps
*
* ie:
* 1 ----------------- ----------------- -----------------
* \ / \ / \ /
* 0 \_________________/ \_________________/ \_________________/
*
* < DashLength > < DashLength > < DashLength > < DashLength > < DashLength > < DashLength >
*/
float ModulateDashedLine(float Distance, float DashLength)
{
// Add one for antialias pixel
DashLength += 1.f;
const float HalfDash = 0.5f*DashLength;
const float Period = 2.0f*DashLength;
// Offset the interpolation so that the dash starts at distance = 0;
const float Interp = Distance - HalfDash + 0.5f;
const float Mod = Interp - Period*floor(Interp/Period); // mod(Interp, Period)
// Compute the dash alpha using a clamped triangle wave
const float TriWave = 2.0f * abs(Mod - DashLength) - DashLength;
return clamp(TriWave, -1.0f, 1.0f)*0.5f + 0.5f;
}
/**
* Generates an anti-aliased line segment pixel
* This is based on the fast prefiltered lines technique published in GPU Gems 2
*
* Instead of passing edge function coefficients as uniform parameters we use
* texture coordinates to define the line
*
* When rasterized, the submitted geometry must contain every pixel that
* could have coverage > 0. Failing to do so will create sharp, aliased edges
*/
half4 GetLineSegmentElementColor( VertexToPixelInterpolants InVertex )
{
const float2 Gradient = SLATE_UV0(InVertex);
const float2 DashParams = SLATE_UV1(InVertex);
// Get coverage based on distance from segment sides and ends
const float2 OutsideFilterUV = float2(1.0f, 1.0f);
const float2 InsideFilterUV = float2(ShaderParams.x, 0.0f);
const float2 LineCoverage = smoothstep(OutsideFilterUV, InsideFilterUV, abs(Gradient));
const float DashLength = DashParams.y;
const float DashAlpha = ModulateDashedLine(DashParams.x, DashParams.y);
half4 Color = InVertex.Color;
Color.a *= LineCoverage.x * LineCoverage.y * DashAlpha;
return Color;
}
#endif
HALF4_TYPE Main( VertexToPixelInterpolants VIn ) : SV_Target0
{
half4 OutColor;
#if SHADER_TYPE == ST_Default || SHADER_TYPE == ST_Custom
OutColor = GetDefaultElementColor( VIn );
#elif SHADER_TYPE == ST_RoundedBox
OutColor = GetRoundedBoxElementColor( VIn );
#elif SHADER_TYPE == ST_Border
OutColor = GetBorderElementColor( VIn );
#elif SHADER_TYPE == ST_GrayscaleFont || SHADER_TYPE == ST_ColorFont
OutColor = GetFontElementColor( VIn );
#elif SHADER_TYPE == ST_SdfFont || SHADER_TYPE == ST_MsdfFont
OutColor = GetSdfTextElementColor( VIn );
#else
OutColor = GetLineSegmentElementColor( VIn );
#endif
// For standard slate which only has a few shaders, the permutation is fine here,
// for Engine Materials, we don't ever define this macro, so instead we fallback
// to looking at the shader constants.
#if USE_MATERIALS
const half DrawDisabledEffect = DrawFlags.x;
#else
#if DRAW_DISABLED_EFFECT
const half DrawDisabledEffect = 1;
#else
const half DrawDisabledEffect = 0;
#endif
#endif
if (DrawDisabledEffect != 0)
{
#if USE_LEGACY_DISABLED_EFFECT
#if SOURCE_IN_LINEAR_SPACE
half3 LumCoeffs = half3( 0.3, 0.59, .11 );
half3 Grayish = half3(.1, .1, .1);
#else
// These are just the linear space values above converted to sRGB space.
half3 LumCoeffs = float3( 0.5843, 0.7921, 0.3647 );
half3 Grayish = float3(0.349, 0.349, 0.349);
#endif
//desaturate
half Lum = dot( LumCoeffs, OutColor.rgb );
OutColor.rgb = lerp( OutColor.rgb, float3(Lum,Lum,Lum), .8 );
OutColor.rgb = lerp( OutColor.rgb, Grayish, clamp( distance( OutColor.rgb, Grayish ), 0, .8) );
#else
//float bw = (min(OutColor.r, min(OutColor.g, OutColor.b)) + max(OutColor.r, max(OutColor.g, OutColor.b))) * 0.5f;
//OutColor.rgb = lerp(OutColor.rgb, float3(bw, bw, bw), .5);
OutColor.a *= .45f;
#endif
}
// Contrast
OutColor = ApplyContrast(OutColor);
// Gamma Correct
OutColor.rgb = GammaCorrect(OutColor.rgb);
#if !USE_MATERIALS
// Deal with cases where the incoming alpha is inverted like if you're dealing with render targets coming
// from the scene renderer.
OutColor.a = lerp(OutColor.a, 1 - OutColor.a, GammaAndAlphaValues.z);
#endif
return OutColor;
}
float4 DebugOverdrawMain( VertexToPixelInterpolants VIn ) : SV_Target0
{
return .1;
}
/** Batch Color */
float4 BatchColor;
float4 DebugBatchingMain(VertexToPixelInterpolants VIn) : SV_Target0
{
return BatchColor;
}