效果如下:(由于对上传图片大小有限制,所以GIF截图质量下降严重)


材质 “Glow 效果” 的实现【UE4】_UE4


算法较简单,首先来看 Base color 部分:

材质 “Glow 效果” 的实现【UE4】_#endif_02

就是将对事先准备好的三张纹理进行线性插值,其中 Mask 为遮罩纹理

接着再来看 Emissive Color 部分:

材质 “Glow 效果” 的实现【UE4】_UE4_03

算法也很简单,即随着时间的变化,经过变化的常量颜色和遮罩纹理进行线性插值,然后结果传入 Emissive Color(基于物理的渲染中,Emissive Color 决定了这个材质有多亮)

总览图

材质 “Glow 效果” 的实现【UE4】_#endif_04

附:基于物理的材质渲染图 (Specular 通常用于 non-metalic 表面)

材质 “Glow 效果” 的实现【UE4】_UE4_05

最后,附上 UE4 自动生成 HLSL 代码,有兴趣的朋友可以研究一下:

/**
* MaterialTemplate.usf: Filled in by FHLSLMaterialTranslator::GetMaterialShaderCode for each material being compiled.
*/
#include "Random.usf"
#include "UniformBuffers/Material.usf"
// for MaterialExpressionDepthOfFieldFunction
#include "DepthOfFieldCommon.usf"
#include "CircleDOFCommon.usf"
#include "GlobalDistanceFieldShared.usf"
#if USES_SPEEDTREE
#include "SpeedTreeCommon.usf"
#endif
#define NUM_MATERIAL_TEXCOORDS_VERTEX 1
#define NUM_MATERIAL_TEXCOORDS 1
#ifdef MIN_MATERIAL_TEXCOORDS
#include "MinMaterialTexCoords.usf"
#endif

#if MATERIAL_ATMOSPHERIC_FOG
#include "AtmosphereCommon.usf"
#endif
#include "PaniniProjection.usf"
#ifndef USE_DITHERED_LOD_TRANSITION
#if USE_INSTANCING
#ifndef USE_DITHERED_LOD_TRANSITION_FOR_INSTANCED
#error "USE_DITHERED_LOD_TRANSITION_FOR_INSTANCED should have been defined"
#endif
#define USE_DITHERED_LOD_TRANSITION USE_DITHERED_LOD_TRANSITION_FOR_INSTANCED
#else
#ifndef USE_DITHERED_LOD_TRANSITION_FROM_MATERIAL
#error "USE_DITHERED_LOD_TRANSITION_FROM_MATERIAL should have been defined"
#endif
#define USE_DITHERED_LOD_TRANSITION USE_DITHERED_LOD_TRANSITION_FROM_MATERIAL
#endif
#endif
#ifndef USE_STENCIL_LOD_DITHER
#define USE_STENCIL_LOD_DITHER USE_STENCIL_LOD_DITHER_DEFAULT
#endif
//Platforms that don't run the editor shouldn't need editor features in the shaders.
#define PLATFORM_SUPPORTS_EDITOR_SHADERS (!PS4_PROFILE && !XBOXONE_PROFILE && !ESDEFERRED_PROFILE && !WOLF_PROFILE)
//Tie Editor features to platform support and the COMPILE_SHADERS_FOR_DEVELOPMENT which is set via CVAR.
#define USE_EDITOR_SHADERS (PLATFORM_SUPPORTS_EDITOR_SHADERS && USE_DEVELOPMENT_SHADERS)
//Materials also have to opt in to these features.
#define USE_EDITOR_COMPOSITING (USE_EDITOR_SHADERS && EDITOR_PRIMITIVE_MATERIAL)
#define MATERIALBLENDING_ANY_TRANSLUCENT (MATERIALBLENDING_TRANSLUCENT || MATERIALBLENDING_ADDITIVE || MATERIALBLENDING_MODULATE)
/**
* Parameters used by vertex and pixel shaders to access particle properties.
*/
struct FMaterialParticleParameters
{
half RelativeTime;
half MotionBlurFade;
half Random;
half4 Velocity;
half4 Color;
float4 TranslatedWorldPositionAndSize;
half4 MacroUV;
half4 DynamicParameter;
/** mesh particle orientation */
float4x4 LocalToWorld;
#if USE_PARTICLE_SUBUVS
/** SubUV texture coordinates*/
MaterialFloat2 SubUVCoords[2];
/** SubUV interpolation value*/
MaterialFloat SubUVLerp;
#endif
float2 Size;
};
float4 GetDynamicParameter(FMaterialParticleParameters Parameters, float4 Default)
{
#if PARTICLE_FACTORY
return Parameters.DynamicParameter;
#else
return Default;
#endif
}
/**
* Parameters calculated from the pixel material inputs.
*/
struct FPixelMaterialInputs
{
MaterialFloat3 EmissiveColor;
MaterialFloat Opacity;
MaterialFloat OpacityMask;
MaterialFloat3 BaseColor;
MaterialFloat Metallic;
MaterialFloat Specular;
MaterialFloat Roughness;
MaterialFloat3 Normal;
MaterialFloat AmbientOcclusion;
MaterialFloat2 Refraction;
MaterialFloat PixelDepthOffset;
};
/**
* Parameters needed by pixel shader material inputs, related to Geometry.
* These are independent of vertex factory.
*/
struct FMaterialPixelParameters
{
#if NUM_MATERIAL_TEXCOORDS
float2 TexCoords[NUM_MATERIAL_TEXCOORDS];
#endif
half4 VertexColor;
half3 TangentNormal;
half3 WorldNormal;
half3 ReflectionVector;
half3 CameraVector;
half3 LightVector;
/**
* Like SV_Position (.xy is pixel position at pixel center, z:DeviceZ, .w:SceneDepth)
* using shader generated value SV_POSITION
*/
float4 SvPosition;

float4 ScreenPosition;
half UnMirrored;
half TwoSidedSign;
/**
* Orthonormal rotation-only transform from tangent space to world space
* The transpose(TangentToWorld) is WorldToTangent, and TangentToWorld[2] is WorldVertexNormal
*/
half3x3 TangentToWorld;
/**
* Interpolated worldspace position of this pixel
* todo: Make this TranslatedWorldPosition and also rename the VS/DS/HS WorldPosition to be TranslatedWorldPosition
*/
float3 AbsoluteWorldPosition;
/**
* Interpolated worldspace position of this pixel, centered around the camera
*/
float3 WorldPosition_CamRelative;
/**
* Interpolated worldspace position of this pixel, not including any world position offset or displacement.
* Only valid if shader is compiled with NEEDS_WORLD_POSITION_EXCLUDING_SHADER_OFFSETS, otherwise just contains 0
*/
float3 WorldPosition_NoOffsets;
/**
* Interpolated worldspace position of this pixel, not including any world position offset or displacement.
* Only valid if shader is compiled with NEEDS_WORLD_POSITION_EXCLUDING_SHADER_OFFSETS, otherwise just contains 0
*/
float3 WorldPosition_NoOffsets_CamRelative;
half3 LightingPositionOffset;
float AOMaterialMask;
#if LIGHTMAP_UV_ACCESS
float2 LightmapUVs;
#endif
#if USE_INSTANCING
half4 PerInstanceParams;
#endif
FMaterialParticleParameters Particle;
#if (ES2_PROFILE || ES3_1_PROFILE)
float4 LayerWeights;
#endif
#if TEX_COORD_SCALE_ANALYSIS
FTexCoordScalesParams TexCoordScalesParams;
#endif
#if POST_PROCESS_MATERIAL && (FEATURE_LEVEL <= FEATURE_LEVEL_ES3_1)
/** Used in mobile custom pp material to preserve original SceneColor Alpha */
half BackupSceneColorAlpha;
#endif
};
// @todo compat hack
FMaterialPixelParameters MakeInitializedMaterialPixelParameters()
{
FMaterialPixelParameters MPP;
MPP = (FMaterialPixelParameters)0;
MPP.TangentToWorld = float3x3(1,0,0,0,1,0,0,0,1);
return MPP;
}
/**
* Parameters needed by domain shader material inputs.
* These are independent of vertex factory.
*/
struct FMaterialTessellationParameters
{
// Note: Customized UVs are only evaluated in the vertex shader, which is not really what you want with tessellation, but keeps the code simpler
// (tessellation texcoords are the same as pixels shader texcoords)
#if NUM_MATERIAL_TEXCOORDS
float2 TexCoords[NUM_MATERIAL_TEXCOORDS];
#endif
float4 VertexColor;
// TODO: Non translated world position
float3 WorldPosition;
float3 TangentToWorldPreScale;
// TangentToWorld[2] is WorldVertexNormal, [0] and [1] are binormal and tangent
float3x3 TangentToWorld;
};
/**
* Parameters needed by vertex shader material inputs.
* These are independent of vertex factory.
*/
struct FMaterialVertexParameters
{
// Position in the translated world (VertexFactoryGetWorldPosition).
// Previous position in the translated world (VertexFactoryGetPreviousWorldPosition) if
// computing material's output for previous frame (See {BasePassVertex,Velocity}Shader.usf).
float3 WorldPosition;
// TangentToWorld[2] is WorldVertexNormal
half3x3 TangentToWorld;
#if USE_INSTANCING
float4x4 InstanceLocalToWorld;
float3 InstanceLocalPosition;
float4 PerInstanceParams;
#elif PARTICLE_MESH_FACTORY
float4x4 InstanceLocalToWorld;
#endif
float3 PreSkinnedPosition;
half4 VertexColor;
#if NUM_MATERIAL_TEXCOORDS_VERTEX
float2 TexCoords[NUM_MATERIAL_TEXCOORDS_VERTEX];
#if (ES2_PROFILE || ES3_1_PROFILE)
float2 TexCoordOffset; // Offset for UV localization for large UV values
#endif
#endif
FMaterialParticleParameters Particle;
};
float3 GetTranslatedWorldPosition(FMaterialVertexParameters Parameters)
{
return Parameters.WorldPosition;
}
float3 GetPrevTranslatedWorldPosition(FMaterialVertexParameters Parameters)
{
// Previous world position and current world position are sharing the
// same attribute in Parameters, because in BasePassVertexShader.usf
// and in VelocityShader.usf, we are regenerating a Parameters from
// VertexFactoryGetPreviousWorldPosition() instead of
// VertexFactoryGetWorldPosition().
return GetTranslatedWorldPosition(Parameters);
}
float3 GetWorldPosition(FMaterialVertexParameters Parameters)
{
return GetTranslatedWorldPosition(Parameters) - ResolvedView.PreViewTranslation;
}
float3 GetPrevWorldPosition(FMaterialVertexParameters Parameters)
{
return GetPrevTranslatedWorldPosition(Parameters) - ResolvedView.PrevPreViewTranslation;
}
//TODO(bug UE-17131): We should compute world displacement for the previous frame
float3 GetWorldPosition(FMaterialTessellationParameters Parameters)
{
return Parameters.WorldPosition;
}
float3 GetTranslatedWorldPosition(FMaterialTessellationParameters Parameters)
{
return Parameters.WorldPosition + ResolvedView.PreViewTranslation;
}
float3 GetWorldPosition(FMaterialPixelParameters Parameters)
{
return Parameters.AbsoluteWorldPosition;
}
float3 GetWorldPosition_NoMaterialOffsets(FMaterialPixelParameters Parameters)
{
return Parameters.WorldPosition_NoOffsets;
}
float3 GetTranslatedWorldPosition(FMaterialPixelParameters Parameters)
{
return Parameters.WorldPosition_CamRelative;
}
float3 GetTranslatedWorldPosition_NoMaterialOffsets(FMaterialPixelParameters Parameters)
{
return Parameters.WorldPosition_NoOffsets_CamRelative;
}
// using this function allows to write the same code for VS and PS
float4 GetScreenPosition(FMaterialVertexParameters Parameters)
{
return mul(float4(Parameters.WorldPosition, 1.0f), ResolvedView.TranslatedWorldToClip);
}
// using this function allows to write the same code for VS and PS
float4 GetScreenPosition(FMaterialPixelParameters Parameters)
{
return Parameters.ScreenPosition;
}
#if DEFERRED_DECAL && NUM_MATERIAL_TEXCOORDS
/*
* Material node DecalMipmapLevel's code designed to avoid the 2x2 pixels artefacts on the edges around where the decal
*
* The problem around edges of the meshes, is that the hardware computes the mipmap level according to ddx(uv) and ddy(uv),
* but since the pixel shader are invocated by group of 2x2 pixels, then on edges some pixel might be getting the
* mesh of the same group of pixel, then one of the delta might be very big, leading to choosing a low mipmap level for this
* group of 4 pixels, causing the artefacts.
*/
float2 ComputeDecalUVFromSvPosition(float4 SvPosition)
{
half DeviceZ = LookupDeviceZ(SvPositionToBufferUV(SvPosition));
SvPosition.z = DeviceZ;
float4 DecalVector = mul(float4(SvPosition.xyz,1), SvPositionToDecal);
DecalVector.xyz /= DecalVector.w;
DecalVector = DecalVector * 0.5f + 0.5f;
DecalVector.xyz = DecalVector.zyx;
return DecalVector.xy;
}
float2 ComputeDecalDDX(FMaterialPixelParameters Parameters)
{
/*
* Assuming where in a pixel shader invocation, then we compute manualy compute two d(uv)/d(x)
* with the pixels's left and right neighbours.
*/
float4 ScreenDeltaX = float4(1, 0, 0, 0);
float2 UvDiffX0 = Parameters.TexCoords[0] - ComputeDecalUVFromSvPosition(Parameters.SvPosition - ScreenDeltaX);
float2 UvDiffX1 = ComputeDecalUVFromSvPosition(Parameters.SvPosition + ScreenDeltaX) - Parameters.TexCoords[0];
/*
* So we have two diff on the X axis, we want the one that has the smallest length
*/
return dot(UvDiffX0, UvDiffX0) < dot(UvDiffX1, UvDiffX1) ? UvDiffX0 : UvDiffX1;
}
float2 ComputeDecalDDY(FMaterialPixelParameters Parameters)
{
// do same for the Y axis
float4 ScreenDeltaY = float4(0, 1, 0, 0);
float2 UvDiffY0 = Parameters.TexCoords[0] - ComputeDecalUVFromSvPosition(Parameters.SvPosition - ScreenDeltaY);
float2 UvDiffY1 = ComputeDecalUVFromSvPosition(Parameters.SvPosition + ScreenDeltaY) - Parameters.TexCoords[0];
return dot(UvDiffY0, UvDiffY0) < dot(UvDiffY1, UvDiffY1) ? UvDiffY0 : UvDiffY1;
}
float ComputeDecalMipmapLevel(FMaterialPixelParameters Parameters, float2 TextureSize)
{
float2 UvPixelDiffX = ComputeDecalDDX(Parameters) * TextureSize;
float2 UvPixelDiffY = ComputeDecalDDY(Parameters) * TextureSize;
// Computes the mipmap level
float MaxDiff = max(dot(UvPixelDiffX, UvPixelDiffX), dot(UvPixelDiffY, UvPixelDiffY));
return 0.5 * log2(MaxDiff);
}
#else // DEFERRED_DECAL && NUM_MATERIAL_TEXCOORDS
float2 ComputeDecalDDX(FMaterialPixelParameters Parameters)
{
return 0.0f;
}

