Files
UnrealEngineUWP/Engine/Shaders/Private/PostProcessMaterialShaders.usf
jason hoerner b527382d73 Post Process Alpha is preserved in SceneColor where possible for post process materials that don't write alpha. This includes making it so materials that read and write from SceneColor, but don't write alpha, automatically read and write alpha. For materials that only write to SceneColor, a Color Write mask is set to mask the alpha channel in the destination. An important bug with UserSceneTextures was also fixed where a final pass of a post process chain wasn't properly identified if it wrote to a UserSceneTexture overridden to "SceneColor".
Propagated alpha is handled by storing the alpha to a local variable when PostProcessInput0 (SceneColor) is fetched from either a SceneTexture or UserSceneTexture node, then writing the fetched value to the output.  The propagation of alpha is free in any material which reads from a SceneTexture, but an extra scalar and vector ALU is generated per UserSceneTexture, since the alpha propagation is conditional based on whether a given input has SceneColor assigned as its input (based on the value of the UserSceneTextureSceneColorInput parameter).  The first UserSceneTexture vector ALU will be free, rolled into the output assignment.  The material can be set up to output opacity to optimize that minor overhead out completely if desired.

An advantage of this approach is that it even works with custom HLSL, since it just requires SceneTextureLookup to be reached by any means, and it also straightforwardly handles UserSceneTextures reading and writing SceneColor.  Some complexity is added in that SceneTexture or UserSceneTexture nodes feeding into custom HLSL, where the input pin value itself isn't used, now need to be explicitly dead stripped, as the side effect of writing to PropagatedAlpha prevents the shader compiler from optimizing out calls to SceneTextureLookup based on the return value being unused in downstream code.  See UMaterialExpressionCustom::Compile for where that is handled (with detailed comments).

#jira UE-133044
#rb eric.renaudhoude massimo.tristano jason.nadro

[CL 36747746 by jason hoerner in 5.5 branch]
2024-10-01 17:47:27 -04:00

550 lines
17 KiB
Plaintext

