// Copyright Epic Games, Inc. All Rights Reserved. #include "/Engine/Private/Common.ush" #include "/Engine/Shared/SubstrateDefinitions.h" #if PERMUTATION_TILETYPE == 0 #define SUBSTRATE_FASTPATH 1 #elif PERMUTATION_TILETYPE == 1 #define SUBSTRATE_SINGLEPATH 1 #elif PERMUTATION_TILETYPE == 2 // COMPLEX PATH #else #error Substrate tile type non-implemented #endif #define OpaqueBasePass #define SUBSTRATE_MATERIALCONTAINER_IS_WRITABLE 1 #define SUBSTRATE_INLINE_SHADING 0 #define SUBSTRATE_SSS_MATERIAL_OVERRIDE 0 #include "/Engine/Private/Substrate/Substrate.ush" #include "SubstrateTile.ush" //////////////////////////////////////////////////////////////////////////////////////////////////////////// // Post Dbuffer application #if SHADER_DBUFFER Texture2D DBufferATexture; Texture2D DBufferBTexture; Texture2D DBufferCTexture; Texture2D DBufferRenderMask; SamplerState DBufferATextureSampler; SamplerState DBufferBTextureSampler; SamplerState DBufferCTextureSampler; #ifndef SUBSTRATE_STENCIL_DBUFFER_MASK #error SUBSTRATE_STENCIL_DBUFFER_MASK needs to be defined #endif #ifndef STENCIL_SUBSTRATE_RECEIVE_DBUFFER_NORMAL_BIT_ID #error STENCIL_SUBSTRATE_RECEIVE_DBUFFER_NORMAL_BIT_ID needs to be defined #endif #ifndef STENCIL_SUBSTRATE_RECEIVE_DBUFFER_DIFFUSE_BIT_ID #error STENCIL_SUBSTRATE_RECEIVE_DBUFFER_DIFFUSE_BIT_ID needs to be defined #endif #ifndef STENCIL_SUBSTRATE_RECEIVE_DBUFFER_ROUGHNESS_BIT_ID #error STENCIL_SUBSTRATE_RECEIVE_DBUFFER_ROUGHNESS_BIT_ID needs to be defined #endif // Sanity check #if !defined(USE_DBUFFER) || USE_DBUFFER != 1 #error This shader should only be compiled when USE_DBUFFER == 1 #endif #define MATERIALDECALRESPONSEMASK 0x7 #define ENABLE_DBUFFER_TEXTURES 1 #define DBUFFER_BASEPASS_RESOURCE 0 #define OpaqueBasePassDBufferATexture DBufferATexture #define OpaqueBasePassDBufferBTexture DBufferBTexture #define OpaqueBasePassDBufferCTexture DBufferCTexture #define OpaqueBasePassDBufferRenderMask DBufferRenderMask #if SUPPORTS_INDEPENDENT_SAMPLERS #define OpaqueBasePassDBufferATextureSampler DBufferATextureSampler #define OpaqueBasePassDBufferBTextureSampler DBufferATextureSampler #define OpaqueBasePassDBufferCTextureSampler DBufferATextureSampler #else #define OpaqueBasePassDBufferATextureSampler DBufferATextureSampler #define OpaqueBasePassDBufferBTextureSampler DBufferBTextureSampler #define OpaqueBasePassDBufferCTextureSampler DBufferCTextureSampler #endif #include "../DBufferDecalShared.ush" int2 ViewResolution; uint MaxBytesPerPixel; uint FirstSliceStoringSubstrateSSSData; RWTexture2D TopLayerTexture; RWTexture2DArray MaterialTextureArrayUAV; uint TileEncoding; uint TileListBufferOffset; Buffer TileListBuffer; Buffer TileIndirectBuffer; // TODO // * Add support Eye, Hair, Water, ... ? // * Refactor encoding to share code between Substrate export [numthreads(SUBSTRATE_TILE_SIZE, SUBSTRATE_TILE_SIZE, 1)] void MainCS( uint2 DispatchThreadId : SV_DispatchThreadID, uint LinearIndex : SV_GroupIndex, uint3 GroupId : SV_GroupID, uint2 GroupThreadId : SV_GroupThreadID) { const uint2 TileCoord = SubstrateUnpackTile(TileListBuffer[TileListBufferOffset + GroupId.x], TileEncoding); const uint2 PixelCoord = TileCoord * SUBSTRATE_TILE_SIZE + GroupThreadId; const bool bIsValid = all(PixelCoord < uint2(View.ViewSizeAndInvSize.xy)); if (!bIsValid) { return; } // Early out of no decal has been written or if the pixel has no 'decal responseness' const uint ResponseMask = SceneStencilTexture.Load(uint3(PixelCoord, 0)) STENCIL_COMPONENT_SWIZZLE; uint ValidDBufferTargetMask = GetDBufferTargetMask(PixelCoord); BRANCH if (!ValidDBufferTargetMask || (ResponseMask & SUBSTRATE_STENCIL_DBUFFER_MASK) == 0) { return; } // Load Dbuffer data only attributes for which the material has some 'responseness' ValidDBufferTargetMask = ValidDBufferTargetMask & ( (((ResponseMask >> STENCIL_SUBSTRATE_RECEIVE_DBUFFER_DIFFUSE_BIT_ID) & 0x1) << SUBSTRATE_DBUFFER_RESPONSE_BASECOLOR_BIT_OFFSET) | (((ResponseMask >> STENCIL_SUBSTRATE_RECEIVE_DBUFFER_NORMAL_BIT_ID) & 0x1) << SUBSTRATE_DBUFFER_RESPONSE_NORMAL_BIT_OFFSET) | (((ResponseMask >> STENCIL_SUBSTRATE_RECEIVE_DBUFFER_ROUGHNESS_BIT_ID) & 0x1) << SUBSTRATE_DBUFFER_RESPONSE_ROUGHNESS_BIT_OFFSET) ); const float2 BufferUV = SvPositionToBufferUV(float4(PixelCoord + 0.5f, 0, 0)); const FSubstrateDBuffer DBufferData = SubstrateGetDBufferData(BufferUV, ValidDBufferTargetMask); // Early out if DBuffer has no coverage BRANCH if (DBufferData.Coverage <= 0) { return; } FSubstrateAddressing SubstrateAddressing = GetSubstratePixelDataByteOffset(PixelCoord, uint2(View.BufferSizeAndInvSize.xy), MaxBytesPerPixel); FSubstratePixelHeader SubstratePixelHeader = UnpackSubstrateHeaderIn(MaterialTextureArrayUAV, SubstrateAddressing, TopLayerTexture); BRANCH if (SubstratePixelHeader.ClosureCount <= 0) { return; } BRANCH if (SubstratePixelHeader.IsSimpleMaterial() || SubstratePixelHeader.IsSingleMaterial()) { // 1. Load BSDF data const float Dither = InterleavedGradientNoise(PixelCoord + 0.5f, View.StateFrameIndexMod8); FSubstrateBSDF BSDF = UnpackSubstrateBSDFIn(MaterialTextureArrayUAV, SubstrateAddressing, SubstratePixelHeader); // 2. Load DBuffer data and apply them float3 F0 = 0; float3 DiffuseAlbedo = 0; float Roughness = 0; { Roughness = SubstrateGetBSDFRoughness(BSDF); float3 BaseColor = 0; float Metallic = 0; float Specular = 0; ConvertToMetalness(SLAB_DIFFUSEALBEDO(BSDF), SLAB_F0(BSDF), BaseColor, Specular, Metallic); Metallic = Metallic * DBufferData.OneMinusCoverage_Roughness + DBufferData.Metallic; Specular = Specular * DBufferData.OneMinusCoverage_Roughness + DBufferData.Specular; Roughness = Roughness * DBufferData.OneMinusCoverage_Roughness + DBufferData.Roughness; BaseColor = BaseColor * DBufferData.OneMinusCoverage_BaseColor + DBufferData.BaseColor; ConvertFromMetalness(BaseColor, Specular, Metallic, DiffuseAlbedo, F0); } // 3. Top layer data FSubstrateTopLayerData TopLayerData = SubstrateUnpackTopLayerData(SubstratePixelHeader.PackedTopLayerData); TopLayerData.WorldNormal = TopLayerData.WorldNormal * DBufferData.OneMinusCoverage_WorldNormal + DBufferData.WorldNormal; TopLayerData.Roughness = Roughness; TopLayerTexture[PixelCoord] = SubstratePackTopLayerData(TopLayerData); // 4. Rencode data // SUBSTRATE_TODO: For now, doing manual reencoding by duplicating logic from Substrate export. Need to refactor Substrate export to have small reusable packing functions // Roughness / DiffuseColor / F0 if (SubstratePixelHeader.IsSimpleMaterial()) { const uint PackedDiffuse20Bits = PackR7G7B6Gamma2(DiffuseAlbedo, Dither); const uint PackedDiffuse12Bits = PackedDiffuse20Bits & 0xFFF; const uint PackedDiffuse8Bits = (PackedDiffuse20Bits >> 12) & 0xFF; const uint PackedF020Bits = PackR7G7B6Gamma2(F0, Dither); const uint PackedRoughness8bits= PackR8(Roughness); // Data0 (Header_State|Header_AO|Roughness|Diffuse8bits) MaterialTextureArrayUAV[uint3(PixelCoord, 0)] = (SubstratePixelHeader.PackedHeader & 0x0000FFFF) | (PackedRoughness8bits << 16) | (PackedDiffuse8Bits << 24); MaterialTextureArrayUAV[uint3(PixelCoord, 1)] = PackedF020Bits | (PackedDiffuse12Bits << 20); } #if !SUBSTRATE_FASTPATH else if (SubstratePixelHeader.IsSingleMaterial()) { const uint OptimisedLegacyMode = (SubstratePixelHeader.PackedHeader >> (HEADER_SINGLEENCODING_BIT_COUNT)) & HEADER_SINGLE_OPTLEGACYMODE_BIT_MASK; if (OptimisedLegacyMode == SINGLE_OPTLEGACYMODE_NONE) { MaterialTextureArrayUAV[uint3(PixelCoord, 2)] = (MaterialTextureArrayUAV[uint3(PixelCoord, 2)] & 0xFFFF00FF) | (PackR8(Roughness) << 8); const uint PackedDiffuse20Bits = PackR7G7B6Gamma2(DiffuseAlbedo, Dither); const uint PackedF020Bits = PackR7G7B6Gamma2(F0, Dither); const uint PackedData32Bits = (PackedDiffuse20Bits << 12) | (PackedF020Bits & 0xFFF); const uint PackedData8Bits = (PackedF020Bits >> 12) & 0xFF; uint RWOffset = 1; MaterialTextureArrayUAV[uint3(PixelCoord, RWOffset++)] = PackedData32Bits; MaterialTextureArrayUAV[uint3(PixelCoord, RWOffset++)] = PackedData8Bits | PackRGBA8(float4(0.0f, Roughness, (SLAB_ANISOTROPY(BSDF) + 1.f) * 0.5f, (SLAB_SSSPHASEANISOTROPY(BSDF) + 1.f) * 0.5f)); if (BSDF_GETHASF90(BSDF) || BSDF_GETHASHAZINESS(BSDF)) { if (BSDF_GETHASHAZINESS(BSDF)) { FHaziness Haziness = UnpackHaziness(SLAB_HAZINESS(BSDF)); Haziness.Weight *= DBufferData.OneMinusCoverage_Roughness; const uint PackedHaziness = PackHaziness(Haziness); MaterialTextureArrayUAV[uint3(PixelCoord, RWOffset)] = (MaterialTextureArrayUAV[uint3(PixelCoord, RWOffset)] & 0x0000FFFF) | (PackedHaziness << 16); } ++RWOffset; } const bool bHasSSS = BSDF_GETSSSTYPE(BSDF) != SSS_TYPE_NONE; if (bHasSSS) { // Disable SSS when the coverage reach one to preserve the decal color SLAB_SSSMFP(BSDF) *= DBufferData.OneMinusCoverage_BaseColor; if (DBufferData.OneMinusCoverage_BaseColor == 0.0f) { BSDF_SETSSSTYPE(BSDF, SSS_TYPE_NONE); } if (BSDF_GETSSSTYPE(BSDF) == SSS_TYPE_DIFFUSION_PROFILE) { // Nothing to do } else { // Path used for bottom most layer SSS, simple volume and two sided lighting. MaterialTextureArrayUAV[uint3(PixelCoord, RWOffset)] = PackR11G11B10F(SLAB_SSSMFP(BSDF)); } // Only write SSS data if needed if (BSDF_GETSSSTYPE(BSDF) == SSS_TYPE_DIFFUSION) { FSubstrateSubsurfaceExtras SSSDataExtras; SubstrateSubsurfaceExtrasSetBaseColor(SSSDataExtras, DiffuseAlbedo); SubstrateStoreSubsurfaceExtras(MaterialTextureArrayUAV, FirstSliceStoringSubstrateSSSData, PixelCoord, SSSDataExtras.Bytes); } ++RWOffset; } if (BSDF_GETHASFUZZ(BSDF)) { MaterialTextureArrayUAV[uint3(PixelCoord, RWOffset)] = PackFuzz( SLAB_FUZZ_COLOR(BSDF), SLAB_FUZZ_AMOUNT(BSDF) * DBufferData.OneMinusCoverage_BaseColor, SLAB_FUZZ_ROUGHNESS(BSDF), Dither); RWOffset++; } } else if (OptimisedLegacyMode == SINGLE_OPTLEGACYMODE_CLEARCOAT) { const uint InData2 = MaterialTextureArrayUAV[uint3(PixelCoord, 2)]; // Clear coat disappear with coverage FHaziness Haziness = UnpackHaziness(InData2); Haziness.Weight *= DBufferData.OneMinusCoverage_BaseColor; Haziness.BottomNormalOct = lerp(DEFAULT_CLEAR_COAT_BOTTOM_NORMAL_OCT, Haziness.BottomNormalOct, DBufferData.OneMinusCoverage_BaseColor); const uint PackedHaziness = PackHaziness(Haziness); MaterialTextureArrayUAV[uint3(PixelCoord, 2)] = PackedHaziness; } else if (OptimisedLegacyMode == SINGLE_OPTLEGACYMODE_CLOTH) { // Roughness is only stored in the TopLayer texture. // Diffuse Albedo & F0 const uint PackedDiffuse20Bits = PackR7G7B6Gamma2(DiffuseAlbedo, Dither); const uint PackedF020Bits = PackR7G7B6Gamma2(F0, Dither); const uint PackedFuzzColor20bits = PackR7G7B6Gamma2(SLAB_FUZZ_COLOR(BSDF), Dither); const uint PackedFuzzAmount8bits = PackR8(SLAB_FUZZ_AMOUNT(BSDF) * DBufferData.OneMinusCoverage_BaseColor); const uint FuzzAmountOffset = HEADER_SINGLEENCODING_BIT_COUNT + HEADER_SINGLE_OPTLEGACYMODE_BIT_COUNT; const uint FuzzAmountMask = (1u << FuzzAmountOffset) - 1u; MaterialTextureArrayUAV[uint3(PixelCoord, 0)] = (MaterialTextureArrayUAV[uint3(PixelCoord, 0)] & FuzzAmountMask) | (PackedFuzzAmount8bits << FuzzAmountOffset); MaterialTextureArrayUAV[uint3(PixelCoord, 1)] = (PackedDiffuse20Bits | ((PackedFuzzColor20bits & 0xFFC00) << 10)); MaterialTextureArrayUAV[uint3(PixelCoord, 2)] = (PackedF020Bits | ((PackedFuzzColor20bits & 0x3FF) << 20)); } else if (OptimisedLegacyMode == SINGLE_OPTLEGACYMODE_SSSWRAP || OptimisedLegacyMode == SINGLE_OPTLEGACYMODE_TWO_SIDED_SSSWRAP) { // Roughness is only stored in the TopLayer texture. const float3 MFP = SLAB_SSSMFP(BSDF) * DBufferData.OneMinusCoverage_BaseColor; // DiffuseAlbedo & F0 const uint PackedDiffuse20Bits = PackR7G7B6Gamma2(DiffuseAlbedo, Dither); const uint PackedF020Bits = PackR7G7B6Gamma2(F0, Dither); const uint PackedSSSMFP30bits = PackR10G10B10F(MFP); const uint PackedSSSMFP12BitsA = (PackedSSSMFP30bits) & 0xFFF; // 12 lower bits const uint PackedSSSMFP12BitsB = (PackedSSSMFP30bits >> 12) & 0xFFF; // next 12 lower bits const uint PackedSSSMFP6BitsC = (PackedSSSMFP30bits >> 24) & 0x03F; // 6 higher bits const uint BitShift = (7 + HEADER_SINGLEENCODING_BIT_COUNT + HEADER_SINGLE_OPTLEGACYMODE_BIT_COUNT); MaterialTextureArrayUAV[uint3(PixelCoord, 0)] = (MaterialTextureArrayUAV[uint3(PixelCoord, 0)] & 0x03FFFFFF) | (PackedSSSMFP6BitsC<< BitShift); MaterialTextureArrayUAV[uint3(PixelCoord, 1)] = PackedDiffuse20Bits | (PackedSSSMFP12BitsA << 20); MaterialTextureArrayUAV[uint3(PixelCoord, 2)] = PackedF020Bits | (PackedSSSMFP12BitsB << 20); } else if (OptimisedLegacyMode == SINGLE_OPTLEGACYMODE_SSSPROFILE) { const uint PackedDiffuse20Bits = PackR7G7B6Gamma2(DiffuseAlbedo, Dither); const uint PackedF020Bits = PackR7G7B6Gamma2(F0, Dither); FSubstrateSubsurfaceHeader SSSDataHeader; SSSDataHeader.Bytes = MaterialTextureArrayUAV[uint3(PixelCoord, FirstSliceStoringSubstrateSSSData + 0)]; float ProfileScl= SubstrateSubSurfaceHeaderGetProfileRadiusScale(SSSDataHeader) * DBufferData.OneMinusCoverage_BaseColor; uint ProfileId = SubstrateSubSurfaceHeaderGetProfileId(SSSDataHeader); SubstrateSubSurfaceHeaderSetProfile(SSSDataHeader, ProfileScl, DBufferData.OneMinusCoverage_BaseColor == 0.0f ? 0 : ProfileId); // Note We cannot change the type using SubstrateSubSurfaceHeaderSetSSSType(SSSDataHeader, SSS_TYPE_NONE); // Because we are stuck using the SINGLE_OPTLEGACYMODE_SSSPROFILE mode. // So some SSSProfile material with decal will look difference when using deferred DBuffer instead of in shader application. MaterialTextureArrayUAV[uint3(PixelCoord, 0)] = (MaterialTextureArrayUAV[uint3(PixelCoord, 0)] & 0x00FFFFFF) | (PackR8(Roughness) << 24); MaterialTextureArrayUAV[uint3(PixelCoord, 1)] = PackedDiffuse20Bits | (PackR8(ProfileScl) << 24); MaterialTextureArrayUAV[uint3(PixelCoord, 2)] = (MaterialTextureArrayUAV[uint3(PixelCoord, 2)] & 0xFF000000) | PackedF020Bits; SubstrateStoreSubsurfaceHeader(MaterialTextureArrayUAV, FirstSliceStoringSubstrateSSSData, PixelCoord, SSSDataHeader.Bytes); if (SubstrateSubSurfaceHeaderHasExtras(SSSDataHeader)) { FSubstrateSubsurfaceExtras SSSDataExtras; SubstrateSubsurfaceExtrasSetBaseColor(SSSDataExtras, DiffuseAlbedo); SubstrateStoreSubsurfaceExtras(MaterialTextureArrayUAV, FirstSliceStoringSubstrateSSSData, PixelCoord, SSSDataExtras.Bytes); } } } #endif // !SUBSTRATE_FASTPATH } } #endif // SHADER_DBUFFER