float2 ComputeDecalDDY(FMaterialPixelParameters Parameters)
{
return 0.0f;
}
float ComputeDecalMipmapLevel(FMaterialPixelParameters Parameters, float2 TextureSize)
{
return 0.0f;
}
#endif // DEFERRED_DECAL && NUM_MATERIAL_TEXCOORDS
#if DEFERRED_DECAL
/*
* Deferred decal don't have a Primitive uniform buffer, because we don't know on which primitive the decal
* So instead of setting up a primitive buffer that may cost to much CPU effort to be almost never used,
* we directly fetch this value from the DeferredDecal.usf specific uniform variable DecalToWorld.
*/
float3 GetActorWorldPosition()
{
return DecalToWorld[3].xyz;
}
#else
float3 GetActorWorldPosition()
{
return Primitive.ActorWorldPosition;
}
#endif // DEFERRED_DECAL
#if DECAL_PRIMITIVE
float DecalLifetimeOpacity()
{
return DecalParams.y;
}
#else
float DecalLifetimeOpacity()
{
return 0.0f;
}
#endif // DECAL_PRIMITIVE
/** Transforms a vector from tangent space to world space, prescaling by an amount calculated previously */
MaterialFloat3 TransformTangentVectorToWorld_PreScaled(FMaterialTessellationParameters Parameters, MaterialFloat3 InTangentVector)
{
#if FEATURE_LEVEL >= FEATURE_LEVEL_SM5
// used optionally to scale up the vector prior to conversion
InTangentVector *= abs( Parameters.TangentToWorldPreScale );
// Transform directly to world space
// The vector transform is optimized for this case, only one vector-matrix multiply is needed
return mul(InTangentVector, Parameters.TangentToWorld);
#else
return TransformTangentVectorToWorld(Parameters.TangentToWorld, InTangentVector);
#endif // #if FEATURE_LEVEL_SM5
}
/** Transforms a vector from tangent space to view space */
MaterialFloat3 TransformTangentVectorToView(FMaterialPixelParameters Parameters, MaterialFloat3 InTangentVector)
{
// Transform from tangent to world, and then to view space
return mul(mul(InTangentVector, Parameters.TangentToWorld), (MaterialFloat3x3)ResolvedView.TranslatedWorldToView);
}
/** Transforms a vector from local space to world space (VS version) */
MaterialFloat3 TransformLocalVectorToWorld(FMaterialVertexParameters Parameters,MaterialFloat3 InLocalVector)
{
#if USE_INSTANCING || PARTICLE_MESH_FACTORY
return mul(InLocalVector, (MaterialFloat3x3)Parameters.InstanceLocalToWorld);
#else
return mul(InLocalVector, GetLocalToWorld3x3());
#endif
}
/** Transforms a vector from local space to world space (PS version) */
MaterialFloat3 TransformLocalVectorToWorld(FMaterialPixelParameters Parameters,MaterialFloat3 InLocalVector)
{
return mul(InLocalVector, GetLocalToWorld3x3());
}
#if HAS_PRIMITIVE_UNIFORM_BUFFER
/** Transforms a vector from world space to local space */
MaterialFloat3 TransformWorldVectorToLocal(MaterialFloat3 InWorldVector)
{
return mul(InWorldVector, (MaterialFloat3x3)Primitive.WorldToLocal);
}
/** Transforms a position from local space to absolute world space */
float3 TransformLocalPositionToWorld(FMaterialPixelParameters Parameters,float3 InLocalPosition)
{
return mul(float4(InLocalPosition, 1), Primitive.LocalToWorld).xyz;
}
/** Transforms a position from local space to absolute world space */
float3 TransformLocalPositionToWorld(FMaterialVertexParameters Parameters,float3 InLocalPosition)
{
#if USE_INSTANCING || PARTICLE_MESH_FACTORY
return mul(float4(InLocalPosition, 1), Parameters.InstanceLocalToWorld).xyz;
#else
return mul(float4(InLocalPosition, 1), Primitive.LocalToWorld).xyz;
#endif
}
#endif
#if HAS_PRIMITIVE_UNIFORM_BUFFER
/** Return the object's position in world space */
float3 GetObjectWorldPosition(FMaterialPixelParameters Parameters)
{
return Primitive.ObjectWorldPositionAndRadius.xyz;
}
float3 GetObjectWorldPosition(FMaterialTessellationParameters Parameters)
{
return Primitive.ObjectWorldPositionAndRadius.xyz;
}
float3 GetObjectWorldPosition(FMaterialVertexParameters Parameters)
{
#if USE_INSTANCING || PARTICLE_MESH_FACTORY
return Parameters.InstanceLocalToWorld[3].xyz;
#else
return Primitive.ObjectWorldPositionAndRadius.xyz;
#endif
}
#endif
/** Get the per-instance random value when instancing */
float GetPerInstanceRandom(FMaterialVertexParameters Parameters)
{
#if USE_INSTANCING
return Parameters.PerInstanceParams.x;
#else
return 0.0;
#endif
}
/** Get the per-instance random value when instancing */
float GetPerInstanceRandom(FMaterialPixelParameters Parameters)
{
#if USE_INSTANCING
return Parameters.PerInstanceParams.x;
#else
return 0.0;
#endif
}
/** Get the per-instance fade-out amount when instancing */
float GetPerInstanceFadeAmount(FMaterialPixelParameters Parameters)
{
#if USE_INSTANCING
return float(Parameters.PerInstanceParams.y);
#else
return float(1.0);
#endif
}
/** Get the per-instance fade-out amount when instancing */
float GetPerInstanceFadeAmount(FMaterialVertexParameters Parameters)
{
#if USE_INSTANCING
return float(Parameters.PerInstanceParams.y);
#else
return float(1.0);
#endif
}

