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

150 lines
6.7 KiB
HLSL

// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
struct FLumenCardBasePassOutput
{
float4 Target0;
float4 Target1;
float4 Target2;
};
FLumenCardBasePassOutput LumenCardBasePass(FPixelMaterialInputs PixelMaterialInputs, FMaterialPixelParameters MaterialParameters, float4 SvPosition)
{
#if TEMPLATE_USES_SUBSTRATE
const float3 V = MaterialParameters.WorldNormal; // Use the normal to avoid view dependent transmittance effects when baking non directional card data.
const float3 L = MaterialParameters.WorldNormal;
float3 DiffuseColor = 0;
float3 SpecularColor = 0;
float3 WorldNormal = 0;
float3 Emissive = 0;
float TotalCoverage = 1.f;
// Initialise a Substrate header with normal in registers
FSubstrateData SubstrateData = PixelMaterialInputs.GetFrontSubstrateData();
FSubstratePixelHeader SubstratePixelHeader = MaterialParameters.GetFrontSubstrateHeader();
#if SUBSTRATE_OPTIMIZED_UNLIT
// Unlit BSDF goes through the SubstrateTree to support weighting operations. Technically, layering and mixing could also be supported.
float3 UnlitSurfaceTransmittancePreCoverage = 0.0f;
float3 UnlitSurfaceNormal = 0.0f;
SubstratePixelHeader.SubstrateUpdateTreeUnlit(
uint2(SvPosition.xy),
MaterialParameters.CameraVector,
SubstrateData,
Emissive,
TotalCoverage,
UnlitSurfaceTransmittancePreCoverage,
UnlitSurfaceNormal);
#else // SUBSTRATE_OPTIMIZED_UNLIT
SubstratePixelHeader.IrradianceAO.MaterialAO = GetMaterialAmbientOcclusion(PixelMaterialInputs);
if (SubstratePixelHeader.SubstrateTree.BSDFCount > 0)
{
// To have better 'specular as diffuse' response, we use bForceFullyRoughAccurate which computes specular & diffuse separately.
// They are both merge them manually at the end, as Lumen card stores only diffuse albedo.
// Match behavior in LumenHardwareRayTracingCommon.ush
FSubstrateIntegrationSettings Settings = InitSubstrateIntegrationSettings(true /*bForceFullyRough*/, true /*bRoughDiffuseEnabled*/, 0 /*PeelLayersAboveDepth*/, false/*bRoughnessTracking*/);
Settings.bForceFullyRoughAccurate = true;
float3 TotalTransmittancePreCoverage = 0;
SubstratePixelHeader.SubstrateUpdateTree(SubstrateData, V, Settings, TotalCoverage, TotalTransmittancePreCoverage);
// Extract averaged DiffuseAlbedo / SpecularColor
SUBSTRATE_UNROLL_N(SUBSTRATE_CLAMPED_CLOSURE_COUNT)
for (int BSDFIdx = 0; BSDFIdx < SubstratePixelHeader.SubstrateTree.BSDFCount; ++BSDFIdx)
{
#define CurrentBSDF SubstratePixelHeader.SubstrateTree.BSDFs[BSDFIdx]
if (SubstrateIsBSDFVisible(CurrentBSDF))
{
FSubstrateAddressing NullSubstrateAddressing = (FSubstrateAddressing)0; // Fake unused in SubstrateCreateBSDFContext when using Forward inline shading
FSubstrateBSDFContext SubstrateBSDFContext = SubstrateCreateBSDFContext(SubstratePixelHeader, CurrentBSDF, NullSubstrateAddressing, V, L);
FSubstrateEnvLightResult SubstrateEnvLight = SubstrateEvaluateForEnvLight(SubstrateBSDFContext, true /*bEnableSpecular*/, Settings);
// Use LuminanceWeightV instead of LuminanceWeight(..) as we only need to weight these value with the view transmittance, not the light transmittance;
const float3 Weight = CurrentBSDF.LuminanceWeightV;
DiffuseColor += Weight * (SubstrateEnvLight.DiffuseWeight + SubstrateEnvLight.DiffuseBackFaceWeight); // This is not correct, as the albedo/diffuse color can go above 1. However this is necessary to match legacy behavior.
SpecularColor += Weight * SubstrateEnvLight.SpecularWeight;
SpecularColor += Weight * SubstrateEnvLight.SpecularHazeWeight;
WorldNormal += Weight * SubstrateBSDFContext.N;
Emissive += Weight * CurrentBSDF.Emissive;
// Lumen card stores only diffuse albedo, so merge specular & diffuse color,
// which are propertly weighted respectively to each other
DiffuseColor = SpecularColor + DiffuseColor;
// Note : The legacy path uses a different formulation for composition diffuse and specular color. This result in lighter albedo.
// If the above formulation causes issues for legacy projects, one could re-enable the code below:
// DiffuseColor = Weight * SubstrateEnvLight.DiffuseColor;
// SpecularColor = Weight * SubstrateEnvLight.SpecularColor;
// EnvBRDFApproxFullyRough(DiffuseColor, SpecularColor);
}
#undef CurrentBSDF
}
}
#endif // SUBSTRATE_OPTIMIZED_UNLIT
float Opacity = 1.0f;
#if MATERIALBLENDING_ANY_TRANSLUCENT
Opacity = TotalCoverage > 0.5f ? 1.0f : 0.0f;
#elif MATERIALBLENDING_MASKED
Opacity = GetMaterialMask(PixelMaterialInputs) >= 0.0f ? 1.0f : 0.0f;
#endif
#else // TEMPLATE_USES_SUBSTRATE
float3 BaseColor = GetMaterialBaseColor(PixelMaterialInputs);
float Metallic = GetMaterialMetallic(PixelMaterialInputs);
float Specular = GetMaterialSpecular(PixelMaterialInputs);
float Roughness = GetMaterialRoughness(PixelMaterialInputs);
float3 Emissive = GetMaterialEmissive(PixelMaterialInputs);
float3 WorldNormal = MaterialParameters.WorldNormal;
float Opacity = 1.0f;
#if MATERIALBLENDING_ANY_TRANSLUCENT
Opacity = GetMaterialOpacity(PixelMaterialInputs) > 0.5f ? 1.0f : 0.0f;
#elif MATERIALBLENDING_MASKED
Opacity = GetMaterialMask(PixelMaterialInputs) >= 0.0f ? 1.0f : 0.0f;
#endif
float3 DiffuseColor = BaseColor - BaseColor * Metallic;
float3 SpecularColor = lerp(0.08f * Specular.xxx, BaseColor, Metallic.xxx);
EnvBRDFApproxFullyRough(DiffuseColor, SpecularColor);
uint ShadingModel = GetMaterialShadingModel(PixelMaterialInputs);
float3 SubsurfaceColor = 0.0f;
// Match the logic in BasePassPixelShader.usf. MATERIAL_SHADINGMODEL_SUBSURFACE_PROFILE and MATERIAL_SHADINGMODEL_EYE
// are ignored from the following logic as the subsurface color does not contribute to them.
#if MATERIAL_SHADINGMODEL_SUBSURFACE || MATERIAL_SHADINGMODEL_PREINTEGRATED_SKIN || MATERIAL_SHADINGMODEL_TWOSIDED_FOLIAGE || MATERIAL_SHADINGMODEL_CLOTH
if (ShadingModel == SHADINGMODELID_SUBSURFACE || ShadingModel == SHADINGMODELID_PREINTEGRATED_SKIN || ShadingModel == SHADINGMODELID_TWOSIDED_FOLIAGE || ShadingModel == SHADINGMODELID_CLOTH)
{
float4 SubsurfaceData = GetMaterialSubsurfaceData(PixelMaterialInputs);
DiffuseColor += SubsurfaceData.rgb;
}
#endif
#endif // TEMPLATE_USES_SUBSTRATE
// Normals are stored in local card space
float3 CardSpaceNormal = float3(0, 0, 1);
// As of 04/06/2022 sometimes we get NaN normals from Nanite meshes with WPO
if (all(IsFinite(WorldNormal)))
{
WorldNormal = normalize(WorldNormal);
CardSpaceNormal = mul(float4(WorldNormal, 0.0f), ResolvedView.TranslatedWorldToView).xyz;
}
FLumenCardBasePassOutput ShadedPixel;
ShadedPixel.Target0 = float4(sqrt(DiffuseColor), Opacity);
ShadedPixel.Target1 = float4(CardSpaceNormal.xy * 0.5f + 0.5f, 0.0f, /* bValid */ 1.0f);
ShadedPixel.Target2 = float4(Emissive * LumenCardPass.CachedLightingPreExposure, 0.0f);
return ShadedPixel;
}