// Copyright 1998-2014 Epic Games, Inc. All Rights Reserved. /*============================================================================= DeferredShadingCommon.usf: Common definitions for deferred shading. =============================================================================*/ #ifndef __DEFERRED_SHADING_COMMON__ #define __DEFERRED_SHADING_COMMON__ #include "LightAccumulator.usf" // TODO: for CustomGBufferResolvePS() MSAA_SAMPLE_COUNT is defined by C++ code as 2 or 4 // bot not for any other shaders! #ifndef MSAA_SAMPLE_COUNT #define MSAA_SAMPLE_COUNT 2 #endif float3 RGBToYCoCg( float3 RGB ) { float Y = dot( RGB, float3( 1, 2, 1 ) ) * 0.25; float Co = dot( RGB, float3( 2, 0, -2 ) ) * 0.25 + ( 0.5 * 256.0 / 255.0 ); float Cg = dot( RGB, float3( -1, 2, -1 ) ) * 0.25 + ( 0.5 * 256.0 / 255.0 ); float3 YCoCg = float3( Y, Co, Cg ); return YCoCg; } float3 YCoCgToRGB( float3 YCoCg ) { float Y = YCoCg.x; float Co = YCoCg.y - ( 0.5 * 256.0 / 255.0 ); float Cg = YCoCg.z - ( 0.5 * 256.0 / 255.0 ); float R = Y + Co - Cg; float G = Y + Cg; float B = Y - Co - Cg; float3 RGB = float3( R, G, B ); return RGB; } // Octahedron Normal Vectors // [Cigolle 2014, "A Survey of Efficient Representations for Independent Unit Vectors"] // Mean Max // oct 8:8 0.33709 0.94424 // snorm 8:8:8 0.17015 0.38588 // oct 10:10 0.08380 0.23467 // snorm 10:10:10 0.04228 0.09598 // oct 12:12 0.02091 0.05874 float2 UnitVectorToOctahedron( float3 N ) { N.xy /= dot( 1, abs(N) ); if( N.z <= 0 ) { N.xy = ( 1 - abs(N.yx) ) * ( N.xy >= 0 ? float2(1,1) : float2(-1,-1) ); } return N.xy; } float3 OctahedronToUnitVector( float2 Oct ) { float3 N = float3( Oct, 1 - dot( 1, abs(Oct) ) ); if( N.z < 0 ) { N.xy = ( 1 - abs(N.yx) ) * ( N.xy >= 0 ? float2(1,1) : float2(-1,-1) ); } return normalize(N); } float3 Pack1212To888( float2 x ) { // Pack 12:12 to 8:8:8 #if 0 uint2 x1212 = (uint2)( x * 4095.0 ); uint2 High = x1212 >> 8; uint2 Low = x1212 & 255; uint3 x888 = uint3( Low, High.x | (High.y << 4) ); return x888 / 255.0; #else float2 x1212 = floor( x * 4095 ); float2 High = floor( x1212 / 256 ); // x1212 >> 8 float2 Low = x1212 - High * 256; // x1212 & 255 float3 x888 = float3( Low, High.x + High.y * 16 ); return saturate( x888 / 255 ); #endif } float2 Pack888To1212( float3 x ) { // Pack 8:8:8 to 12:12 #if 0 uint3 x888 = (uint3)( x * 255.0 ); uint High = x888.z >> 4; uint Low = x888.z & 15; uint2 x1212 = x888.xy | uint2( Low << 8, High << 8 ); return x1212 / 4095.0; #else float3 x888 = floor( x * 255 ); float High = floor( x888.z / 16 ); // x888.z >> 4 float Low = x888.z - High * 16; // x888.z & 15 float2 x1212 = x888.xy + float2( Low, High ) * 256; return saturate( x1212 / 4095 ); #endif } float3 EncodeNormal( float3 N ) { return N * 0.5 + 0.5; //return Pack1212To888( UnitVectorToOctahedron( N ) * 0.5 + 0.5 ); } float3 DecodeNormal( float3 N ) { return N * 2 - 1; //return OctahedronToUnitVector( Pack888To1212( N ) * 2 - 1 ); } float3 EncodeSpecularColor(float3 SpecularColor) { // Allocate more precision to the darks, which is necessary with bright specular lighting and strong Fresnel return sqrt(saturate(SpecularColor)); } float3 DecodeSpecularColor(float3 SpecularColor) { return SpecularColor * SpecularColor; } #if USE_FRAMEBUFFER_SRGB float3 EncodeDiffuseColor(float3 DiffuseColor) { // we use sRGB on the render target to give more precision to the darks return DiffuseColor; } float3 DecodeDiffuseColor(float3 DiffuseColor) { // we use sRGB on the render target to give more precision to the darks return DiffuseColor; } float3 EncodeBaseColor(float3 BaseColor) { // we use sRGB on the render target to give more precision to the darks return BaseColor; } float3 DecodeBaseColor(float3 BaseColor) { // we use sRGB on the render target to give more precision to the darks return BaseColor; } #else // @todo: remove once Apple fixes radr://16754329 AMD Cards don't always perform FRAMEBUFFER_SRGB if the draw FBO has mixed sRGB & non-SRGB colour attachments float3 EncodeDiffuseColor(float3 DiffuseColor) { // Allocate more precision to the darks return sqrt(saturate(DiffuseColor)); } float3 DecodeDiffuseColor(float3 DiffuseColor) { return DiffuseColor * DiffuseColor; } float3 EncodeBaseColor(float3 BaseColor) { // Gamma 2.0 return sqrt( saturate(BaseColor) ); } float3 DecodeBaseColor(float3 BaseColor) { return Square( BaseColor ); } #endif float3 EncodeSubsurfaceColor(float3 SubsurfaceColor) { return sqrt(saturate(SubsurfaceColor)); } // @param SubsurfaceProfile 0..1, SubsurfaceProfileId = int(x * 255) float3 EncodeSubsurfaceProfile(float SubsurfaceProfile) { return float3(SubsurfaceProfile, 0, 0); } float EncodeIndirectIrradiance(float IndirectIrradiance) { float L = IndirectIrradiance; const float LogBlackPoint = 0.00390625; // exp2(-8); return log2( L + LogBlackPoint ) / 16 + 0.5; } float DecodeIndirectIrradiance(float IndirectIrradiance) { // LogL -> L float LogL = IndirectIrradiance; const float LogBlackPoint = 0.00390625; // exp2(-8); return exp2( LogL * 16 - 8 ) - LogBlackPoint; // 1 exp2, 1 smad, 1 ssub } float ComputeAngleFromRoughness( float Roughness, const float Threshold = 0.04f ) { #if 1 float Angle = 3 * Square( Roughness ); #else const float LogThreshold = log2( Threshold ); float Power = 0.5 / pow( Roughness, 4 ) - 0.5; float Angle = acos( exp2( LogThreshold / Power ) ); #endif return Angle; } float ComputeRoughnessFromAngle( float Angle, const float Threshold = 0.04f ) { #if 1 float Roughness = sqrt( 0.33333 * Angle ); #else const float LogThreshold = log2( Threshold ); float Power = LogThreshold / log2( cos( Angle ) ); float Roughness = sqrt( sqrt( 2 / (Power * 4 + 2) ) ); #endif return Roughness; } float AddAngleToRoughness( float Angle, float Roughness ) { return saturate( sqrt( Square( Roughness ) + 0.33333 * Angle ) ); } // @param Scalar clamped in 0..1 range // @param Mask 0..1 // @return 8bit in range 0..1 float Encode71(float Scalar, int Mask) { return 127.0f / 255.0f * saturate(Scalar) + 128.0f / 255.0f * Mask; } // 8bit reinterpretation as 7bit,1bit // @param Scalar 0..1 // @param Mask 0..1 // @return 7bit in 0.1 float Decode71(float Scalar, out int Mask) { Mask = (int)(Scalar > 0.5f); return (Scalar - 0.5f * Mask) * 2.0f; } uint DecodeShadingModelId(float InPackedChannel) { return (uint)( InPackedChannel * 255.999 ); } #define SHADINGMODELID_UNLIT 0 #define SHADINGMODELID_DEFAULT_LIT 1 #define SHADINGMODELID_SUBSURFACE 2 #define SHADINGMODELID_PREINTEGRATED_SKIN 3 #define SHADINGMODELID_CLEAR_COAT 4 #define SHADINGMODELID_SUBSURFACE_PROFILE 5 #define SHADINGMODELID_NUM 6 // all values that are output by the forward rendering pass struct FGBufferData { // normalized float3 WorldNormal; // 0..1 float3 DiffuseColor; // 0..1 float3 SpecularColor; // 0..1 float3 BaseColor; // 0..1 float Metallic; // 0..1 float Specular; // 0..1 float3 CustomData; // Indirect irradiance luma float IndirectIrradiance; // Static shadow factors for channels assigned by Lightmass // Lights using static shadowing will pick up the appropriate channel in their deferred pass float4 PrecomputedShadowFactors; // 0..1 float Roughness; // 0..1 float Opacity; // 0..1 ambient occlusion e.g.SSAO, wet surface mask, skylight mask, ... float GBufferAO; // 0..255 int ShadingModelID; // 0..1 decal receiver mask uint DecalMask; // 0..1 uint HasDistanceFieldRepresentation; // in world units float CustomDepth; // in unreal units (linear), can be used to reconstruct world position, // only valid when decoding the GBuffer as the value gets reconstructed from the Z buffer float Depth; }; // all values that are output by the forward rendering pass struct FDBufferData { // 0..1, premultiplied with ColorOpacity float3 Color; // 0:opaque ..1:see through float ColorOpacity; // -1..1, premultiplied with NormalOpacity float3 WorldNormal; // 0:opaque ..1:see through float NormalOpacity; // 0..1, premultiplied with RoughnessOpacity float Roughness; // 0:opaque ..1:see through float RoughnessOpacity; }; struct FScreenSpaceData { // GBuffer (material attributes from forward rendering pass) FGBufferData GBuffer; // 0..1, only valid in some passes, 1 if off float AmbientOcclusion; }; /** Populates OutGBufferA, B and C */ void EncodeGBuffer( FGBufferData GBuffer, out float4 OutGBufferA, out float4 OutGBufferB, out float4 OutGBufferC, out float4 OutGBufferD, out float4 OutGBufferE, float QuantizationBias = 0 // -0.5 to 0.5 random float. Used to bias quantization. ) { OutGBufferA.rgb = EncodeNormal( GBuffer.WorldNormal ); // compress in 2 bits uint PackedAlpha = GBuffer.DecalMask * 2 + (GBuffer.HasDistanceFieldRepresentation); OutGBufferA.a = PackedAlpha * (1.0 / 3.0); if (GBuffer.ShadingModelID == SHADINGMODELID_UNLIT) { OutGBufferB = 0; OutGBufferC = 0; OutGBufferD = 0; } else { bool bSubsurface = (GBuffer.ShadingModelID == SHADINGMODELID_SUBSURFACE || GBuffer.ShadingModelID == SHADINGMODELID_PREINTEGRATED_SKIN || GBuffer.ShadingModelID == SHADINGMODELID_SUBSURFACE_PROFILE); #if DIFFUSE_SPEC_INPUTS OutGBufferB.rgb = EncodeSpecularColor(GBuffer.SpecularColor); OutGBufferC.rgb = EncodeDiffuseColor(GBuffer.DiffuseColor); if (bSubsurface) { // Only Fortnite uses DIFFUSE_SPEC_INPUTS which doesn't have IndirectIrradiance, GBufferAO is lost OutGBufferC.a = GBuffer.Opacity; } else { // No space for AO. Multiply IndirectIrradiance by AO instead of storing. OutGBufferC.a = EncodeIndirectIrradiance(GBuffer.IndirectIrradiance * GBuffer.GBufferAO) + QuantizationBias * (1.0 / 255.0); } #else // NOTE OutGBufferB.b is currently unused! OutGBufferB.r = bSubsurface ? GBuffer.Opacity : GBuffer.Metallic; OutGBufferB.g = GBuffer.Specular; OutGBufferB.b = 0; OutGBufferC.rgb = EncodeBaseColor(GBuffer.BaseColor); // No space for AO. Multiply IndirectIrradiance by AO instead of storing. OutGBufferC.a = EncodeIndirectIrradiance(GBuffer.IndirectIrradiance * GBuffer.GBufferAO) + QuantizationBias * (1.0 / 255.0); #endif OutGBufferB.a = GBuffer.ShadingModelID * (1.0 / 255.0); // Roughness in OutGBufferD.r instead of OutGBufferB.a so that deferred decals can blend in roughness while using MRT OutGBufferD.r = GBuffer.Roughness; OutGBufferD.gba = GBuffer.CustomData; } OutGBufferE = GBuffer.PrecomputedShadowFactors; } /** Populates FGBufferData */ FGBufferData DecodeGBufferData( float4 InGBufferA, float4 InGBufferB, float4 InGBufferC, float4 InGBufferD, float4 InGBufferE, float CustomNativeDepth, float SceneDepth, bool bGetNormalizedNormal) { FGBufferData GBuffer; GBuffer.WorldNormal = DecodeNormal( InGBufferA.xyz ); if(bGetNormalizedNormal) { GBuffer.WorldNormal = normalize(GBuffer.WorldNormal); } uint PackedAlpha = (uint)( InGBufferA.a * 3.999 ); GBuffer.DecalMask = PackedAlpha & 2; GBuffer.HasDistanceFieldRepresentation = PackedAlpha & 1; GBuffer.ShadingModelID = DecodeShadingModelId(InGBufferB.a); bool bSubsurface = (GBuffer.ShadingModelID == SHADINGMODELID_SUBSURFACE || GBuffer.ShadingModelID == SHADINGMODELID_PREINTEGRATED_SKIN || GBuffer.ShadingModelID == SHADINGMODELID_SUBSURFACE_PROFILE); #if DIFFUSE_SPEC_INPUTS GBuffer.BaseColor = 0; GBuffer.Metallic = 0; GBuffer.Specular = 0; GBuffer.DiffuseColor = DecodeDiffuseColor(InGBufferC.rgb); GBuffer.SpecularColor = DecodeSpecularColor(InGBufferB.rgb); GBuffer.Opacity = bSubsurface ? InGBufferC.a : 0; #else GBuffer.BaseColor = DecodeBaseColor(InGBufferC.rgb); GBuffer.Metallic = bSubsurface ? 0 : InGBufferB.r; GBuffer.Specular = InGBufferB.g; GBuffer.DiffuseColor = GBuffer.BaseColor - GBuffer.BaseColor * GBuffer.Metallic; GBuffer.SpecularColor = lerp( 0.08 * GBuffer.Specular.xxx, GBuffer.BaseColor, GBuffer.Metallic ); // todo: COMPILE_SHADERS_FOR_DEVELOPMENT is unfinished feature, using XBOXONE_PROFILE as workaround #if COMPILE_SHADERS_FOR_DEVELOPMENT == 1 && !XBOXONE_PROFILE { // this feature is only needed for development/editor - we can compile it out for a shipping build (see r.CompileShadersForDevelopment cvar help) GBuffer.DiffuseColor = GBuffer.DiffuseColor * View.DiffuseOverrideParameter.www + View.DiffuseOverrideParameter.xyz; GBuffer.SpecularColor = GBuffer.SpecularColor * View.SpecularOverrideParameter.w + View.SpecularOverrideParameter.xyz; } #endif //COMPILE_SHADERS_FOR_DEVELOPMENT == 1 GBuffer.Opacity = bSubsurface ? InGBufferB.r : 0; #endif GBuffer.Roughness = InGBufferD.r; GBuffer.CustomData = InGBufferD.gba; GBuffer.GBufferAO = 1; GBuffer.IndirectIrradiance = DecodeIndirectIrradiance(InGBufferC.a); GBuffer.PrecomputedShadowFactors = InGBufferE; GBuffer.CustomDepth = ConvertFromDeviceZ(CustomNativeDepth); GBuffer.Depth = SceneDepth; return GBuffer; } /** Populates FDBufferData */ FDBufferData DecodeDBufferData( float4 DBufferA, float4 DBufferB, float2 DBufferC) { FDBufferData ret; // UNORM 4 channel ret.Color = DBufferA.rgb; ret.ColorOpacity = DBufferA.a; // UNORM 4 channel, 128/255 represents 0 ret.WorldNormal = DBufferB.rgb * 2 - (256.0 / 255.0); ret.NormalOpacity = DBufferB.a; // UNORM 2 channel ret.Roughness = DBufferC.r; ret.RoughnessOpacity = DBufferC.g; return ret; } /** Populates DBufferA, DBufferB, DBufferC as float4 and puts opacity in alpha for frame buffer blending */ // @param MultiOpacity .x: Color, .y:Normal, .z:Roughness void EncodeDBufferData(FGBufferData GBufferData, float3 MultiOpacity, out float4 DBufferA, out float4 DBufferB, out float4 DBufferC) { // UNORM 4 channel #if DIFFUSE_SPEC_INPUTS DBufferA = float4(GBufferData.DiffuseColor, MultiOpacity.x); #else DBufferA = float4(GBufferData.BaseColor, MultiOpacity.x); #endif // UNORM 4 channel, 128/255 represents 0 DBufferB = float4(GBufferData.WorldNormal * 0.5f + 128.0f/255.0f, MultiOpacity.y); // UNORM 2 channel DBufferC = float4(GBufferData.Roughness, 0, 0, MultiOpacity.z); } float3 ExtractSubsurfaceColor(FGBufferData BufferData) { return Square(BufferData.CustomData); } uint ExtractSubsurfaceProfileInt(FGBufferData BufferData) { // can be optimized return uint(BufferData.CustomData.r * 255.0f + 0.5f); } /** Populates DBufferA, DBufferB, DBufferC as float4 and puts opacity in alpha for frame buffer blending */ void ApplyDBufferData(FDBufferData DBufferData, inout float3 WorldNormal, inout float3 SubsurfaceColor, inout float Roughness, #if DIFFUSE_SPEC_INPUTS inout float3 DiffuseColor, inout float3 SpecularColor #else inout float3 BaseColor, inout float Metallic, inout float Specular #endif ) { WorldNormal = WorldNormal * DBufferData.NormalOpacity + DBufferData.WorldNormal; Roughness = Roughness * DBufferData.RoughnessOpacity + DBufferData.Roughness; SubsurfaceColor *= DBufferData.ColorOpacity; #if DIFFUSE_SPEC_INPUTS DiffuseColor = DiffuseColor * DBufferData.ColorOpacity + DBufferData.Color; SpecularColor = SpecularColor * DBufferData.ColorOpacity + 0.04f; // most non metal materials have a specular of 4% #else BaseColor = BaseColor * DBufferData.ColorOpacity + DBufferData.Color; Metallic = Metallic * DBufferData.ColorOpacity + 0; // decals are always no metallic Specular = Specular * DBufferData.ColorOpacity + 0.5f; // most non metal materials have a specular of 4% which is 0.5 in this scale #endif } Texture2D DBufferATexture; SamplerState DBufferATextureSampler; Texture2D DBufferBTexture; SamplerState DBufferBTextureSampler; Texture2D DBufferCTexture; SamplerState DBufferCTextureSampler; Texture2D ScreenSpaceAOTexture; SamplerState ScreenSpaceAOTextureSampler; Texture2D CustomDepthTexture; SamplerState CustomDepthTextureSampler; #if FEATURE_LEVEL >= FEATURE_LEVEL_SM4 // In all but SM5 we need to explicitly declare how many samples are in a multisampled texture. #if FEATURE_LEVEL >= FEATURE_LEVEL_SM5 #define FMultisampledGBuffer Texture2DMS #else #define FMultisampledGBuffer Texture2DMS #endif Texture2D DBufferATextureNonMS; Texture2D DBufferBTextureNonMS; Texture2D DBufferCTextureNonMS; Texture2D ScreenSpaceAOTextureNonMS; Texture2D CustomDepthTextureNonMS; // @param PixelPos - integer pixel pos (from left top) FGBufferData GetGBufferDataUint(uint2 PixelPos, bool bGetNormalizedNormal = true) { float4 GBufferA = GBuffers.GBufferATextureNonMS.Load(int3(PixelPos, 0)); float4 GBufferB = GBuffers.GBufferBTextureNonMS.Load(int3(PixelPos, 0)); float4 GBufferC = GBuffers.GBufferCTextureNonMS.Load(int3(PixelPos, 0)); float4 GBufferD = GBuffers.GBufferDTextureNonMS.Load(int3(PixelPos, 0)); float CustomNativeDepth = CustomDepthTextureNonMS.Load(int3(PixelPos, 0)).r; #if ALLOW_STATIC_LIGHTING float4 GBufferE = GBuffers.GBufferETextureNonMS.Load(int3(PixelPos, 0)); #else float4 GBufferE = 1; #endif float SceneDepth = CalcSceneDepth(PixelPos); return DecodeGBufferData(GBufferA, GBufferB, GBufferC, GBufferD, GBufferE, CustomNativeDepth, SceneDepth, bGetNormalizedNormal); } FDBufferData GetDBufferData(uint2 PixelPos) { float4 DBufferA = DBufferATextureNonMS.Load(int3(PixelPos, 0)); float4 DBufferB = DBufferBTextureNonMS.Load(int3(PixelPos, 0)); float2 DBufferC = DBufferCTextureNonMS.Load(int3(PixelPos, 0)).rg; return DecodeDBufferData(DBufferA, DBufferB, DBufferC); } // @param PixelPos - integer pixel pos (from left top) FScreenSpaceData GetScreenSpaceDataUint(uint2 PixelPos, bool bGetNormalizedNormal = true) { FScreenSpaceData Out; Out.GBuffer = GetGBufferDataUint(PixelPos, bGetNormalizedNormal); // todo: optimize // this is what we want but because WhiteDummy (in case AO is disabled) doesn't support this lookup we do the code below // Out.AmbientOcclusion = ScreenSpaceAOTextureNonMS.Load(int3(PixelPos, 0)).r; { uint width; uint height; uint levels; ScreenSpaceAOTextureNonMS.GetDimensions(0, width, height, levels); float4 ScreenSpaceAO = Texture2DSampleLevel(ScreenSpaceAOTexture, ScreenSpaceAOTextureSampler, (PixelPos + 0.5f) / float2(width, height), 0); Out.AmbientOcclusion = ScreenSpaceAO.r; } return Out; } #endif // @param UV - UV space in the GBuffer textures (BufferSize resolution) FGBufferData GetGBufferData(float2 UV, bool bGetNormalizedNormal = true) { float4 GBufferA = Texture2DSampleLevel(GBuffers.GBufferATexture, GBuffers.GBufferATextureSampler, UV, 0); float4 GBufferB = Texture2DSampleLevel(GBuffers.GBufferBTexture, GBuffers.GBufferBTextureSampler, UV, 0); float4 GBufferC = Texture2DSampleLevel(GBuffers.GBufferCTexture, GBuffers.GBufferCTextureSampler, UV, 0); float4 GBufferD = Texture2DSampleLevel(GBuffers.GBufferDTexture, GBuffers.GBufferDTextureSampler, UV, 0); float CustomNativeDepth = Texture2DSampleLevel(CustomDepthTexture, CustomDepthTextureSampler, UV, 0).r; #if ALLOW_STATIC_LIGHTING float4 GBufferE = Texture2DSampleLevel(GBuffers.GBufferETexture, GBuffers.GBufferETextureSampler, UV, 0); #else float4 GBufferE = 1; #endif float SceneDepth = CalcSceneDepth(UV); return DecodeGBufferData(GBufferA, GBufferB, GBufferC, GBufferD, GBufferE, CustomNativeDepth, SceneDepth, bGetNormalizedNormal); } // Minimal path for just the lighting model, used to branch around unlit pixels (skybox) uint GetShadingModelId(float2 UV) { float4 GBufferB = Texture2DSampleLevel(GBuffers.GBufferBTexture, GBuffers.GBufferBTextureSampler, UV, 0); return DecodeShadingModelId(GBufferB.a); } // @param UV - UV space in the DBuffer textures (BufferSize resolution) FDBufferData GetDBufferData(float2 UV) { float4 DBufferA = Texture2DSampleLevel(DBufferATexture, DBufferATextureSampler, UV, 0); float4 DBufferB = Texture2DSampleLevel(DBufferBTexture, DBufferBTextureSampler, UV, 0); float2 DBufferC = Texture2DSampleLevel(DBufferCTexture, DBufferCTextureSampler, UV, 0).rg; return DecodeDBufferData(DBufferA, DBufferB, DBufferC); } // @param UV - UV space in the GBuffer textures (BufferSize resolution) FScreenSpaceData GetScreenSpaceData(float2 UV, bool bGetNormalizedNormal = true) { FScreenSpaceData Out; Out.GBuffer = GetGBufferData(UV, bGetNormalizedNormal); float4 ScreenSpaceAO = Texture2DSampleLevel(ScreenSpaceAOTexture, ScreenSpaceAOTextureSampler, UV, 0); Out.AmbientOcclusion = ScreenSpaceAO.r; return Out; } #if FEATURE_LEVEL >= FEATURE_LEVEL_SM5 FGBufferData GetGBufferDataMS(int2 IntUV, uint SampleIndex, bool bGetNormalizedNormal = true) { float4 GBufferA = GBuffers.GBufferATextureMS.Load(IntUV, SampleIndex); float4 GBufferB = GBuffers.GBufferBTextureMS.Load(IntUV, SampleIndex); float4 GBufferC = GBuffers.GBufferCTextureMS.Load(IntUV, SampleIndex); float4 GBufferD = GBuffers.GBufferDTextureMS.Load(IntUV, SampleIndex); float CustomNativeDepth = CustomDepthTextureNonMS.Load(int3(IntUV, 0)).r; #if ALLOW_STATIC_LIGHTING float4 GBufferE = GBuffers.GBufferETextureMS.Load(IntUV, SampleIndex); #else float4 GBufferE = 1; #endif float DeviceZ = SceneDepthSurface.Load(IntUV, SampleIndex); float SceneDepth = ConvertFromDeviceZ(DeviceZ); return DecodeGBufferData(GBufferA, GBufferB, GBufferC, GBufferD, GBufferE, CustomNativeDepth, SceneDepth, bGetNormalizedNormal); } FGBufferData GetGBufferDataMS(float2 UV, uint SampleIndex, bool bGetNormalizedNormal = true) { float2 SurfaceDimensions; float NumSurfaceSamples; // assuming all GBuffers share the same size GBuffers.GBufferCTextureMS.GetDimensions(SurfaceDimensions.x, SurfaceDimensions.y, NumSurfaceSamples); int2 IntUV = (int2)trunc(UV * SurfaceDimensions); return GetGBufferDataMS(IntUV, SampleIndex, bGetNormalizedNormal); } #endif #endif // __DEFERRED_SHADING_COMMON__