MaterialFloat GetDistanceCullFade()
{
return saturate( View.RealTime * PrimitiveFade.FadeTimeScaleBias.x + PrimitiveFade.FadeTimeScaleBias.y );
}
float3 RotateAboutAxis(float4 NormalizedRotationAxisAndAngle, float3 PositionOnAxis, float3 Position)
{
// Project Position onto the rotation axis and find the closest point on the axis to Position
float3 ClosestPointOnAxis = PositionOnAxis + NormalizedRotationAxisAndAngle.xyz * dot(NormalizedRotationAxisAndAngle.xyz, Position - PositionOnAxis);
// Construct orthogonal axes in the plane of the rotation
float3 UAxis = Position - ClosestPointOnAxis;
float3 VAxis = cross(NormalizedRotationAxisAndAngle.xyz, UAxis);
float CosAngle;
float SinAngle;
sincos(NormalizedRotationAxisAndAngle.w, SinAngle, CosAngle);
// Rotate using the orthogonal axes
float3 R = UAxis * CosAngle + VAxis * SinAngle;
// Reconstruct the rotated world space position
float3 RotatedPosition = ClosestPointOnAxis + R;
// Convert from position to a position offset
return RotatedPosition - Position;
}
// Material Expression function
float MaterialExpressionDepthOfFieldFunction(float SceneDepth, int FunctionValueIndex)
{
// tryed switch() but seems that doesn't work
if(FunctionValueIndex == 0) // TDOF_NearAndFarMask
{
return CalcUnfocusedPercentCustomBound(SceneDepth, 1, 1);
}
else if(FunctionValueIndex == 1) // TDOF_Near
{
return CalcUnfocusedPercentCustomBound(SceneDepth, 1, 0);
}
else if(FunctionValueIndex == 2) // TDOF_Far
{
return CalcUnfocusedPercentCustomBound(SceneDepth, 0, 1);
}
else if(FunctionValueIndex == 3) // TDOF_CircleOfConfusionRadius
{
// * 2 to compensate for half res
return DepthToCoc(SceneDepth) * 2.0f;
}
return 0;
}
// TODO convert to LUT
float3 MaterialExpressionBlackBody( float Temp )
{
float u = ( 0.860117757f + 1.54118254e-4f * Temp + 1.28641212e-7f * Temp*Temp ) / ( 1.0f + 8.42420235e-4f * Temp + 7.08145163e-7f * Temp*Temp );
float v = ( 0.317398726f + 4.22806245e-5f * Temp + 4.20481691e-8f * Temp*Temp ) / ( 1.0f - 2.89741816e-5f * Temp + 1.61456053e-7f * Temp*Temp );
float x = 3*u / ( 2*u - 8*v + 4 );
float y = 2*v / ( 2*u - 8*v + 4 );
float z = 1 - x - y;
float Y = 1;
float X = Y/y * x;
float Z = Y/y * z;
float3x3 XYZtoRGB =
{
3.2404542, -1.5371385, -0.4985314,
-0.9692660, 1.8760108, 0.0415560,
0.0556434, -0.2040259, 1.0572252,
};
return mul( XYZtoRGB, float3( X, Y, Z ) ) * pow( 0.0004 * Temp, 4 );
}
float4 MaterialExpressionAtmosphericFog(FMaterialPixelParameters Parameters, float3 AbsoluteWorldPosition)
{
#if MATERIAL_ATMOSPHERIC_FOG
// WorldPosition default value is Parameters.AbsoluteWorldPosition if not overridden by the user
float3 ViewVector = AbsoluteWorldPosition - ResolvedView.WorldCameraOrigin;
float SceneDepth = length(ViewVector);
return GetAtmosphericFog(ResolvedView.WorldCameraOrigin, ViewVector, SceneDepth, float3(0.f, 0.f, 0.f));
#else
return float4(0.f, 0.f, 0.f, 0.f);
#endif
}
float3 MaterialExpressionAtmosphericLightVector(FMaterialPixelParameters Parameters)
{
#if MATERIAL_ATMOSPHERIC_FOG
return View.AtmosphericFogSunDirection;
#else
return float3(0.f, 0.f, 0.f);
#endif
}
float3 MaterialExpressionAtmosphericLightColor(FMaterialPixelParameters Parameters)
{
#if MATERIAL_ATMOSPHERIC_FOG
return View.AtmosphericFogSunColor;
#else
return float3(0.f, 0.f, 0.f);
#endif
}
/**
* Utility function to unmirror one coordinate value to the other side
* UnMirrored == 1 if normal
* UnMirrored == -1 if mirrored
*
* Used by most of parameter functions generated via code in this file
*/
MaterialFloat UnMirror( MaterialFloat Coordinate, FMaterialPixelParameters Parameters )
{
return ((Coordinate)*(Parameters.UnMirrored)*0.5+0.5);
}
/**
* UnMirror only U
*/
MaterialFloat2 UnMirrorU( MaterialFloat2 UV, FMaterialPixelParameters Parameters )
{
return MaterialFloat2(UnMirror(UV.x, Parameters), UV.y);
}
/**
* UnMirror only V
*/
MaterialFloat2 UnMirrorV( MaterialFloat2 UV, FMaterialPixelParameters Parameters )
{
return MaterialFloat2(UV.x, UnMirror(UV.y, Parameters));
}
/**
* UnMirror only UV
*/
MaterialFloat2 UnMirrorUV( MaterialFloat2 UV, FMaterialPixelParameters Parameters )
{
return MaterialFloat2(UnMirror(UV.x, Parameters), UnMirror(UV.y, Parameters));
}
/**
* Transforms screen space positions into UVs with [.5, .5] centered on ObjectPostProjectionPosition,
* And [1, 1] at ObjectPostProjectionPosition + (ObjectRadius, ObjectRadius).
*/
MaterialFloat2 GetParticleMacroUV(FMaterialPixelParameters Parameters)
{
return (Parameters.ScreenPosition.xy / Parameters.ScreenPosition.w - Parameters.Particle.MacroUV.xy) * Parameters.Particle.MacroUV.zw + MaterialFloat2(.5, .5);
}
#ifndef MOBILE_EMULATION
#define MOBILE_EMULATION ((FEATURE_LEVEL == FEATURE_LEVEL_ES2 || FEATURE_LEVEL == FEATURE_LEVEL_ES3_1) && (!(COMPILER_GLSL_ES2||COMPILER_GLSL_ES3_1) && USE_DEVELOPMENT_SHADERS && (COMPILER_METAL && MAC)))
#endif
MaterialFloat4 ProcessMaterialColorTextureLookup(MaterialFloat4 TextureValue)
{
#if (ES2_PROFILE || ES3_1_PROFILE)
#if MOBILE_EMULATION
if( View.MobilePreviewMode > 0.5f)
{
// undo HW srgb->lin
TextureValue.rgb = pow(TextureValue.rgb, 1.0f / 2.2f); // TODO: replace with a more accurate lin -> sRGB conversion.
}
#endif
// sRGB read approximation
TextureValue.rgb *= TextureValue.rgb;
#endif
return TextureValue;
}
MaterialFloat4 ProcessMaterialLinearColorTextureLookup(MaterialFloat4 TextureValue)
{
return TextureValue;
}
MaterialFloat ProcessMaterialGreyscaleTextureLookup(MaterialFloat TextureValue)
{
#if (ES2_PROFILE || ES3_1_PROFILE)
#if MOBILE_EMULATION
if( View.MobilePreviewMode > 0.5f )
{
// undo HW srgb->lin
TextureValue = pow(TextureValue, 1.0f/2.2f); // TODO: replace with a more accurate lin -> sRGB conversion.
}
#endif
// sRGB read approximation
TextureValue *= TextureValue;
#endif
return TextureValue;
}
MaterialFloat ProcessMaterialLinearGreyscaleTextureLookup(MaterialFloat TextureValue)
{
return TextureValue;
}
SamplerState GetMaterialSharedSampler(SamplerState TextureSampler, SamplerState SharedSampler)
{
#if SUPPORTS_INDEPENDENT_SAMPLERS
return SharedSampler;
#else
// Note: to match behavior on platforms that don't support SUPPORTS_INDEPENDENT_SAMPLERS,
return TextureSampler;
#endif
}
MaterialFloat3 ReflectionAboutCustomWorldNormal(FMaterialPixelParameters Parameters, MaterialFloat3 WorldNormal, bool bNormalizeInputNormal)
{
if (bNormalizeInputNormal)
{
WorldNormal = normalize(WorldNormal);
}
return -Parameters.CameraVector + WorldNormal * dot(WorldNormal, Parameters.CameraVector) * 2.0;
}
#ifndef SPHERICAL_OPACITY_FOR_SHADOW_DEPTHS
#define SPHERICAL_OPACITY_FOR_SHADOW_DEPTHS 0
#endif
/**
* Note: Calling this function requires the vertex factory to have been compiled with SPHERICAL_PARTICLE_OPACITY set to 1
*/
float GetSphericalParticleOpacity(FMaterialPixelParameters Parameters, float Density)
{
float Opacity = 0;
#if PARTICLE_FACTORY || HAS_PRIMITIVE_UNIFORM_BUFFER
#if PARTICLE_FACTORY
float3 ParticleTranslatedWorldPosition = Parameters.Particle.TranslatedWorldPositionAndSize.xyz;
float ParticleRadius = max(0.000001f, Parameters.Particle.TranslatedWorldPositionAndSize.w);
#elif HAS_PRIMITIVE_UNIFORM_BUFFER
// Substitute object attributes if the mesh is not a particle
// This is mostly useful for previewing materials using spherical opacity in the material editor
float3 ParticleTranslatedWorldPosition = Primitive.ObjectWorldPositionAndRadius.xyz + ResolvedView.PreViewTranslation.xyz;
float ParticleRadius = max(0.000001f, Primitive.ObjectWorldPositionAndRadius.w);
#endif
// Rescale density to make the final opacity independent of the particle radius
float RescaledDensity = Density / ParticleRadius;
// Distance from point being shaded to particle center
float DistanceToParticle = length(Parameters.WorldPosition_NoOffsets_CamRelative - ParticleTranslatedWorldPosition);
FLATTEN
if (DistanceToParticle < ParticleRadius)
{
// Distance from point being shaded to the point on the sphere along the view direction
float HemisphericalDistance = sqrt(ParticleRadius * ParticleRadius - DistanceToParticle * DistanceToParticle);
#if SPHERICAL_OPACITY_FOR_SHADOW_DEPTHS
// When rendering shadow depths we can't use scene depth or the near plane, just use the distance through the whole sphere
float DistanceThroughSphere = HemisphericalDistance * 2;
#else
// Initialize near and far sphere intersection distances
float NearDistance = Parameters.ScreenPosition.w - HemisphericalDistance;
float FarDistance = Parameters.ScreenPosition.w + HemisphericalDistance;
float SceneDepth = CalcSceneDepth(SvPositionToBufferUV(Parameters.SvPosition));
FarDistance = min(SceneDepth, FarDistance);
// Take into account opaque objects intersecting the sphere
float DistanceThroughSphere = FarDistance - NearDistance;
#endif
// Use the approximation for the extinction line integral from "Spherical Billboards and their Application to Rendering Explosions"
Opacity = saturate(1 - exp2(-RescaledDensity * (1 - DistanceToParticle / ParticleRadius) * DistanceThroughSphere));
#if !SPHERICAL_OPACITY_FOR_SHADOW_DEPTHS
// Fade out as the particle approaches the near plane
Opacity = lerp(0, Opacity, saturate((Parameters.ScreenPosition.w - ParticleRadius - View.NearPlane) / ParticleRadius));
#endif
}
#endif
return Opacity;
}
#if USES_SPEEDTREE
/** Vertex offset for SpeedTree wind and LOD */
float3 GetSpeedTreeVertexOffsetInner(FMaterialVertexParameters Parameters, int GeometryType, int WindType, int LODType, float BillboardThreshold, FSpeedTreeData STData)
{
#if (NUM_MATERIAL_TEXCOORDS_VERTEX < 6) || PARTICLE_MESH_FACTORY
return float4(0,0,0);
#endif
#if USE_INSTANCING
float3x3 LocalToWorld = (float3x3)Parameters.InstanceLocalToWorld;
float3 LocalPosition = Parameters.InstanceLocalPosition;
// skip if this instance is hidden
if (Parameters.PerInstanceParams.z < 1.f)
{
return float3(0,0,0);
}
#else
float3x3 LocalToWorld = (float3x3)Primitive.LocalToWorld;
float3 LocalPosition = mul(float4(GetWorldPosition(Parameters), 1), Primitive.WorldToLocal).xyz;
#endif
float3 TreePos = GetObjectWorldPosition(Parameters);
// compute LOD by finding screen space size
float LodInterp = 1.0;
#if !USE_INSTANCING || !USE_DITHERED_LOD_TRANSITION
if (LODType == SPEEDTREE_LOD_TYPE_SMOOTH)
{
const float ScreenMultiple = 0.5 * max(View.ViewSizeAndInvSize.x * ResolvedView.ViewToClip[0][0],
View.ViewSizeAndInvSize.y * ResolvedView.ViewToClip[1][1]);
const float ScreenRadius = ScreenMultiple * Primitive.ObjectWorldPositionAndRadius.w /
max(1.0, dot(TreePos - ResolvedView.WorldCameraOrigin, ResolvedView.ViewForward.xyz));
const float ScreenArea = PI * ScreenRadius * ScreenRadius * View.ViewSizeAndInvSize.z * View.ViewSizeAndInvSize.w;
LodInterp = saturate((ScreenArea - SpeedTreeLODInfo.x) / SpeedTreeLODInfo.z);
}
#endif
TreePos *= 0.001; // The only other use of the tree position is as an offset into trig functions, but big numbers don't play nice there
// SpeedTrees should only be uniformly scaled, but if necessary, it takes a few more instructions
float TreeScale = length(mul(float3(0,0,1), LocalToWorld));
//float3(length((float3)LocalToWorld[0]),
// length((float3)LocalToWorld[1]),
// length((float3)LocalToWorld[2]));
// @todo There is probably a more optimal way to get the rotated (but not translated or scaled) vertex position needed for correct wind
float3 OriginalPosition = LocalPosition;
OriginalPosition = mul(OriginalPosition, LocalToWorld) / TreeScale;
float3 FinalPosition = OriginalPosition;

if (GeometryType == SPEEDTREE_GEOMETRY_TYPE_BILLBOARD)
{
if (BillboardThreshold < 1.0)
{
// billboard meshes can have triangles drop out if they aren't facing the camera
// this rotates the view direction around so we ignore the local Z component
float3 LocalView2D = normalize(float3(ResolvedView.ViewForward.xy, 0));
float3 LocalNormal2D = normalize(float3(Parameters.TangentToWorld[2].xy, 0));
if (dot(LocalView2D, LocalNormal2D) > (-1.0 + BillboardThreshold * 0.25))
{
FinalPosition = float3(0,0,0);
}
}
}
else
{
// rotated normal needed in a few places
float3 Normal = Parameters.TangentToWorld[2];
// branches and fronds
if (GeometryType == SPEEDTREE_GEOMETRY_TYPE_BRANCH || GeometryType == SPEEDTREE_GEOMETRY_TYPE_FROND)
{
// smooth LOD
#if !USE_INSTANCING
if (LODType == SPEEDTREE_LOD_TYPE_SMOOTH)
{
float3 LODPos = float3(Parameters.TexCoords[3].x, Parameters.TexCoords[3].y, Parameters.TexCoords[4].x);
LODPos = mul(LODPos, LocalToWorld) / TreeScale;
FinalPosition = lerp(LODPos, FinalPosition, LodInterp);
}
#endif
// frond wind, if needed
if (GeometryType == SPEEDTREE_GEOMETRY_TYPE_FROND && WindType == SPEEDTREE_WIND_TYPE_PALM)
{
float2 TexCoords = Parameters.TexCoords[0];
float4 WindExtra = float4(Parameters.TexCoords[5].x, Parameters.TexCoords[5].y, Parameters.TexCoords[6].x, 0.0);
FinalPosition = RippleFrond(STData, FinalPosition, Normal, TexCoords.x, TexCoords.y, WindExtra.x, WindExtra.y, WindExtra.z);
}
}
// leaves and facing leaves
if (GeometryType == SPEEDTREE_GEOMETRY_TYPE_FACINGLEAF ||
(GeometryType == SPEEDTREE_GEOMETRY_TYPE_LEAF &&
(LODType == SPEEDTREE_LOD_TYPE_SMOOTH || (WindType > SPEEDTREE_WIND_TYPE_FASTEST && WindType != SPEEDTREE_WIND_TYPE_PALM))))
{
// remove anchor pos from vertex position
float3 Anchor = float3(Parameters.TexCoords[4].y, Parameters.TexCoords[5].x, Parameters.TexCoords[5].y);
Anchor = (mul(Anchor, LocalToWorld)) / TreeScale;
FinalPosition -= Anchor;
// smooth LOD
#if !USE_INSTANCING
if (LODType == SPEEDTREE_LOD_TYPE_SMOOTH)
{
if (GeometryType == SPEEDTREE_GEOMETRY_TYPE_LEAF)
{
float3 LODPos = float3(Parameters.TexCoords[3].x, Parameters.TexCoords[3].y, Parameters.TexCoords[4].x);
LODPos = mul(LODPos, LocalToWorld) / TreeScale - Anchor;
FinalPosition = lerp(LODPos, FinalPosition, LodInterp);
}
else
{
float LODScalar = Parameters.TexCoords[3].x;
FinalPosition *= lerp(LODScalar, 1.0, LodInterp);
}
}
#endif
// face camera-facing leaves to the camera, if needed
if (GeometryType == SPEEDTREE_GEOMETRY_TYPE_FACINGLEAF)
{
// have to rotate the view into local space
FinalPosition = FinalPosition.x * ResolvedView.ViewRight +
FinalPosition.y * ResolvedView.ViewUp +
FinalPosition.z * ResolvedView.ViewForward;
}
// leaf wind
if (WindType > SPEEDTREE_WIND_TYPE_FASTEST && WindType != SPEEDTREE_WIND_TYPE_PALM)
{
float4 WindExtra = float4(Parameters.TexCoords[6].x, Parameters.TexCoords[6].y, Parameters.TexCoords[7].x, Parameters.TexCoords[7].y);
float LeafWindTrigOffset = Anchor.x + Anchor.y;
FinalPosition = LeafWind(STData, WindExtra.w > 0.0, FinalPosition, Normal, WindExtra.x, float3(0,0,0), WindExtra.y, WindExtra.z, LeafWindTrigOffset, WindType);
}

// move leaf back to anchor
FinalPosition += Anchor;
}
if (WindType > SPEEDTREE_WIND_TYPE_FAST)
{
// branch wind (applies to all geometry)
float2 VertBranchWind = Parameters.TexCoords[2];
FinalPosition = BranchWind(STData, FinalPosition, TreePos, float4(VertBranchWind, 0, 0), WindType);
}
}
// global wind can apply to the whole tree, even billboards
if (WindType != SPEEDTREE_WIND_TYPE_NONE)
{
FinalPosition = GlobalWind(STData, FinalPosition, TreePos, true);
}
// convert into a world space offset
return (FinalPosition - OriginalPosition) * TreeScale;
}
/** Vertex offset for SpeedTree wind and LOD */
float3 GetSpeedTreeVertexOffset(FMaterialVertexParameters Parameters, int GeometryType, int WindType, int LODType, float BillboardThreshold, bool bUsePreviousFrame)
{
#if VF_SUPPORTS_SPEEDTREE_WIND
if (bUsePreviousFrame)
{
return GetSpeedTreeVertexOffsetInner(Parameters, GeometryType, WindType, LODType, BillboardThreshold, GetPreviousSpeedTreeData());
}
return GetSpeedTreeVertexOffsetInner(Parameters, GeometryType, WindType, LODType, BillboardThreshold, GetCurrentSpeedTreeData());
#else
return 0;
#endif
}
#endif
MaterialFloat2 GetLightmapUVs(FMaterialPixelParameters Parameters)
{
#if LIGHTMAP_UV_ACCESS
return Parameters.LightmapUVs;
#else
return MaterialFloat2(0,0);
#endif
}
#if USES_EYE_ADAPTATION
#include "EyeAdaptationCommon.usf"
#endif
#if POST_PROCESS_MATERIAL
// only Postprocess material bind PostprocessInput0, PostprocessInput0Size, PostprocessInput0MinMax ...
#include "PostProcessCommon.usf" // PostprocessInput0
#endif
#if NEEDS_SCENE_TEXTURES
#include "DeferredShadingCommon.usf" // GetGBufferData()
float4 GetPostProcessInputSize(uint Index)
{
#if POST_PROCESS_MATERIAL
if (Index == 0) return PostprocessInput0Size;
else if (Index == 1) return PostprocessInput1Size;
else if (Index == 2) return PostprocessInput2Size;
else if (Index == 3) return PostprocessInput3Size;
else if (Index == 4) return PostprocessInput4Size;
else if (Index == 5) return PostprocessInput5Size;
else if (Index == 6) return PostprocessInput6Size;
#endif
return 1.0f;
}
float4 GetPostProcessInputMinMax(uint Index)
{
#if POST_PROCESS_MATERIAL
if (Index == 0) return PostprocessInput0MinMax;
else if (Index == 1) return PostprocessInput1MinMax;
else if (Index == 2) return PostprocessInput2MinMax;
else if (Index == 3) return PostprocessInput3MinMax;
else if (Index == 4) return PostprocessInput4MinMax;
else if (Index == 5) return PostprocessInput5MinMax;
else if (Index == 6) return PostprocessInput6MinMax;
#endif
return 1.0f;
}
MaterialFloat4 MobileSceneTextureLookup(inout FMaterialPixelParameters Parameters, int SceneTextureId, float2 UV)
{
#if (FEATURE_LEVEL <= FEATURE_LEVEL_ES3_1)

// For fetching SceneDepth and CustomDepth after tonemapper we need to switch OpenGL coordinates
// SceneColor will be transformed to OpenGL coordinates during tonemapping
#define NEEDTOSWITCHVERTICLEAXIS (COMPILER_GLSL_ES2 || COMPILER_GLSL_ES3_1) && (POST_PROCESS_MATERIAL && !POST_PROCESS_MATERIAL_BEFORE_TONEMAP)

//PPI_SceneDepth
if (SceneTextureId == 1)
{
#if NEEDTOSWITCHVERTICLEAXIS
UV.y = 1.0 - UV.y;
#endif

MaterialFloat Depth = ConvertFromDeviceZ(Texture2DSample(SceneDepthTexture, SceneDepthTextureSampler, UV).r);
return MaterialFloat4(Depth.rrr, 0.f);
}
//PPI_CustomDepth
else if (SceneTextureId == 13)
{
#if NEEDTOSWITCHVERTICLEAXIS
UV.y = 1.0 - UV.y;
#endif
MaterialFloat Depth = ConvertFromDeviceZ(Texture2DSample(CustomDepthTexture, CustomDepthTextureSampler, UV).r);
return MaterialFloat4(Depth.rrr, 0.f);
}
// PPI_PostprocessInput
else if (SceneTextureId == 14)
{
#if POST_PROCESS_MATERIAL
MaterialFloat4 Input0 = Texture2DSample(PostprocessInput0, PostprocessInput0Sampler, UV);
#if POST_PROCESS_MATERIAL_BEFORE_TONEMAP
Input0 = Decode32BPPHDR(Input0);
#endif
// We need to preserve original SceneColor Alpha as it's used by tonemaper on mobile
Parameters.BackupSceneColorAlpha = Input0.a;
return Input0;
#endif// POST_PROCESS_MATERIAL
}
#endif// FEATURE_LEVEL
return MaterialFloat4(0.0f, 0.0f, 0.0f, 0.0f);
}
float4 SceneTextureLookup(float2 UV, int SceneTextureIndex, bool bFiltered)
{
#if SCENE_TEXTURES_DISABLED
return float4(0.0f, 0.0f, 0.0f, 0.0f);
#endif
FScreenSpaceData ScreenSpaceData = GetScreenSpaceData(UV, false);
#if PS4_PROFILE
// TODO: PS4 doesn't optimize out correctly the switch(), so it thinks it needs all the Samplers even if they get compiled out
// This will get fixed after launch per Sony...
if (SceneTextureIndex == 0) return float4(CalcSceneColor(UV), 0);
else if (SceneTextureIndex == 1) return ScreenSpaceData.GBuffer.Depth;
else if (SceneTextureIndex == 2) return float4(ScreenSpaceData.GBuffer.DiffuseColor, 0);
else if (SceneTextureIndex == 3) return float4(ScreenSpaceData.GBuffer.SpecularColor, 0);
else if (SceneTextureIndex == 4) return float4(ScreenSpaceData.GBuffer.CustomData.rgb, 0);
else if (SceneTextureIndex == 5) return float4(ScreenSpaceData.GBuffer.BaseColor, 0);
else if (SceneTextureIndex == 6) return ScreenSpaceData.GBuffer.Specular;
else if (SceneTextureIndex == 7) return ScreenSpaceData.GBuffer.Metallic;
else if (SceneTextureIndex == 8) return float4(ScreenSpaceData.GBuffer.WorldNormal, 0);
else if (SceneTextureIndex == 9) return 1; // todo
else if (SceneTextureIndex == 10) return ScreenSpaceData.GBuffer.CustomData.a;
else if (SceneTextureIndex == 11) return ScreenSpaceData.GBuffer.Roughness;
else if (SceneTextureIndex == 12) return ScreenSpaceData.GBuffer.GBufferAO;
else if (SceneTextureIndex == 13) return ScreenSpaceData.GBuffer.CustomDepth;
#if POST_PROCESS_MATERIAL
else if (SceneTextureIndex == 14) if (bFiltered) { return Texture2DSample(PostprocessInput0, BilinearTextureSampler0, UV);} else { return Texture2DSample(PostprocessInput0, PostprocessInput0Sampler, UV);}
else if (SceneTextureIndex == 15) if (bFiltered) { return Texture2DSample(PostprocessInput1, BilinearTextureSampler0, UV);} else { return Texture2DSample(PostprocessInput1, PostprocessInput1Sampler, UV);}
else if (SceneTextureIndex == 16) if (bFiltered) { return Texture2DSample(PostprocessInput2, BilinearTextureSampler0, UV);} else { return Texture2DSample(PostprocessInput2, PostprocessInput2Sampler, UV);}
else if (SceneTextureIndex == 17) if (bFiltered) { return Texture2DSample(PostprocessInput3, BilinearTextureSampler0, UV);} else { return Texture2DSample(PostprocessInput3, PostprocessInput3Sampler, UV);}
else if (SceneTextureIndex == 18) if (bFiltered) { return Texture2DSample(PostprocessInput4, BilinearTextureSampler0, UV);} else { return Texture2DSample(PostprocessInput4, PostprocessInput4Sampler, UV);}
else if (SceneTextureIndex == 19) if (bFiltered) { return Texture2DSample(PostprocessInput5, BilinearTextureSampler0, UV);} else { return Texture2DSample(PostprocessInput5, PostprocessInput5Sampler, UV);}
else if (SceneTextureIndex == 20) if (bFiltered) { return Texture2DSample(PostprocessInput6, BilinearTextureSampler0, UV);} else { return Texture2DSample(PostprocessInput6, PostprocessInput6Sampler, UV);}
#endif // POST_PROCESS_MATERIAL
else if (SceneTextureIndex == 21) return 0; // material compiler will return an error
else if (SceneTextureIndex == 22) return float4(GetShadingModelColor(ScreenSpaceData.GBuffer.ShadingModelID), 1);
else if (SceneTextureIndex == 23) return ScreenSpaceData.AmbientOcclusion;
else if (SceneTextureIndex == 24) return ScreenSpaceData.GBuffer.CustomStencil;
else if (SceneTextureIndex == 25) return float4(ScreenSpaceData.GBuffer.StoredBaseColor, 0);
else if (SceneTextureIndex == 26) return float4(ScreenSpaceData.GBuffer.StoredSpecular.rrr, 0);
#else
switch(SceneTextureIndex)
{
// order needs to match to ESceneTextureId
// PPI_SceneColor
case 0: return float4(CalcSceneColor(UV), 0);
// PPI_SceneDepth
case 1: return ScreenSpaceData.GBuffer.Depth;
// PPI_DiffuseColor
case 2: return float4(ScreenSpaceData.GBuffer.DiffuseColor, 0);
// PPI_SpecularColor
case 3: return float4(ScreenSpaceData.GBuffer.SpecularColor, 0);
// PPI_SubsurfaceColor
case 4: return IsSubsurfaceModel(ScreenSpaceData.GBuffer.ShadingModelID) ? float4( ExtractSubsurfaceColor(ScreenSpaceData.GBuffer), ScreenSpaceData.GBuffer.CustomData.a ) : ScreenSpaceData.GBuffer.CustomData;
// PPI_BaseColor
case 5: return float4(ScreenSpaceData.GBuffer.BaseColor, 0);
// PPI_Specular
case 6: return ScreenSpaceData.GBuffer.Specular;
// PPI_Metallic
case 7: return ScreenSpaceData.GBuffer.Metallic;
// PPI_WorldNormal
case 8: return float4(ScreenSpaceData.GBuffer.WorldNormal, 0);
// PPI_SeparateTranslucency
case 9: return float4(1, 1, 1, 1); // todo
// PPI_Opacity
case 10: return ScreenSpaceData.GBuffer.CustomData.a;
// PPI_Roughness
case 11: return ScreenSpaceData.GBuffer.Roughness;
// PPI_MaterialAO
case 12: return ScreenSpaceData.GBuffer.GBufferAO;
// PPI_CustomDepth
case 13: return ScreenSpaceData.GBuffer.CustomDepth;
#if POST_PROCESS_MATERIAL
// PPI_PostprocessInput0
case 14: return Texture2DSample(PostprocessInput0, bFiltered ? BilinearTextureSampler0 : PostprocessInput0Sampler, UV);
// PPI_PostprocessInput1
case 15: return Texture2DSample(PostprocessInput1, bFiltered ? BilinearTextureSampler0 : PostprocessInput1Sampler, UV);
// PPI_PostprocessInput2
case 16: return Texture2DSample(PostprocessInput2, bFiltered ? BilinearTextureSampler0 : PostprocessInput2Sampler, UV);
// PPI_PostprocessInput3
case 17: return Texture2DSample(PostprocessInput3, bFiltered ? BilinearTextureSampler0 : PostprocessInput3Sampler, UV);
// PPI_PostprocessInput4
case 18: return Texture2DSample(PostprocessInput4, bFiltered ? BilinearTextureSampler0 : PostprocessInput4Sampler, UV);
// PPI_PostprocessInput5
case 19: return Texture2DSample(PostprocessInput5, bFiltered ? BilinearTextureSampler0 : PostprocessInput5Sampler, UV);
// PPI_PostprocessInput6
case 20: return Texture2DSample(PostprocessInput6, bFiltered ? BilinearTextureSampler0 : PostprocessInput6Sampler, UV);
#endif // __POST_PROCESS_COMMON__
// PPI_DecalMask
case 21: return 0; // material compiler will return an error
// PPI_ShadingModel
case 22: return float4(GetShadingModelColor(ScreenSpaceData.GBuffer.ShadingModelID), 1);
// PPI_AmbientOcclusion
case 23: return ScreenSpaceData.AmbientOcclusion;
// PPI_CustomStencil
case 24: return ScreenSpaceData.GBuffer.CustomStencil;
// PPI_StoredBaseColor
case 25: return float4(ScreenSpaceData.GBuffer.StoredBaseColor, 0);
// PPI_StoredSpecular
case 26: return float4(ScreenSpaceData.GBuffer.StoredSpecular.rrr, 0);
default:
return float4(0, 0, 0, 0);
}
#endif
#if PS4_PROFILE
// PS4 as of SDK 930 can't figure out the switch statement exits through all code paths...
return float4(0, 0, 0, 0);
#endif
}
#endif // NEEDS_SCENE_TEXTURES
// Uniform material expressions.
// can return in tangent space or world space (use MATERIAL_TANGENTSPACENORMAL)
half3 GetMaterialNormalRaw(FPixelMaterialInputs PixelMaterialInputs)
{
return PixelMaterialInputs.Normal;
}
half3 GetMaterialNormal(FMaterialPixelParameters Parameters, FPixelMaterialInputs PixelMaterialInputs)
{
half3 RetNormal;
RetNormal = GetMaterialNormalRaw(PixelMaterialInputs);

#if (USE_EDITOR_SHADERS && !(ES2_PROFILE || ES3_1_PROFILE || ESDEFERRED_PROFILE)) || MOBILE_EMULATION
{
// this feature is only needed for development/editor - we can compile it out for a shipping build (see r.CompileShadersForDevelopment cvar help)
half3 OverrideNormal = View.NormalOverrideParameter.xyz;
#if !MATERIAL_TANGENTSPACENORMAL
OverrideNormal = Parameters.TangentToWorld[2] * (1 - View.NormalOverrideParameter.w);
#endif
RetNormal = RetNormal * View.NormalOverrideParameter.w + OverrideNormal;
}
#endif
return RetNormal;
}
half3 GetMaterialEmissiveRaw(FPixelMaterialInputs PixelMaterialInputs)
{
return PixelMaterialInputs.EmissiveColor;
}
half3 GetMaterialEmissive(FPixelMaterialInputs PixelMaterialInputs)
{
half3 EmissiveColor = GetMaterialEmissiveRaw(PixelMaterialInputs);
#if !MATERIAL_ALLOW_NEGATIVE_EMISSIVECOLOR
EmissiveColor = max(EmissiveColor, 0.0f);
#endif
return EmissiveColor;
}
half3 GetMaterialEmissiveForCS(FMaterialPixelParameters Parameters)
{
return 0;
}
half3 GetMaterialBaseColorRaw(FPixelMaterialInputs PixelMaterialInputs)
{
return PixelMaterialInputs.BaseColor;
}
half3 GetMaterialBaseColor(FPixelMaterialInputs PixelMaterialInputs)
{
return saturate(GetMaterialBaseColorRaw(PixelMaterialInputs));
}
half GetMaterialMetallicRaw(FPixelMaterialInputs PixelMaterialInputs)
{
return PixelMaterialInputs.Metallic;
}
half GetMaterialMetallic(FPixelMaterialInputs PixelMaterialInputs)
{
return saturate(GetMaterialMetallicRaw(PixelMaterialInputs));
}
half GetMaterialSpecularRaw(FPixelMaterialInputs PixelMaterialInputs)
{
return PixelMaterialInputs.Specular;
}
half GetMaterialSpecular(FPixelMaterialInputs PixelMaterialInputs)
{
return saturate(GetMaterialSpecularRaw(PixelMaterialInputs));
}
half GetMaterialRoughnessRaw(FPixelMaterialInputs PixelMaterialInputs)
{
return PixelMaterialInputs.Roughness;
}
half GetMaterialRoughness(FPixelMaterialInputs PixelMaterialInputs)
{
#if MATERIAL_FULLY_ROUGH
return 1;
#endif
#if (ES2_PROFILE || ES3_1_PROFILE)
// The smallest normalized value that can be represented in IEEE 754 (FP16) is 2^-14 = 6.1e-5.
// The code will make the following computation involving roughness: 1.0 / Roughness^4.
// Therefore to prevent division by zero on devices that do not support denormals, Roughness^4
//
// Note that we also clamp to 1.0 to match the deferred renderer on PC where the roughness is
// stored in an 8-bit value and thus automatically clamped at 1.0.
half Roughness = GetMaterialRoughnessRaw(PixelMaterialInputs);
#if MOBILE_EMULATION
{
// this is only needed for mobile preview on PC
Roughness = Roughness * View.RoughnessOverrideParameter.y + View.RoughnessOverrideParameter.x;
}
#endif
// Increase value from 0.09 to 0.12 to fix missing specular lobe problem on device
return clamp( Roughness, 0.12, 1.0 );
#else
half Roughness = saturate(GetMaterialRoughnessRaw(PixelMaterialInputs));

#if (USE_EDITOR_SHADERS && !ESDEFERRED_PROFILE)
{
// this feature is only needed for development/editor - we can compile it out for a shipping build (see r.CompileShadersForDevelopment cvar help)
Roughness = Roughness * View.RoughnessOverrideParameter.y + View.RoughnessOverrideParameter.x;
}
#endif

return Roughness;
#endif
}
half GetMaterialTranslucencyDirectionalLightingIntensity()
{
return 1.00000;
}
half GetMaterialTranslucentShadowDensityScale()
{
return 0.50000;
}
half GetMaterialTranslucentSelfShadowDensityScale()
{
return 2.00000;
}
half GetMaterialTranslucentSelfShadowSecondDensityScale()
{
return 10.00000;
}
half GetMaterialTranslucentSelfShadowSecondOpacity()
{
return 0.00000;
}
half GetMaterialTranslucentBackscatteringExponent()
{
return 30.00000;
}
half3 GetMaterialTranslucentMultipleScatteringExtinction()
{
return MaterialFloat3(1.00000, 0.83300, 0.58800);
}
// This is the clip value constant that is defined in the material (range 0..1)
// Use GetMaterialMask() to get the Material Mask combined with this.
half GetMaterialOpacityMaskClipValue()
{
return 0.33330;
}
// Should only be used by GetMaterialOpacity(), returns the unmodified value generated from the shader expressions of the opacity input.
// To compute the opacity depending on the material blending GetMaterialOpacity() should be called instead.
half GetMaterialOpacityRaw(FPixelMaterialInputs PixelMaterialInputs)
{
return PixelMaterialInputs.Opacity;
}
#if MATERIALBLENDING_MASKED || (DECAL_BLEND_MODE == DECALBLENDMODEID_VOLUMETRIC)
// Returns the material mask value generated from the material expressions.
// Use GetMaterialMask() to get the value altered depending on the material blend mode.
half GetMaterialMaskInputRaw(FPixelMaterialInputs PixelMaterialInputs)
{
return PixelMaterialInputs.OpacityMask;
}
// Returns the material mask value generated from the material expressions minus the used defined
half GetMaterialMask(FPixelMaterialInputs PixelMaterialInputs)
{
return GetMaterialMaskInputRaw(PixelMaterialInputs) - GetMaterialOpacityMaskClipValue();
}
#endif
// Returns the material opacity depending on the material blend mode.
half GetMaterialOpacity(FPixelMaterialInputs PixelMaterialInputs)
{
// Clamp to valid range to prevent negative colors from lerping
return saturate(GetMaterialOpacityRaw(PixelMaterialInputs));
}
float3 GetMaterialWorldPositionOffset(FMaterialVertexParameters Parameters)
{
#if USE_INSTANCING
// skip if this instance is hidden
if (Parameters.PerInstanceParams.z < 1.f)
{
return float3(0,0,0);
}
#endif
return MaterialFloat3(0.00000000,0.00000000,0.00000000);;
}
float3 GetMaterialPreviousWorldPositionOffset(FMaterialVertexParameters Parameters)
{
#if USE_INSTANCING
// skip if this instance is hidden
if (Parameters.PerInstanceParams.z < 1.f)
{
return float3(0,0,0);
}
#endif
return MaterialFloat3(0.00000000,0.00000000,0.00000000);;
}
half3 GetMaterialWorldDisplacement(FMaterialTessellationParameters Parameters)
{
return MaterialFloat3(0.00000000,0.00000000,0.00000000);;
}
half GetMaterialMaxDisplacement()
{
return 0.00000;
}
half GetMaterialTessellationMultiplier(FMaterialTessellationParameters Parameters)
{
return 1.00000000;;
}
// .rgb:SubsurfaceColor, .a:SSProfileId in 0..1 range
half4 GetMaterialSubsurfaceDataRaw(FMaterialPixelParameters Parameters)
{
return 0;;
}
half4 GetMaterialSubsurfaceData(FMaterialPixelParameters Parameters)
{
half4 OutSubsurface = GetMaterialSubsurfaceDataRaw(Parameters);
OutSubsurface.rgb = saturate(OutSubsurface.rgb);
return OutSubsurface;
}
half GetMaterialCustomData0(FMaterialPixelParameters Parameters)
{
return 1.00000000;;
}
half GetMaterialCustomData1(FMaterialPixelParameters Parameters)
{
return 0.10000000;;
}
half GetMaterialAmbientOcclusionRaw(FPixelMaterialInputs PixelMaterialInputs)
{
return PixelMaterialInputs.AmbientOcclusion;
}
half GetMaterialAmbientOcclusion(FPixelMaterialInputs PixelMaterialInputs)
{
return saturate(GetMaterialAmbientOcclusionRaw(PixelMaterialInputs));
}
half2 GetMaterialRefraction(FPixelMaterialInputs PixelMaterialInputs)
{
return PixelMaterialInputs.Refraction;
}
#if NUM_MATERIAL_TEXCOORDS
void GetMaterialCustomizedUVs(FMaterialVertexParameters Parameters, out float2 OutTexCoords[NUM_MATERIAL_TEXCOORDS])
{
OutTexCoords[0] = Parameters.TexCoords[0].xy;
}
#endif
float GetMaterialPixelDepthOffset(FPixelMaterialInputs PixelMaterialInputs)
{
return PixelMaterialInputs.PixelDepthOffset;
}
void CalcPixelMaterialInputs(in out FMaterialPixelParameters Parameters, in out FPixelMaterialInputs PixelMaterialInputs)
{
// Initial calculations (required for Normal)
// The Normal is a special case as it might have its own expressions and also be used to calculate other inputs, so perform the assignment here
PixelMaterialInputs.Normal = MaterialFloat3(0.00000000,0.00000000,1.00000000);
// Note that here MaterialNormal can be in world space or tangent space
float3 MaterialNormal = GetMaterialNormal(Parameters, PixelMaterialInputs);
#if MATERIAL_TANGENTSPACENORMAL
#if SIMPLE_FORWARD_SHADING
// Drop normal with simple forward shading for scalability
MaterialNormal = float3(0, 0, 1);
#endif
// flip the normal for backfaces being rendered with a two-sided material
Parameters.TangentNormal = MaterialNormal * Parameters.TwoSidedSign;
#if FEATURE_LEVEL >= FEATURE_LEVEL_SM4
// ES2 will rely on only the final normalize for performance
Parameters.TangentNormal = normalize(Parameters.TangentNormal);
#endif
// normalizing after the tangent space to world space conversion improves quality with sheared bases (UV layout to WS causes shrearing)
// use full precision normalize to avoid overflows
Parameters.WorldNormal = normalize(float3(TransformTangentVectorToWorld(Parameters.TangentToWorld, Parameters.TangentNormal)));
#else
// Here we don't supoport two sided materials
Parameters.TangentNormal = Parameters.WorldNormal = normalize(MaterialNormal);
#endif
Parameters.ReflectionVector = ReflectionAboutCustomWorldNormal(Parameters, Parameters.WorldNormal, false);
#if !PARTICLE_SPRITE_FACTORY
Parameters.Particle.MotionBlurFade = 1.0f;
#endif // !PARTICLE_SPRITE_FACTORY
// Now the rest of the inputs
MaterialFloat2 Local0 = (Parameters.TexCoords[0].xy * 1.00000000);
MaterialFloat4 Local1 = ProcessMaterialColorTextureLookup(Texture2DSample(Material.Texture2D_0,Material.Texture2D_0Sampler,Local0));
MaterialFloat4 Local2 = ProcessMaterialColorTextureLookup(Texture2DSample(Material.Texture2D_1,Material.Texture2D_1Sampler,Local0));
MaterialFloat4 Local3 = ProcessMaterialColorTextureLookup(Texture2DSample(Material.Texture2D_2,Material.Texture2D_2Sampler,Local0));
MaterialFloat3 Local4 = lerp(Local1.rgb,Local2.rgb,Local3.rgb);
MaterialFloat Local5 = (View.GameTime * 0.25000000);
MaterialFloat Local6 = (Local5 * 6.28318548);
MaterialFloat Local7 = cos(Local6);
MaterialFloat3 Local8 = lerp(MaterialFloat3(0.00000000,1.00000000,0.79365396),(MaterialFloat3(0.13119602,0.83164400,0.83999997) * 5.00000000),MaterialFloat(Local7));
MaterialFloat3 Local9 = lerp(MaterialFloat3(0.00000000,0.00000000,0.00000000),Local8,Local3.rgb);
MaterialFloat3 Local10 = (Local4 * Local9);
MaterialFloat3 Local11 = (Local10 + Material.VectorExpressions[1].rgb);
MaterialFloat3 Local12 = (Local4 * Material.VectorExpressions[2].rgb);
PixelMaterialInputs.EmissiveColor = Local11;
PixelMaterialInputs.Opacity = 1.00000000;
PixelMaterialInputs.OpacityMask = 1.00000000;
PixelMaterialInputs.BaseColor = Local12;
PixelMaterialInputs.Metallic = 0.00000000;
PixelMaterialInputs.Specular = 0.50000000;
PixelMaterialInputs.Roughness = 0.50000000;
PixelMaterialInputs.AmbientOcclusion = 1.00000000;
PixelMaterialInputs.Refraction = 0;
PixelMaterialInputs.PixelDepthOffset = 0.00000000;
}
// Programmatically set the line number after all the material inputs which have a variable number of line endings
// This allows shader error line numbers after this point to be the same regardless of which material is being compiled
#line 1588
void ClipLODTransition(float2 SvPosition, float DitherFactor)
{
if (abs(DitherFactor) > .001)
{
float RandCos = cos(dot(floor(SvPosition.xy), float2(347.83451793,3343.28371963)));
float RandomVal = frac(RandCos * 1000.0);
half RetVal = (DitherFactor < 0.0) ?
(DitherFactor + 1.0 > RandomVal) :
(DitherFactor < RandomVal);
clip(RetVal - .001);
}
}
void ClipLODTransition(FMaterialPixelParameters Parameters, float DitherFactor)
{
ClipLODTransition(Parameters.SvPosition.xy, DitherFactor);
}
#define REQUIRES_VF_ATTRIBUTES_FOR_CLIPPING (USE_INSTANCING && USE_DITHERED_LOD_TRANSITION)
#if USE_INSTANCING && USE_DITHERED_LOD_TRANSITION
void ClipLODTransition(FMaterialPixelParameters Parameters)
{
ClipLODTransition(Parameters, Parameters.PerInstanceParams.w);
}
#elif USE_DITHERED_LOD_TRANSITION && !USE_STENCIL_LOD_DITHER
float NonInstancedDitherLODFactor;
void ClipLODTransition(FMaterialPixelParameters Parameters)
{
if (NonInstancedDitherLODFactor != 0.0)
{
ClipLODTransition(Parameters, NonInstancedDitherLODFactor);
}
}
void ClipLODTransition(float2 SvPosition)
{
if (NonInstancedDitherLODFactor != 0.0)
{
ClipLODTransition(SvPosition, NonInstancedDitherLODFactor);
}
}
#else
void ClipLODTransition(FMaterialPixelParameters Parameters)
{
}
void ClipLODTransition(float2 SvPosition)
{
}
#endif
void GetMaterialClippingShadowDepth(FMaterialPixelParameters Parameters, FPixelMaterialInputs PixelMaterialInputs)
{
ClipLODTransition(Parameters);
#if MATERIALBLENDING_MASKED
clip(GetMaterialMask(PixelMaterialInputs));
#elif MATERIALBLENDING_TRANSLUCENT
clip(GetMaterialOpacity(PixelMaterialInputs) - 1.0f / 255.0f);
#endif
}
void GetMaterialClippingVelocity(FMaterialPixelParameters Parameters, FPixelMaterialInputs PixelMaterialInputs)
{
ClipLODTransition(Parameters);
#if MATERIALBLENDING_MASKED && MATERIAL_DITHER_OPACITY_MASK
clip(GetMaterialMaskInputRaw(PixelMaterialInputs) - 1.0f / 255.0f);
#elif MATERIALBLENDING_MASKED
clip(GetMaterialMask(PixelMaterialInputs));
#elif MATERIALBLENDING_TRANSLUCENT
clip(GetMaterialOpacity(PixelMaterialInputs) - 1.0f / 255.0f);
#endif
}
void GetMaterialCoverageAndClipping(FMaterialPixelParameters Parameters, FPixelMaterialInputs PixelMaterialInputs)
{
ClipLODTransition(Parameters);
#if MATERIALBLENDING_MASKED
#if MATERIAL_DITHER_OPACITY_MASK
/*
012
234
401
*/
//float Dither5 = ( ( (uint)Parameters.SvPosition.x + (uint)Parameters.SvPosition.y * 2 + (uint)View.TemporalAAParams.x ) % 5 ) / 5.0;
float Dither5 = frac( ( Parameters.SvPosition.x + Parameters.SvPosition.y * 2 - 1.5 + View.TemporalAAParams.x ) / 5 );
float Noise = frac( dot( float2( 171.0, 231.0 ) / 71, Parameters.SvPosition.xy ) );
float Dither = ( Dither5 * 5 + Noise ) * (1.0 / 6.0) - 0.5;
clip( GetMaterialMask(PixelMaterialInputs) + Dither );
#else
clip(GetMaterialMask(PixelMaterialInputs));
#endif
#endif
}
#define FrontFaceSemantic SV_IsFrontFace
#define FIsFrontFace bool
half GetFloatFacingSign(FIsFrontFace bIsFrontFace)
{
return bIsFrontFace ? +1 : -1;
}
#if MATERIAL_TWOSIDED_SEPARATE_PASS
#define OPTIONAL_IsFrontFace
static const FIsFrontFace bIsFrontFace = 1;
#else
#define OPTIONAL_IsFrontFace , in FIsFrontFace bIsFrontFace : FrontFaceSemantic
#endif
void CalcMaterialParametersEx(
in out FMaterialPixelParameters Parameters,
in out FPixelMaterialInputs PixelMaterialInputs,
float4 SvPosition,
float4 ScreenPosition,
FIsFrontFace bIsFrontFace,
float3 TranslatedWorldPosition,
float3 TranslatedWorldPositionExcludingShaderOffsets)
{
// Remove the pre view translation
Parameters.WorldPosition_CamRelative = TranslatedWorldPosition.xyz;
Parameters.AbsoluteWorldPosition = TranslatedWorldPosition.xyz - ResolvedView.PreViewTranslation.xyz;
// the variables will have been initialised to 0 earlier.
#if USE_WORLD_POSITION_EXCLUDING_SHADER_OFFSETS
Parameters.WorldPosition_NoOffsets_CamRelative = TranslatedWorldPositionExcludingShaderOffsets;
Parameters.WorldPosition_NoOffsets = TranslatedWorldPositionExcludingShaderOffsets - ResolvedView.PreViewTranslation.xyz;
#endif
Parameters.SvPosition = SvPosition;
Parameters.ScreenPosition = ScreenPosition;
#if COMPILER_GLSL_ES2 || COMPILER_GLSL_ES3_1
// Originally this was normalize(normalize(TranslatedWorldPosition.xyz)) but tegra4 appears to optimize that out.
Parameters.CameraVector = normalize(-0.01 * Parameters.WorldPosition_CamRelative.xyz);
#else
// TranslatedWorldPosition is the world position translated to the camera position, which is just -CameraVector
Parameters.CameraVector = normalize(-Parameters.WorldPosition_CamRelative.xyz);
#endif
Parameters.LightVector = 0;
Parameters.TwoSidedSign = 1.0f;
#if MATERIAL_TWOSIDED && HAS_PRIMITIVE_UNIFORM_BUFFER
Parameters.TwoSidedSign *= View.CullingSign * Primitive.LocalToWorldDeterminantSign;
#if !MATERIAL_TWOSIDED_SEPARATE_PASS
Parameters.TwoSidedSign *= GetFloatFacingSign(bIsFrontFace);
#endif
#endif
// Now that we have all the pixel-related parameters setup, calculate the Material Input/Attributes and Normal
CalcPixelMaterialInputs(Parameters, PixelMaterialInputs);
}
// convenience function to setup CalcMaterialParameters assuming we don't support TranslatedWorldPositionExcludingShaderOffsets
void CalcMaterialParameters(
in out FMaterialPixelParameters Parameters,
in out FPixelMaterialInputs PixelMaterialInputs,
float4 SvPosition,
FIsFrontFace bIsFrontFace)
{
float4 ScreenPosition = SvPositionToResolvedScreenPosition(SvPosition);
float3 TranslatedWorldPosition = SvPositionToResolvedTranslatedWorld(SvPosition);
CalcMaterialParametersEx(Parameters, PixelMaterialInputs, SvPosition, ScreenPosition, bIsFrontFace, TranslatedWorldPosition, TranslatedWorldPosition);
}
void CalcMaterialParametersPost(
in out FMaterialPixelParameters Parameters,
in out FPixelMaterialInputs PixelMaterialInputs,
float4 SvPosition,
FIsFrontFace bIsFrontFace)
{
float4 ScreenPosition = SvPositionToScreenPosition(SvPosition);
float3 TranslatedWorldPosition = SvPositionToTranslatedWorld(SvPosition);
CalcMaterialParametersEx(Parameters, PixelMaterialInputs, SvPosition, ScreenPosition, bIsFrontFace, TranslatedWorldPosition, TranslatedWorldPosition);
}
/** Assemble the transform from tangent space into world space */
half3x3 AssembleTangentToWorld( half3 TangentToWorld0, half4 TangentToWorld2 )
{
// Any mismatch with xNormal will cause distortions for baked normal maps.
// Derive the third basis vector off of the other two.
// Flip based on the determinant sign
half3 TangentToWorld1 = cross(TangentToWorld2.xyz,TangentToWorld0) * TangentToWorld2.w;
// Transform from tangent space to world space
return half3x3(TangentToWorld0, TangentToWorld1, TangentToWorld2.xyz);
}
// Whether the material shader should output pixel depth offset
#define OUTPUT_PIXEL_DEPTH_OFFSET (WANT_PIXEL_DEPTH_OFFSET && (MATERIALBLENDING_SOLID || MATERIALBLENDING_MASKED) && !ES2_PROFILE)
// Whether to use the hidden d3d11 feature that supports depth writes with ZCull by only pushing into the screen
//@todo - use for other SM5 platforms
#define SUPPORTS_CONSERVATIVE_DEPTH_WRITES ((COMPILER_HLSL && FEATURE_LEVEL >= FEATURE_LEVEL_SM5) || (PS4_PROFILE))
#define USE_CONSERVATIVE_DEPTH_WRITES (OUTPUT_PIXEL_DEPTH_OFFSET && SUPPORTS_CONSERVATIVE_DEPTH_WRITES)
#if USE_CONSERVATIVE_DEPTH_WRITES
#if COMPILER_HLSL
// Note: for some reason using SV_DepthLessEqual without these interpolation modifiers causes a compile error in d3d
#define INPUT_POSITION_QUALIFIERS linear noperspective centroid
#define DEPTH_WRITE_SEMANTIC SV_DepthLessEqual
#elif PS4_PROFILE
#define INPUT_POSITION_QUALIFIERS
#define DEPTH_WRITE_SEMANTIC S_DEPTH_LE_OUTPUT
#else
#error USE_CONSERVATIVE_DEPTH_WRITES enabled for unsupported platform
#endif
#else
#define INPUT_POSITION_QUALIFIERS
#define DEPTH_WRITE_SEMANTIC SV_DEPTH
#endif
#if OUTPUT_PIXEL_DEPTH_OFFSET
#define OPTIONAL_OutDepthConservative ,out float OutDepth : DEPTH_WRITE_SEMANTIC
#define OPTIONAL_OutDepth ,out float OutDepth : SV_DEPTH
#else
#define OPTIONAL_OutDepthConservative
#define OPTIONAL_OutDepth
#endif
float ApplyPixelDepthOffsetToMaterialParameters(inout FMaterialPixelParameters MaterialParameters, FPixelMaterialInputs PixelMaterialInputs, out float OutDepth)
{
// Can only push into the scene for conservative depth writes
float PixelDepthOffset = max(GetMaterialPixelDepthOffset(PixelMaterialInputs), 0);
// Update positions used for shading
MaterialParameters.ScreenPosition.w += PixelDepthOffset;
MaterialParameters.SvPosition.w = MaterialParameters.ScreenPosition.w;
MaterialParameters.AbsoluteWorldPosition += MaterialParameters.CameraVector * PixelDepthOffset;
OutDepth = MaterialParameters.ScreenPosition.z / MaterialParameters.ScreenPosition.w;
return PixelDepthOffset;
}

(完)