// Copyright Epic Games, Inc. All Rights Reserved.
/*=============================================================================
PostProcessMaterialShaders.usf: Shaders for rendering post process materials
=============================================================================*/
#include "Common.ush"
#include "ScreenPass.ush"
#pragma message("UESHADERMETADATA_VERSION 02E0E449-2223-45B4-8CD0-93AB087FBEE3")
#ifndef POST_PROCESS_MATERIAL
#define POST_PROCESS_MATERIAL 0
#endif
#if (POST_PROCESS_MATERIAL == 0)
#error POST_PROCESS_MATERIAL must be defined to non-zero in the shader compilation environment.
#endif
// Must match ESceneTextureId
#define PPI_PostProcessInput0 14
#define PPI_PostProcessInput1 15
#define PPI_PostProcessInput2 16
#define PPI_PostProcessInput3 17
#define PPI_PostProcessInput4 18
SCREEN_PASS_TEXTURE_VIEWPORT(PostProcessInput_0)
SCREEN_PASS_TEXTURE_VIEWPORT(PostProcessInput_1)
SCREEN_PASS_TEXTURE_VIEWPORT(PostProcessInput_2)
SCREEN_PASS_TEXTURE_VIEWPORT(PostProcessInput_3)
SCREEN_PASS_TEXTURE_VIEWPORT(PostProcessInput_4)
SCREEN_PASS_TEXTURE_VIEWPORT(PostProcessOutput)
Texture2D PostProcessInput_0_Texture;
Texture2D PostProcessInput_1_Texture;
Texture2D PostProcessInput_2_Texture;
Texture2D PostProcessInput_3_Texture;
Texture2D PostProcessInput_4_Texture;
SamplerState PostProcessInput_0_Sampler;
SamplerState PostProcessInput_1_Sampler;
SamplerState PostProcessInput_2_Sampler;
SamplerState PostProcessInput_3_Sampler;
SamplerState PostProcessInput_4_Sampler;
SamplerState PostProcessInput_BilinearSampler;
#if SUPPORTS_INDEPENDENT_SAMPLERS
#define PostProcessInput_0_SharedSampler PostProcessInput_0_Sampler
#define PostProcessInput_1_SharedSampler PostProcessInput_0_Sampler
#define PostProcessInput_2_SharedSampler PostProcessInput_0_Sampler
#define PostProcessInput_3_SharedSampler PostProcessInput_0_Sampler
#define PostProcessInput_4_SharedSampler PostProcessInput_0_Sampler
#else
#define PostProcessInput_0_SharedSampler PostProcessInput_0_Sampler
#define PostProcessInput_1_SharedSampler PostProcessInput_1_Sampler
#define PostProcessInput_2_SharedSampler PostProcessInput_2_Sampler
#define PostProcessInput_3_SharedSampler PostProcessInput_3_Sampler
#define PostProcessInput_4_SharedSampler PostProcessInput_4_Sampler
#endif
#if MATERIAL_PATH_TRACING_BUFFER_READ
SCREEN_PASS_TEXTURE_VIEWPORT(PathTracingPostProcessInput_0)
SCREEN_PASS_TEXTURE_VIEWPORT(PathTracingPostProcessInput_1)
SCREEN_PASS_TEXTURE_VIEWPORT(PathTracingPostProcessInput_2)
SCREEN_PASS_TEXTURE_VIEWPORT(PathTracingPostProcessInput_3)
SCREEN_PASS_TEXTURE_VIEWPORT(PathTracingPostProcessInput_4)
Texture2D PathTracingPostProcessInput_0_Texture;
Texture2D PathTracingPostProcessInput_1_Texture;
Texture2D PathTracingPostProcessInput_2_Texture;
Texture2D PathTracingPostProcessInput_3_Texture;
Texture2D PathTracingPostProcessInput_4_Texture;
SamplerState PathTracingPostProcessInput_0_Sampler;
SamplerState PathTracingPostProcessInput_1_Sampler;
SamplerState PathTracingPostProcessInput_2_Sampler;
SamplerState PathTracingPostProcessInput_3_Sampler;
SamplerState PathTracingPostProcessInput_4_Sampler;
#endif
#include "NeuralPostProcessCommon.usf"
#define EYE_ADAPTATION_LOOSE_PARAMETERS 1
#include "/Engine/Generated/Material.ush"
uint ManualStencilReferenceValue;
uint ManualStencilTestMask;
struct FPostProcessMaterialVSToPS
{
float4 Position : SV_POSITION;
#if NUM_TEX_COORD_INTERPOLATORS
float4 TexCoords[(NUM_TEX_COORD_INTERPOLATORS+1)/2] : TEXCOORD0;
#endif
};
#if NUM_TEX_COORD_INTERPOLATORS
float2 GetUV(FPostProcessMaterialVSToPS Interpolants, int UVIndex)
{
float4 UVVector = Interpolants.TexCoords[UVIndex / 2];
return Mod(UVIndex, 2) == 1 ? UVVector.zw : UVVector.xy;
}
void SetUV(inout FPostProcessMaterialVSToPS Interpolants, int UVIndex, float2 InValue)
{
FLATTEN
if (Mod(UVIndex, 2) == 1)
{
Interpolants.TexCoords[UVIndex / 2].zw = InValue;
}
else
{
Interpolants.TexCoords[UVIndex / 2].xy = InValue;
}
}
#endif
FMaterialVertexParameters GetPostProcessMaterialVSParameters(float2 UV)
{
// Most params irrelevant so not a lot to fill out here
FMaterialVertexParameters Result = MakeInitializedMaterialVertexParameters();
Result.VertexColor = 1.f.xxxx;
Result.WorldPosition = float3(UV, 0.f);
#if NUM_MATERIAL_TEXCOORDS_VERTEX
UNROLL
for (int CoordinateIndex = 0; CoordinateIndex < NUM_MATERIAL_TEXCOORDS_VERTEX; CoordinateIndex++)
{
Result.TexCoords[CoordinateIndex] = UV;
}
#endif
return Result;
}
#if (FEATURE_LEVEL > FEATURE_LEVEL_ES3_1)
#if VERTEXSHADER
void MainVS(
in float4 InPosition : ATTRIBUTE0,
out FPostProcessMaterialVSToPS Output
)
{
Output = (FPostProcessMaterialVSToPS)0;
DrawRectangle(InPosition, Output.Position);
#if NUM_TEX_COORD_INTERPOLATORS
FMaterialVertexParameters VertexParameters = GetPostProcessMaterialVSParameters(InPosition.xy);
float2 CustomizedUVs[NUM_TEX_COORD_INTERPOLATORS];
GetMaterialCustomizedUVs(VertexParameters, CustomizedUVs);
GetCustomInterpolators(VertexParameters, CustomizedUVs);
{
UNROLL
for (int CoordinateIndex = 0; CoordinateIndex < NUM_MATERIAL_TEXCOORDS; CoordinateIndex++)
{
SetUV(Output, CoordinateIndex, InPosition.xy);
}
}
{
UNROLL
for (int CoordinateIndex = NUM_MATERIAL_TEXCOORDS; CoordinateIndex < NUM_TEX_COORD_INTERPOLATORS; CoordinateIndex++)
{
SetUV(Output, CoordinateIndex, CustomizedUVs[CoordinateIndex]);
}
}
#endif
}
void MainVS_VideoOverlay(
in float4 InPosition : ATTRIBUTE0,
in float2 InTexCoord : ATTRIBUTE1,
out float2 OutUV : TEXCOORD0,
out float4 OutPosition : SV_POSITION
)
{
DrawRectangle(InPosition, InTexCoord, OutPosition, OutUV);
}
#elif PIXELSHADER
#define STENCIL_TEST_EQUAL 0x1u
#define STENCIL_TEST_LESS 0x2u
#define STENCIL_TEST_GREATER 0x4u
// Perform the stencil test manually by discarding test failure.
// This is necessary when Nanite writes custom stencil and the platform cannot support writing arbitrary values directly to
// the custom stencil buffer per-pixel
void ManualStencilTest(uint2 PixelPos)
{
const uint Stencil = CalcSceneCustomStencil(PixelPos);
const uint Test =
(ManualStencilReferenceValue == Stencil ? STENCIL_TEST_EQUAL : 0) |
(ManualStencilReferenceValue < Stencil ? STENCIL_TEST_LESS : 0) |
(ManualStencilReferenceValue > Stencil ? STENCIL_TEST_GREATER : 0);
if ((Test & ManualStencilTestMask) == 0)
{
discard;
}
}
void MainPS(
in FPostProcessMaterialVSToPS Input,
out float4 OutColor : SV_Target0
)
{
ResolvedView = ResolveView();
FMaterialPixelParameters Parameters = MakeInitializedMaterialPixelParameters();
FPixelMaterialInputs PixelMaterialInputs;
// can be optimized
float4 SvPosition = Input.Position;
float2 ViewportUV = (SvPosition.xy - PostProcessOutput_ViewportMin.xy) * PostProcessOutput_ViewportSizeInverse.xy;
#if MANUAL_STENCIL_TEST
ManualStencilTest((uint2)SvPosition.xy);
#endif
#if NUM_TEX_COORD_INTERPOLATORS
{
UNROLL
for (int CoordinateIndex = 0; CoordinateIndex < NUM_MATERIAL_TEXCOORDS; CoordinateIndex++)
{
Parameters.TexCoords[CoordinateIndex] = ViewportUV;
}
}
{
UNROLL
for (int CoordinateIndex = NUM_MATERIAL_TEXCOORDS; CoordinateIndex < NUM_TEX_COORD_INTERPOLATORS; CoordinateIndex++)
{
Parameters.TexCoords[CoordinateIndex] = GetUV(Input, CoordinateIndex);
}
}
#endif
Parameters.VertexColor = 1;
SvPosition.z = LookupDeviceZ(ViewportUVToBufferUV(ViewportUV));
SvPosition.z = max(SvPosition.z, 1e-18);
// fill out other related material parameters
CalcMaterialParametersPost(Parameters, PixelMaterialInputs, SvPosition, true);
#if NEURAL_POSTPROCESS_PREPASS && MATERIAL_NEURAL_POST_PROCESS
OutColor = 0.5f;
#if HAVE_GetNeuralInput1
float4 Index = GetNeuralInput0(Parameters);
float3 NeuralInput0 = GetNeuralInput1(Parameters).rgb;
float Mask = GetNeuralInput2(Parameters);
const bool bWriteToBuffer = Index.x >= 0; //Configuration the same across all pixels.
// Update the source type so we can copy on demand
BRANCH
if (all((uint2)SvPosition.xy == uint2(0, 0)))
{
const int SourceType = bWriteToBuffer ? SOURCE_TYPE_BUFFER : SOURCE_TYPE_TEXTURE;
UpdateNeuralProfileSourceType(SourceType);
}
// Write to buffer/texture based on config
BRANCH
if (Mask != 0)
{
BRANCH
if (bWriteToBuffer)
{
int Batch = (int)Index.x;
int StartChannel = (int)Index.y;
float2 BufferWH = Index.zw;
SaveToNeuralBuffer(Batch, StartChannel, BufferWH, NeuralInput0);
}
else
{
int StartChannel = (int)Index.y;
float2 CustomViewportUV = Index.zw;
float2 Position = CustomViewportUV * PostProcessOutput_ViewportSize.xy + PostProcessOutput_ViewportMin.xy;
if (StartChannel >= 0 && StartChannel <= 2)
{
RWNeuralTexture[(uint2)Position.xy] = float4(NeuralInput0, 0.0f);
}
}
}
// This is for debug only
OutColor.xyz = NeuralInput0;
#if !MATERIALBLENDING_MODULATE && POST_PROCESS_MATERIAL_BEFORE_TONEMAP && !POST_PROCESS_DISABLE_PRE_EXPOSURE_SCALE
OutColor.xyz *= View.PreExposure;
#endif
return;
#endif // HAVE_GetNeuralInput1
#endif // NEURAL_POSTPROCESS_PREPASS
// In Unreal, an alpha channel value of 0.0f means that the pixel is opaque and a value of 1.0f means that there is translucency.
// This is the opposite of how the alpha channel is interpreted in formats like PNG. For this reason, rendered images that are exported
// from the engine will have their alpha channel inverted to match the PNG convention. However, for consistency inside the rendering chain,
// we need to stick to 0.0f as the default value.
float Alpha = 0.0f;
float3 EmissiveColor = 0.0f;
#if SUBSTRATE_ENABLED
// Post process node forces a single unlit BSDF. We get the raw data from the Substrate post process 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
half3 PPColor = BSDF_GETEMISSIVE(UnlitBSDF);
half PPAlpha = saturate(1.0 - UNLIT_TRANSMITTANCE(UnlitBSDF).x); // The current interface only allows legacy opacity transformed to transmittance as 1-Opacity. See UMaterialExpressionSubstratePostProcess.
// Substrate only use premultiplied alpha blending state (except for modulate and holdout) so we need to convert to the here.
#if MATERIALBLENDING_SOLID || MATERIALBLENDING_MASKED
EmissiveColor = PPColor;
#elif MATERIALBLENDING_ALPHACOMPOSITE
EmissiveColor = PPColor; // Premultipled alpha already baked in by the user
#elif MATERIALBLENDING_ALPHAHOLDOUT
EmissiveColor = 0.0;
#elif MATERIALBLENDING_TRANSLUCENT
EmissiveColor = PPColor * PPAlpha;
#elif MATERIALBLENDING_ADDITIVE
EmissiveColor = PPColor; // not affected by alpha
#elif MATERIALBLENDING_MODULATE
EmissiveColor = PPColor; // not affected by alpha
#else
EmissiveColor = PPColor;
#endif
#if SUBSTRATE_USE_PREMULTALPHA_OVERRIDE // AlphaComposite - Premultiplied alpha blending
PPAlpha = GetMaterialOpacity(PixelMaterialInputs);
#endif
#if MATERIAL_OUTPUT_OPACITY_AS_ALPHA
Alpha = PPAlpha;
#endif
#else // SUBSTRATE_ENABLED
// Grab emissive colour as output
#if MATERIAL_OUTPUT_OPACITY_AS_ALPHA
Alpha = GetMaterialOpacity(PixelMaterialInputs);
#endif
EmissiveColor = GetMaterialEmissive(PixelMaterialInputs);
#endif // SUBSTRATE_ENABLED
#ifdef POST_PROCESS_PROPAGATE_ALPHA_INPUT
Alpha = SceneTextureLookup(float2(0,0), PPI_PropagatedAlpha, false).r;
#endif
OutColor = float4(EmissiveColor, Alpha );
#if !MATERIALBLENDING_MODULATE && POST_PROCESS_MATERIAL_BEFORE_TONEMAP && !POST_PROCESS_DISABLE_PRE_EXPOSURE_SCALE
OutColor.xyz *= View.PreExposure;
#endif
}
void MainPS_VideoOverlay(
in float2 InUV : TEXCOORD0,
in float4 SvPosition : SV_Position, // after all interpolators
out float4 OutColor : SV_Target0
)
{
ResolvedView = ResolveView();
FMaterialPixelParameters Parameters = MakeInitializedMaterialPixelParameters();
FPixelMaterialInputs PixelMaterialInputs;
float2 ViewportUV = InUV;
#if NUM_MATERIAL_TEXCOORDS
for(int CoordinateIndex = 0;CoordinateIndex < NUM_MATERIAL_TEXCOORDS;CoordinateIndex++)
{
Parameters.TexCoords[CoordinateIndex] = ViewportUV;
}
#endif
Parameters.VertexColor = 1;
SvPosition.z = LookupDeviceZ(ViewportUVToBufferUV(ViewportUV));
SvPosition.z = max(SvPosition.z, 1e-18);
// fill out other related material parameters
CalcMaterialParametersPost(Parameters, PixelMaterialInputs, SvPosition, true);
// Grab emissive colour as output
#if MATERIAL_OUTPUT_OPACITY_AS_ALPHA
const float Alpha = GetMaterialOpacity(PixelMaterialInputs);
#else
// In Unreal, an alpha channel value of 0.0f means that the pixel is opaque and a value of 1.0f means that there is translucency.
// This is the opposite of how the alpha channel is interpreted in formats like PNG. For this reason, rendered images that are exported
// from the engine will have their alpha channel inverted to match the PNG convention. However, for consistency inside the rendering chain,
// we need to stick to 0.0f as the default value.
const float Alpha = 0.0f;
#endif
OutColor = float4(GetMaterialEmissive(PixelMaterialInputs), Alpha );
}
#else // !VERTEXSHADER && !PIXELSHADER
#error Wrong shader domain.
#endif
#else // FEATURE_LEVEL > FEATURE_LEVEL_ES3_1
//
// Mobile version
//
void MainVS(
in float4 InPosition : ATTRIBUTE0,
in float2 InTexCoord : ATTRIBUTE1,
out FPostProcessMaterialVSToPS Output
)
{
Output = (FPostProcessMaterialVSToPS)0;
float2 OutUV;
DrawRectangle(InPosition, InTexCoord, Output.Position, OutUV);
#if NUM_TEX_COORD_INTERPOLATORS
FMaterialVertexParameters VertexParameters = GetPostProcessMaterialVSParameters(InPosition.xy);
float2 CustomizedUVs[NUM_TEX_COORD_INTERPOLATORS];
GetMaterialCustomizedUVs(VertexParameters, CustomizedUVs);
GetCustomInterpolators(VertexParameters, CustomizedUVs);
UNROLL
for (int CoordinateIndex = 0; CoordinateIndex < NUM_MATERIAL_TEXCOORDS; CoordinateIndex++)
{
#if POST_PROCESS_AR_PASSTHROUGH
SetUV(Output, CoordinateIndex, OutUV);
#else
SetUV(Output, CoordinateIndex, InPosition.xy);
#endif
}
UNROLL
for (int CoordinateIndex = NUM_MATERIAL_TEXCOORDS; CoordinateIndex < NUM_TEX_COORD_INTERPOLATORS; CoordinateIndex++)
{
SetUV(Output, CoordinateIndex, CustomizedUVs[CoordinateIndex]);
}
#endif
}
void MainPS(
in FPostProcessMaterialVSToPS Input,
out half4 OutColor : SV_Target0
)
{
ResolvedView = ResolveView();
FMaterialPixelParameters Parameters = MakeInitializedMaterialPixelParameters();
FPixelMaterialInputs PixelMaterialInputs;
// can be optimized
float4 SvPosition = Input.Position;
float2 ViewportUV = (SvPosition.xy - PostProcessOutput_ViewportMin.xy) * PostProcessOutput_ViewportSizeInverse.xy;
float2 BufferUV = ViewportUVToBufferUV(ViewportUV);
#if NUM_TEX_COORD_INTERPOLATORS
UNROLL
for (int CoordinateIndex = 0; CoordinateIndex < NUM_MATERIAL_TEXCOORDS; CoordinateIndex++)
{
#if POST_PROCESS_AR_PASSTHROUGH
Parameters.TexCoords[CoordinateIndex] = GetUV(Input, CoordinateIndex);
#else
Parameters.TexCoords[CoordinateIndex] = ViewportUV;
#endif
}
UNROLL
for (int CoordinateIndex = NUM_MATERIAL_TEXCOORDS; CoordinateIndex < NUM_TEX_COORD_INTERPOLATORS; CoordinateIndex++)
{
Parameters.TexCoords[CoordinateIndex] = GetUV(Input, CoordinateIndex);
}
#endif
Parameters.VertexColor = 1;
float ClipValue = 1.0f;
float DeviceZ = LookupDeviceZ(BufferUV);
SvPosition.z = DeviceZ;
SvPosition.z = max(SvPosition.z, 1e-18);
// fill out other related material parameters
CalcMaterialParametersPost(Parameters, PixelMaterialInputs, SvPosition, true);
float Alpha = 0.0f;
float3 EmissiveColor = 0.0f;
#if SUBSTRATE_ENABLED
// Unlit forces a single BSDF
FSubstrateBSDF UnlitBSDF = PixelMaterialInputs.FrontMaterial.InlinedBSDF;
UnlitBSDF.SubstrateSanitizeBSDF();
half3 PPColor = BSDF_GETEMISSIVE(UnlitBSDF);
half PPAlpha = saturate(1.0 - UNLIT_TRANSMITTANCE(UnlitBSDF).x); // The current interface only allows legacy opacity transformed to transmittance as 1-Opacity. See UMaterialExpressionSubstratePostProcess.
// Substrate only use premultiplied alpha blending state (except for modulate and holdout) so we need to convert to the here.
#if MATERIALBLENDING_SOLID || MATERIALBLENDING_MASKED
EmissiveColor = PPColor;
#elif MATERIALBLENDING_ALPHACOMPOSITE
EmissiveColor = PPColor; // Premultipled alpha already baked in by the user
#elif MATERIALBLENDING_ALPHAHOLDOUT
EmissiveColor = 0.0;
#elif MATERIALBLENDING_TRANSLUCENT
EmissiveColor = PPColor * PPAlpha;
#elif MATERIALBLENDING_ADDITIVE
EmissiveColor = PPColor; // not affected by alpha
#elif MATERIALBLENDING_MODULATE
EmissiveColor = PPColor; // not affected by alpha
#else
EmissiveColor = PPColor;
#endif
#if SUBSTRATE_USE_PREMULTALPHA_OVERRIDE // AlphaComposite - Premultiplied alpha blending
PPAlpha = GetMaterialOpacity(PixelMaterialInputs);
#endif
#if MATERIAL_OUTPUT_OPACITY_AS_ALPHA
Alpha = PPAlpha;
#endif
#else // SUBSTRATE_ENABLED
// Grab emissive colour as output
EmissiveColor = GetMaterialEmissive(PixelMaterialInputs);
#if MATERIAL_OUTPUT_OPACITY_AS_ALPHA
Alpha = GetMaterialOpacity(PixelMaterialInputs);
#else
Alpha = 0.0f;
#endif
#endif // SUBSTRATE_ENABLED
half4 FullSceneColor = half4(EmissiveColor, Alpha);
#if POST_PROCESS_MATERIAL_BEFORE_TONEMAP
#if OUTPUT_GAMMA_SPACE
FullSceneColor.rgb = sqrt(FullSceneColor.rgb);
#endif
#if !MATERIALBLENDING_MODULATE
FullSceneColor.xyz *= View.PreExposure;
#endif
#endif
OutColor = FullSceneColor;
}
#endif // FEATURE_LEVEL > FEATURE_LEVEL_ES3_1