// Copyright Epic Games, Inc. All Rights Reserved. /** * MaterialTemplate.usf: Filled in by FHLSLMaterialTranslator::GetMaterialShaderCode for each material being compiled. */ #ifndef ENABLE_NEW_HLSL_GENERATOR #define ENABLE_NEW_HLSL_GENERATOR 0 #endif #if !MATERIAL_LWC_ENABLED #define UE_DF_FORCE_FP32_OPS 1 #endif #ifndef USE_VERTEX_FACTORY_FWD #define USE_VERTEX_FACTORY_FWD 0 #endif #if USE_VERTEX_FACTORY_FWD #include "/Engine/Generated/VertexFactoryFwd.ush" #endif #include "/Engine/Private/WorldSpaceMath.ush" #include "/Engine/Private/SceneTexturesCommon.ush" #include "/Engine/Private/EyeAdaptationCommon.ush" #include "/Engine/Private/Random.ush" #include "/Engine/Private/SobolRandom.ush" #include "/Engine/Private/MonteCarlo.ush" #include "/Engine/Generated/UniformBuffers/Material.ush" #include "/Engine/Private/DepthOfFieldCommon.ush" #include "/Engine/Private/CircleDOFCommon.ush" #include "/Engine/Private/DistanceField/GlobalDistanceFieldShared.ush" #include "/Engine/Private/PhysicsFieldSampler.ush" #include "/Engine/Private/SceneData.ush" #include "/Engine/Private/HairShadingCommon.ush" #include "/Engine/Private/HairStrands/HairCardsAttributeCommon.ush" #include "/Engine/Private/HairStrands/HairStrandsAttributeCommon.ush" #include "/Engine/Private/DeferredShadingCommon.ush" #include "/Engine/Private/SparseVolumeTexture/SparseVolumeTextureCommon.ush" #include "/Engine/Shared/EnvironmentComponentsFlags.h" #include "/Engine/Private/MeshPaintTextureCommon.ush" #include "/Engine/Private/ColorSpace.ush" #include "/Engine/Private/VirtualTextureMaterial.ush" #include "/Engine/Private/MaterialCache/MaterialCacheMaterialCommon.ush" #if MATERIAL_SKY_ATMOSPHERE && PROJECT_SUPPORT_SKY_ATMOSPHERE #include "/Engine/Private/Quantization.ush" #endif // Reduce latency of Nanite material base pass pixel shaders caused by register pressure due to the calculation of WPO #if PIXELSHADER && IS_NANITE_SHADING_PASS && IS_BASE_PASS COMPILER_FORCE_WAVE32_MODE #endif // Update this GUID to force all material shaders to recompile // Merge conflicts on this line should be resolved by generating a new GUID #pragma message("UESHADERMETADATA_VERSION E745A0A0-E5E2-4976-B407-F7C36FCE9880") #if USES_SPEEDTREE #include "/Engine/Private/SpeedTreeCommon.ush" #endif ////////////////////////////////////////////////////////////////////////// //! Must match ESceneTextureId #define PPI_SceneColor 0 #define PPI_SceneDepth 1 #define PPI_DiffuseColor 2 #define PPI_SpecularColor 3 #define PPI_SubsurfaceColor 4 #define PPI_BaseColor 5 #define PPI_Specular 6 #define PPI_Metallic 7 #define PPI_WorldNormal 8 #define PPI_SeparateTranslucency 9 #define PPI_Opacity 10 #define PPI_Roughness 11 #define PPI_MaterialAO 12 #define PPI_CustomDepth 13 #define PPI_PostProcessInput0 14 #define PPI_PostProcessInput1 15 #define PPI_PostProcessInput2 16 #define PPI_PostProcessInput3 17 #define PPI_PostProcessInput4 18 #define PPI_PostProcessInput5 19 // (UNUSED) #define PPI_PostProcessInput6 20 // (UNUSED) #define PPI_DecalMask 21 #define PPI_ShadingModelColor 22 #define PPI_ShadingModelID 23 #define PPI_AmbientOcclusion 24 #define PPI_CustomStencil 25 #define PPI_StoredBaseColor 26 #define PPI_StoredSpecular 27 #define PPI_Velocity 28 #define PPI_WorldTangent 29 #define PPI_Anisotropy 30 #define PPI_IsFirstPerson 31 #ifdef POST_PROCESS_PROPAGATE_ALPHA_INPUT // Arbitrary internal value to fetch local variable propagated alpha stored in the SceneTextureLookup function #define PPI_PropagatedAlpha 64 #endif // Defines to remap PPI_UserSceneTexture0-6 to PPI_PostProcessInput0-6, generated via GenerateUserSceneTextureRemapDefines %{user_scene_texture_remap} ////////////////////////////////////////////////////////////////////////// #define NUM_MATERIAL_TEXCOORDS_VERTEX %{num_material_texcoords_vertex} #define NUM_MATERIAL_TEXCOORDS %{num_material_texcoords} #define NUM_CUSTOM_VERTEX_INTERPOLATORS %{num_custom_vertex_interpolators} #define NUM_TEX_COORD_INTERPOLATORS %{num_tex_coord_interpolators} // Vertex interpolators offsets definition %{vertex_interpolators_offsets_definition} #if NUM_VIRTUALTEXTURE_SAMPLES || LIGHTMAP_VT_ENABLED #include "/Engine/Private/VirtualTextureCommon.ush" #endif #include "/Engine/Private/MaterialTexture.ush" #ifdef MIN_MATERIAL_TEXCOORDS #include "/Engine/Private/MinMaterialTexCoords.ush" #endif #if MATERIAL_SKY_ATMOSPHERE && PROJECT_SUPPORT_SKY_ATMOSPHERE #include "/Engine/Private/SkyAtmosphereCommon.ush" #endif #if MATERIAL_SHADINGMODEL_SINGLELAYERWATER || POST_PROCESS_MATERIAL // For WaterSampleSceneDepthWithoutWater() // This is currently included for post processing materials even if the SceneDepthWithoutWater texture is not sampled. // It might be worth it to add a switch to only include it if the texture is actually required by the material. #include "/Engine/Private/SingleLayerWaterCommon.ush" #endif #include "/Engine/Private/PaniniProjection.ush" #include "/Engine/Private/DBufferNormalReprojection.ush" #ifndef USE_INSTANCE_CULLING #define USE_INSTANCE_CULLING 0 #endif #ifndef USE_STENCIL_LOD_DITHER #define USE_STENCIL_LOD_DITHER USE_STENCIL_LOD_DITHER_DEFAULT #endif //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 || SUBSTRATE_BLENDING_TRANSLUCENT_GREYTRANSMITTANCE || SUBSTRATE_BLENDING_TRANSLUCENT_COLOREDTRANSMITTANCE || SUBSTRATE_BLENDING_COLOREDTRANSMITTANCEONLY) #define IS_MATERIAL_TRANSLUCENT_AND_LIT (MATERIALBLENDING_TRANSLUCENT) #define IS_MESHPARTICLE_FACTORY (PARTICLE_MESH_FACTORY || NIAGARA_MESH_FACTORY) #define HAS_INSTANCE_LOCAL_TO_WORLD_PS (NEEDS_INSTANCE_LOCAL_TO_WORLD_PS && (USE_INSTANCING || USE_INSTANCE_CULLING || IS_MESHPARTICLE_FACTORY || IS_NANITE_PASS)) #define HAS_INSTANCE_WORLD_TO_LOCAL_PS (NEEDS_INSTANCE_WORLD_TO_LOCAL_PS && (USE_INSTANCING || USE_INSTANCE_CULLING || IS_MESHPARTICLE_FACTORY || IS_NANITE_PASS)) #define IS_HAIR_FACTORY (HAIR_STRAND_MESH_FACTORY || HAIR_CARD_MESH_FACTORY) #if IS_NANITE_PASS #define ALLOW_CLIP 0 #define clip(x) #if COMPUTE_SHADED // Threads are remapped into 2x2 quad layout, so quad read x/y/d work for ddx/ddy // https://microsoft.github.io/DirectX-Specs/d3d/HLSL_SM_6_6_Derivatives.html #elif COMPUTESHADER #define ddx(x) 0 #define ddy(x) 0 #define fwidth(x) 0 #endif #else #define ALLOW_CLIP 1 #endif #define TEMPLATE_USES_SUBSTRATE (SUBSTRATE_ENABLED && MATERIAL_IS_SUBSTRATE) #define SUBSTRATE_OPAQUE_MATERIAL (TEMPLATE_USES_SUBSTRATE && ((!SUBSTRATE_MATERIAL_EXPORT_EXECUTED && MATERIALBLENDING_ANY_TRANSLUCENT<=0) || SUBSTRATE_MATERIAL_EXPORT_FROM_OPAQUE)) #define SUBSTRATE_TRANSLUCENT_MATERIAL (TEMPLATE_USES_SUBSTRATE && ((!SUBSTRATE_MATERIAL_EXPORT_EXECUTED && MATERIALBLENDING_ANY_TRANSLUCENT>0 ) || SUBSTRATE_MATERIAL_EXPORT_FROM_TRANSLUCENT)) #define SUBSTRATE_OPAQUE_DEFERRED (SUBSTRATE_OPAQUE_MATERIAL && !FORWARD_SHADING) #define SUBSTRATE_TRANSLUCENT_FORWARD (SUBSTRATE_TRANSLUCENT_MATERIAL && !FORWARD_SHADING) #define SUBSTRATE_FORWARD_SHADING (FORWARD_SHADING && (SUBSTRATE_OPAQUE_MATERIAL || SUBSTRATE_TRANSLUCENT_MATERIAL)) #define SUBSTRATE_OUTPUT_ROUGH_REFRACTION (SUBSTRATE_ENABLED && SUBSTRATE_OPAQUE_ROUGH_REFRACTION_ENABLED && SUBSTRATE_MATERIAL_OUTPUT_OPAQUE_ROUGH_REFRACTIONS) // Premultiplied alpha override is only enabled when alpha composite blending is used and the root node input is fed. #define SUBSTRATE_USE_PREMULTALPHA_OVERRIDE (SUBSTRATE_ENABLED && MATERIALBLENDING_ALPHACOMPOSITE && SUBSTRATE_PREMULTIPLIED_ALPHA_OPACITY_OVERRIDEN) #if TEMPLATE_USES_SUBSTRATE #define SUBSTRATE_INLINE_SHADING 1 #endif #if SUBSTRATE_TRANSLUCENT_FORWARD && !SUBSTRATE_MATERIAL_EXPORT_FROM_TRANSLUCENT && !defined(SUBSTRATE_DEFERRED_SHADING) // This is for translucent surface to be able to fetch opaque material data such as normal, albedo and more from SceneTextures (See SceneTextureLookup). #define SUBSTRATE_DEFERRED_SHADING 1 #endif #if SUBSTRATE_ENABLED #include "/Engine/Private/Substrate/Substrate.ush" #ifdef SubstrateStruct #define SubstratePublicStruct SubstrateStruct #else #define SubstratePublicStruct Substrate #endif #include "/Engine/Public/SubstratePublic.ush" #undef SubstratePublicStruct #else struct FSubstrateData { uint Dummy; }; FSubstrateData GetInitialisedSubstrateData() { return (FSubstrateData)0; } #endif #if TEMPLATE_USES_SUBSTRATE #include "/Engine/Private/Substrate/SubstrateTree.ush" #include "/Engine/Private/Substrate/SubstrateLegacyConversion.ush" // This can only included here due to interaction between SUBSTRATE_MATERIAL_EXPORT_EXECUTED and SUBSTRATE_LEGACY_MATERIAL_APPLIES_FINAL_WEIGHT #endif #include "/Engine/Private/DBufferDecalShared.ush" /** * Parameters used by vertex and pixel shaders to access particle properties. */ struct FMaterialParticleParameters { /** Relative time [0-1]. */ half RelativeTime; /** Fade amount due to motion blur. */ half MotionBlurFade; /** Random value per particle [0-1]. */ half Random; /** XYZ: Direction, W: Speed. */ half4 Velocity; /** Per-particle color. */ half4 Color; /** Particle translated world space position and size(radius). */ float4 TranslatedWorldPositionAndSize; /** Previous Particle translated world space position and size */ float4 PrevTranslatedWorldPositionAndSize; /** Particle world space position. */ FWSVector3 WorldPosition; /** Previous particle world space position. */ FWSVector3 PrevWorldPosition; /** Macro UV scale and bias. */ half4 MacroUV; /** Dynamic parameters used by particle systems. */ #if NIAGARA_PARTICLE_FACTORY && (DYNAMIC_PARAMETERS_MASK != 0) uint DynamicParameterValidMask; #endif half4 DynamicParameter; #if( DYNAMIC_PARAMETERS_MASK & 2) half4 DynamicParameter1; #endif #if (DYNAMIC_PARAMETERS_MASK & 4) half4 DynamicParameter2; #endif #if (DYNAMIC_PARAMETERS_MASK & 8) half4 DynamicParameter3; #endif /** mesh particle transform */ FDFMatrix ParticleToWorld; /** Inverse mesh particle transform */ FDFInverseMatrix WorldToParticle; #if USE_PARTICLE_SUBUVS /** SubUV texture coordinates*/ MaterialFloat2 SubUVCoords[2]; /** SubUV interpolation value*/ MaterialFloat SubUVLerp; #endif /** The size of the particle. */ float2 Size; /** The sprite rotation */ float SpriteRotation; }; float4 GetDynamicParameter(FMaterialParticleParameters Parameters, float4 Default, int ParameterIndex=0) { #if (NIAGARA_PARTICLE_FACTORY) switch ( ParameterIndex ) { #if (DYNAMIC_PARAMETERS_MASK & 1) case 0: return float4( (Parameters.DynamicParameterValidMask & 0x0001) == 0 ? Default.x : Parameters.DynamicParameter.x, (Parameters.DynamicParameterValidMask & 0x0002) == 0 ? Default.y : Parameters.DynamicParameter.y, (Parameters.DynamicParameterValidMask & 0x0004) == 0 ? Default.z : Parameters.DynamicParameter.z, (Parameters.DynamicParameterValidMask & 0x0008) == 0 ? Default.w : Parameters.DynamicParameter.w ); #endif #if (DYNAMIC_PARAMETERS_MASK & 2) case 1: return float4( (Parameters.DynamicParameterValidMask & 0x0010) == 0 ? Default.x : Parameters.DynamicParameter1.x, (Parameters.DynamicParameterValidMask & 0x0020) == 0 ? Default.y : Parameters.DynamicParameter1.y, (Parameters.DynamicParameterValidMask & 0x0040) == 0 ? Default.z : Parameters.DynamicParameter1.z, (Parameters.DynamicParameterValidMask & 0x0080) == 0 ? Default.w : Parameters.DynamicParameter1.w ); #endif #if (DYNAMIC_PARAMETERS_MASK & 4) case 2: return float4( (Parameters.DynamicParameterValidMask & 0x0100) == 0 ? Default.x : Parameters.DynamicParameter2.x, (Parameters.DynamicParameterValidMask & 0x0200) == 0 ? Default.y : Parameters.DynamicParameter2.y, (Parameters.DynamicParameterValidMask & 0x0400) == 0 ? Default.z : Parameters.DynamicParameter2.z, (Parameters.DynamicParameterValidMask & 0x0800) == 0 ? Default.w : Parameters.DynamicParameter2.w ); #endif #if (DYNAMIC_PARAMETERS_MASK & 8) case 3: return float4( (Parameters.DynamicParameterValidMask & 0x1000) == 0 ? Default.x : Parameters.DynamicParameter3.x, (Parameters.DynamicParameterValidMask & 0x2000) == 0 ? Default.y : Parameters.DynamicParameter3.y, (Parameters.DynamicParameterValidMask & 0x4000) == 0 ? Default.z : Parameters.DynamicParameter3.z, (Parameters.DynamicParameterValidMask & 0x8000) == 0 ? Default.w : Parameters.DynamicParameter3.w ); #endif default: return Default; } #elif (PARTICLE_FACTORY) if ( ParameterIndex == 0 ) { return Parameters.DynamicParameter; } #endif return Default; } SHADER_PUSH_WARNINGS_STATE SHADER_DISABLE_WARNINGS /** Material declarations */ %{material_declarations} /** FMaterialAttributes utilities */ %{material_attributes_utilities} SHADER_POP_WARNINGS_STATE /** * Parameters calculated from the pixel material inputs. */ struct FPixelMaterialInputs { %{pixel_material_inputs} #if TEMPLATE_USES_SUBSTRATE FSubstrateData GetFrontSubstrateData() { #if SUBSTRATE_USE_FULLYSIMPLIFIED_MATERIAL == 1 return FullySimplifiedFrontMaterial; #else return FrontMaterial; #endif } #endif // TEMPLATE_USES_SUBSTRATE }; /** * Worldspace transforms/vectors, converted to the LWC format used by the material. */ struct FMaterialLWCData { // Vertex/pixel FWSVector3 AbsoluteWorldPosition; FWSVector3 WorldPosition_NoOffsets; // Primitive FWSMatrix LocalToWorld; FWSInverseMatrix WorldToLocal; FWSMatrix PreviousLocalToWorld; FWSInverseMatrix PreviousWorldToLocal; FWSMatrix InstanceToWorld; FWSInverseMatrix WorldToInstance; FWSMatrix PreviousInstanceToWorld; FWSVector3 ObjectWorldPosition; FWSVector3 ActorWorldPosition; FWSMatrix ParticleToWorld; FWSInverseMatrix WorldToParticle; FWSVector3 ParticleWorldPosition; FWSVector3 PrevParticleWorldPosition; // View FWSVector3 PreViewTranslation; FWSVector3 PrevPreViewTranslation; FWSVector3 WorldViewOrigin; FWSVector3 PrevWorldViewOrigin; FWSVector3 WorldCameraOrigin; FWSVector3 PrevWorldCameraOrigin; }; /** * Parameters needed by pixel shader material inputs, related to Geometry. * These are independent of vertex factory. */ struct FMaterialPixelParameters { #if NUM_TEX_COORD_INTERPOLATORS float2 TexCoords[NUM_TEX_COORD_INTERPOLATORS]; #endif /** Interpolated vertex color, in linear color space. */ half4 VertexColor; /** Normalized world space normal. */ half3 WorldNormal; /** Normalized world space tangent. */ half3 WorldTangent; /** Normalized world space reflected camera vector. */ half3 ReflectionVector; /** Normalized world space camera vector, which is the vector from the point being shaded to the camera position. */ float3 CameraVector; /** World space light vector, only valid when rendering a light function. */ half3 LightVector; /** * Like SV_Position (.xy is pixel position at pixel center, z:DeviceZ, .w:SceneDepth) * using shader generated value SV_POSITION * Note: this is not relative to the current viewport. RelativePixelPosition = MaterialParameters.SvPosition.xy - View.ViewRectMin.xy; */ float4 SvPosition; /** Post projection position reconstructed from SvPosition, before the divide by W. left..top -1..1, bottom..top -1..1 within the viewport, W is the SceneDepth */ float4 ScreenPosition; /** * The pixel UV for a view, considering the buffer resolution containing it. * This is precomputed because when PixelDepthOffset is used, it affects ScreenPosition which would then prevent us from recovering the correct UV later. * See ApplyPixelDepthOffsetToMaterialParameters for the details */ float2 ViewBufferUV; #if IS_NANITE_PASS float4 PrevScreenPosition; #endif 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; #if USE_WORLDVERTEXNORMAL_CENTER_INTERPOLATION /** World vertex normal interpolated at the pixel center that is safe to use for derivatives. */ half3 WorldVertexNormal_Center; #endif /** * Interpolated worldspace position of this pixel * todo: Make this TranslatedWorldPosition and also rename the VS WorldPosition to be TranslatedWorldPosition */ FDFVector3 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 */ FDFVector3 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; /** Offset applied to the lighting position for translucency, used to break up aliasing artifacts. */ half3 LightingPositionOffset; /** Derivatives */ float3 WorldPosition_DDX; float3 WorldPosition_DDY; float3 WorldGeoNormal_DDX; float3 WorldGeoNormal_DDY; float4 VertexColor_DDX; float4 VertexColor_DDY; float4 ScreenPosition_DDX; float4 ScreenPosition_DDY; #if NUM_TEX_COORD_INTERPOLATORS float2 TexCoords_DDX[NUM_TEX_COORD_INTERPOLATORS]; float2 TexCoords_DDY[NUM_TEX_COORD_INTERPOLATORS]; #endif float AOMaterialMask; #if LIGHTMAP_UV_ACCESS float2 LightmapUVs; float2 LightmapUVs_DDX; float2 LightmapUVs_DDY; #endif half PerInstanceRandom; #if USE_INSTANCING || USE_INSTANCE_CULLING || IS_NANITE_PASS float4 PerInstanceParams; #endif // Index into View.PrimitiveSceneData uint PrimitiveId; #if IS_NANITE_PASS uint InstanceId; #endif #if MOBILE_MULTI_VIEW uint ViewId; #endif #if USES_PER_INSTANCE_CUSTOM_DATA && VF_USE_PRIMITIVE_SCENE_DATA uint CustomDataOffset; uint CustomDataCount; #endif // Actual primitive Id #if HAIR_STRAND_MESH_FACTORY uint HairControlPointId; // Control point ID float2 HairPrimitiveUV; // U: parametric distance between the two surrounding control point. V: parametric distance along hair width #endif #if HAIR_CARD_MESH_FACTORY float2 HairPrimitiveUV; // AtlasUV float2 HairPrimitiveRootUV; // RootUV half4 HairPrimitiveMaterial; // Card material half HairPrimitiveLength; // Card length half HairPrimitiveGroupIndex;// Card group index #endif #if HAS_INSTANCE_LOCAL_TO_WORLD_PS FDFMatrix InstanceLocalToWorld; #endif #if HAS_INSTANCE_WORLD_TO_LOCAL_PS FDFInverseMatrix InstanceWorldToLocal; #endif /** Per-particle properties. Only valid for particle vertex factories. */ FMaterialParticleParameters Particle; #if FEATURE_LEVEL == FEATURE_LEVEL_ES3_1 float4 LayerWeights; #endif #if TEX_COORD_SCALE_ANALYSIS /** Parameters used by the MaterialTexCoordScales shader. */ FTexCoordScalesParams TexCoordScalesParams; #endif #if COMPILER_HLSL // Workaround for "error X3067: 'GetObjectWorldPosition': ambiguous function call" // Which happens when FMaterialPixelParameters and FMaterialVertexParameters have the same number of floats with the HLSL compiler ver 9.29.952.3111 // Function overload resolution appears to identify types based on how many floats / ints / etc they contain uint Dummy; #endif #if NUM_VIRTUALTEXTURE_SAMPLES || LIGHTMAP_VT_ENABLED FVirtualTextureFeedbackParams VirtualTextureFeedback; #endif #if WATER_MESH_FACTORY uint WaterWaveParamIndex; #endif #if SHADER_TYPE == ST_GrayscaleFont || SHADER_TYPE == ST_ColorFont || SHADER_TYPE == ST_SdfFont || SHADER_TYPE == ST_MsdfFont float4 FontSignedDistanceData; #endif #if CLOUD_LAYER_PIXEL_SHADER float CloudSampleAltitude; float CloudSampleAltitudeInLayer; float CloudSampleNormAltitudeInLayer; float4 VolumeSampleConservativeDensity; float ShadowSampleDistance; float3 CloudEmptySpaceSkippingSphereCenterWorldPosition; float CloudEmptySpaceSkippingSphereRadius; #endif #if TEMPLATE_USES_SUBSTRATE FSharedLocalBases SharedLocalBases; FSubstrateTree SubstrateTree; #if SUBSTRATE_USE_FULLYSIMPLIFIED_MATERIAL == 1 FSharedLocalBases SharedLocalBasesFullySimplified; FSubstrateTree SubstrateTreeFullySimplified; #endif FSubstratePixelFootprint SubstratePixelFootprint; FSubstratePixelHeader GetFrontSubstrateHeader() { FSubstratePixelHeader SubstratePixelHeader = InitialiseSubstratePixelHeader(); #if SUBSTRATE_USE_FULLYSIMPLIFIED_MATERIAL == 1 SubstratePixelHeader.SubstrateTree = SubstrateTreeFullySimplified; SubstratePixelHeader.ClosureCount = SubstrateTreeFullySimplified.BSDFCount; SubstratePixelHeader.SharedLocalBases = SharedLocalBasesFullySimplified; #else SubstratePixelHeader.SubstrateTree = SubstrateTree; SubstratePixelHeader.ClosureCount = SubstrateTree.BSDFCount; SubstratePixelHeader.SharedLocalBases = SharedLocalBases; #endif SubstratePixelHeader.IrradianceAO = InitIrradianceAndOcclusion(); return SubstratePixelHeader; } #endif FMaterialAttributes MaterialAttributes; FMaterialLWCData LWCData; }; /** * Compile out calls to StoreTexCoordScale if we're not doing texcoord scale analysis */ #if TEX_COORD_SCALE_ANALYSIS #define MaterialStoreTexCoordScale(Parameters, UV, TextureReferenceIndex) StoreTexCoordScale(Parameters.TexCoordScalesParams, UV, TextureReferenceIndex) #define MaterialStoreTexSample(Parameters, UV, TextureReferenceIndex) StoreTexSample(Parameters.TexCoordScalesParams, UV, TextureReferenceIndex) #else #define MaterialStoreTexCoordScale(Parameters, UV, TextureReferenceIndex) 1.0f #define MaterialStoreTexSample(Parameters, UV, TextureReferenceIndex) 1.0f #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 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; // Local position relative to the primitive, i.e. before transformation into worldspace. float3 PositionPrimitiveSpace; // Local position relative to the instance. If instancing is disabled, this is the same as PositionPrimitiveSpace. float3 PositionInstanceSpace; // TangentToWorld[2] is WorldVertexNormal half3x3 TangentToWorld; #if USES_PER_INSTANCE_CUSTOM_DATA && VF_USE_PRIMITIVE_SCENE_DATA uint CustomDataOffset; uint CustomDataCount; #endif float PerInstanceRandom; #if USE_INSTANCING || USE_INSTANCE_CULLING || IS_MESHPARTICLE_FACTORY || IS_NANITE_PASS FDFMatrix InstanceLocalToWorld; FDFInverseMatrix InstanceWorldToLocal; #endif #if USE_INSTANCING || USE_INSTANCE_CULLING || IS_NANITE_PASS /** Per-instance properties. */ float4 PerInstanceParams; #if !USE_INSTANCE_CULLING uint InstanceId; #endif uint InstanceOffset; #endif // If either USE_INSTANCING or (IS_MESHPARTICLE_FACTORY && FEATURE_LEVEL >= FEATURE_LEVEL_SM4) // is true, PrevFrameLocalToWorld is a per-instance transform FDFMatrix PrevFrameLocalToWorld; float3 PreSkinnedPosition; float3 PreSkinnedNormal; half4 VertexColor; #if NUM_MATERIAL_TEXCOORDS_VERTEX float2 TexCoords[NUM_MATERIAL_TEXCOORDS_VERTEX]; #if FEATURE_LEVEL == FEATURE_LEVEL_ES3_1 float2 TexCoordOffset; // Offset for UV localization for large UV values #endif #endif #if NUM_CUSTOM_VERTEX_INTERPOLATORS // Used by new HLSL translator float2 CustomInterpolators[NUM_CUSTOM_VERTEX_INTERPOLATORS]; #endif /** Per-particle properties. Only valid for particle vertex factories. */ FMaterialParticleParameters Particle; #if WATER_MESH_FACTORY uint WaterWaveParamIndex; #endif FMaterialAttributes MaterialAttributes; /** Cached primitive and instance data */ FSceneDataIntermediates SceneData; // FIXME: just for compatibility with assets that use custom HLSL expressions, will be removed once we fix up all these assets // Index into View.PrimitiveSceneData uint PrimitiveId; bool bEvaluateWorldPositionOffset; FMaterialLWCData LWCData; }; FMaterialVertexParameters MakeInitializedMaterialVertexParameters() { FMaterialVertexParameters Result = (FMaterialVertexParameters)0; Result.PrimitiveId = INVALID_PRIMITIVE_ID; Result.bEvaluateWorldPositionOffset = true; return Result; } float MaterialReadInterpolatorComponent(FMaterialPixelParameters Parameters, int InterpolatorIndex) { #if NUM_TEX_COORD_INTERPOLATORS return Parameters.TexCoords[NUM_MATERIAL_TEXCOORDS + InterpolatorIndex / 2][InterpolatorIndex & 1]; #else return 0.0f; #endif } void MaterialPackInterpolatorComponent(in out FMaterialVertexParameters Parameters, int InterpolatorIndex, float Value) { #if NUM_CUSTOM_VERTEX_INTERPOLATORS Parameters.CustomInterpolators[InterpolatorIndex / 2][InterpolatorIndex & 1] = Value; #endif } #if GET_PRIMITIVE_DATA_OVERRIDE FPrimitiveSceneData GetPrimitiveData(FMaterialVertexParameters Parameters); FPrimitiveSceneData GetPrimitiveData(FMaterialPixelParameters Parameters); #else FPrimitiveSceneData GetPrimitiveData(FMaterialVertexParameters Parameters) { #if HAS_PRIMITIVE_UNIFORM_BUFFER return Parameters.SceneData.Primitive; #else return (FPrimitiveSceneData)0; #endif } FPrimitiveSceneData GetPrimitiveData(FMaterialPixelParameters Parameters) { #if HAS_PRIMITIVE_UNIFORM_BUFFER return GetPrimitiveData(Parameters.PrimitiveId); #else return (FPrimitiveSceneData)0; #endif } #endif bool UnpackUniform_bool(uint Packed, uint BitOffset) { return (bool)((Packed >> BitOffset) & 0x1); } bool2 UnpackUniform_bool2(uint Packed, uint BitOffset) { return bool2(UnpackUniform_bool(Packed, BitOffset), UnpackUniform_bool(Packed, BitOffset + 1)); } bool3 UnpackUniform_bool3(uint Packed, uint BitOffset) { return bool3(UnpackUniform_bool(Packed, BitOffset), UnpackUniform_bool(Packed, BitOffset + 1), UnpackUniform_bool(Packed, BitOffset + 2)); } bool4 UnpackUniform_bool4(uint Packed, uint BitOffset) { return bool4(UnpackUniform_bool(Packed, BitOffset), UnpackUniform_bool(Packed, BitOffset + 1), UnpackUniform_bool(Packed, BitOffset + 2), UnpackUniform_bool(Packed, BitOffset + 3)); } /** * Returns the upper 3x3 portion of the LocalToWorld matrix. */ MaterialFloat3x3 GetLocalToWorld3x3(FMaterialVertexParameters Parameters) { #if DECAL_PRIMITIVE return (float3x3)DecalToWorld; #else return DFToFloat3x3(GetPrimitiveData(Parameters).LocalToWorld); #endif } MaterialFloat3x3 GetPreviousLocalToWorld3x3(FMaterialVertexParameters Parameters) { #if DECAL_PRIMITIVE return (float3x3)DecalToWorld; #else return DFToFloat3x3(GetPrimitiveData(Parameters).PreviousLocalToWorld); #endif } MaterialFloat3x3 GetLocalToWorld3x3(FMaterialPixelParameters Parameters) { #if DECAL_PRIMITIVE return (float3x3)DecalToWorld; #else return DFToFloat3x3(GetPrimitiveData(Parameters).LocalToWorld); #endif } MaterialFloat3x3 GetLocalToWorld3x3() { return DFToFloat3x3(GetPrimitiveDataFromUniformBuffer().LocalToWorld); } FDFInverseMatrix GetWorldToInstanceDF(FMaterialVertexParameters Parameters) { #if USE_INSTANCING || USE_INSTANCE_CULLING || IS_MESHPARTICLE_FACTORY || IS_NANITE_PASS return Parameters.InstanceWorldToLocal; #else return GetPrimitiveData(Parameters).WorldToLocal; #endif } FWSInverseMatrix GetWorldToInstance(FMaterialVertexParameters Parameters) { return Parameters.LWCData.WorldToInstance; } FDFInverseMatrix GetWorldToInstanceDF(FMaterialPixelParameters Parameters) { #if HAS_INSTANCE_WORLD_TO_LOCAL_PS return Parameters.InstanceWorldToLocal; #else return GetPrimitiveData(Parameters).WorldToLocal; #endif } FWSInverseMatrix GetWorldToInstance(FMaterialPixelParameters Parameters) { return Parameters.LWCData.WorldToInstance; } FDFMatrix GetInstanceToWorldDF(FMaterialVertexParameters Parameters) { #if USE_INSTANCING || USE_INSTANCE_CULLING || IS_MESHPARTICLE_FACTORY || IS_NANITE_PASS return Parameters.InstanceLocalToWorld; #else return GetPrimitiveData(Parameters).LocalToWorld; #endif } FWSMatrix GetInstanceToWorld(FMaterialVertexParameters Parameters) { return Parameters.LWCData.InstanceToWorld; } FDFMatrix GetPrevInstanceToWorldDF(FMaterialVertexParameters Parameters) { #if USE_INSTANCING || USE_INSTANCE_CULLING || IS_MESHPARTICLE_FACTORY || IS_NANITE_PASS return Parameters.PrevFrameLocalToWorld; #else return GetPrimitiveData(Parameters).PreviousLocalToWorld; #endif } FWSMatrix GetPrevInstanceToWorld(FMaterialVertexParameters Parameters) { return Parameters.LWCData.PreviousInstanceToWorld; } FDFMatrix GetInstanceToWorldDF(FMaterialPixelParameters Parameters) { #if HAS_INSTANCE_LOCAL_TO_WORLD_PS return Parameters.InstanceLocalToWorld; #else return GetPrimitiveData(Parameters).LocalToWorld; #endif } FWSMatrix GetInstanceToWorld(FMaterialPixelParameters Parameters) { return Parameters.LWCData.InstanceToWorld; } FDFMatrix GetPrevInstanceToWorldDF(FMaterialPixelParameters Parameters) { return GetPrimitiveData(Parameters).PreviousLocalToWorld; } FWSMatrix GetPrevInstanceToWorld(FMaterialPixelParameters Parameters) { return Parameters.LWCData.PreviousInstanceToWorld; } FWSVector3 GetPreViewTranslation(FMaterialVertexParameters Parameters) { return Parameters.LWCData.PreViewTranslation; } FWSVector3 GetPreViewTranslation(FMaterialPixelParameters Parameters) { return Parameters.LWCData.PreViewTranslation; } FWSVector3 GetPrevPreViewTranslation(FMaterialVertexParameters Parameters) { return Parameters.LWCData.PrevPreViewTranslation; } FWSVector3 GetPrevPreViewTranslation(FMaterialPixelParameters Parameters) { return Parameters.LWCData.PrevPreViewTranslation; } FWSVector3 GetWorldViewOrigin(FMaterialVertexParameters Parameters) { return Parameters.LWCData.WorldViewOrigin; } FWSVector3 GetWorldViewOrigin(FMaterialPixelParameters Parameters) { return Parameters.LWCData.WorldViewOrigin; } FWSVector3 GetPrevWorldViewOrigin(FMaterialVertexParameters Parameters) { return Parameters.LWCData.PrevWorldViewOrigin; } FWSVector3 GetPrevWorldViewOrigin(FMaterialPixelParameters Parameters) { return Parameters.LWCData.PrevWorldViewOrigin; } FWSVector3 GetWorldCameraOrigin(FMaterialVertexParameters Parameters) { return Parameters.LWCData.WorldCameraOrigin; } FWSVector3 GetWorldCameraOrigin(FMaterialPixelParameters Parameters) { return Parameters.LWCData.WorldCameraOrigin; } FWSVector3 GetPrevWorldCameraOrigin(FMaterialVertexParameters Parameters) { return Parameters.LWCData.PrevWorldCameraOrigin; } FWSVector3 GetPrevWorldCameraOrigin(FMaterialPixelParameters Parameters) { return Parameters.LWCData.PrevWorldCameraOrigin; } // World position 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); } FWSVector3 GetWorldPosition(FMaterialVertexParameters Parameters) { return Parameters.LWCData.AbsoluteWorldPosition; } FWSVector3 GetPrevWorldPosition(FMaterialVertexParameters Parameters) { return WSSubtract(GetPrevTranslatedWorldPosition(Parameters), GetPrevPreViewTranslation(Parameters)); } FWSVector3 GetWorldPosition(FMaterialPixelParameters Parameters) { return Parameters.LWCData.AbsoluteWorldPosition; } FWSVector3 GetWorldPosition_NoMaterialOffsets(FMaterialPixelParameters Parameters) { return Parameters.LWCData.WorldPosition_NoOffsets; } float3 GetTranslatedWorldPosition(FMaterialPixelParameters Parameters) { return Parameters.WorldPosition_CamRelative; } float3 GetTranslatedWorldPosition_NoMaterialOffsets(FMaterialPixelParameters Parameters) { return Parameters.WorldPosition_NoOffsets_CamRelative; } FWSVector3 GetParticleWorldPosition(FMaterialVertexParameters Parameters) { return Parameters.LWCData.ParticleWorldPosition; } FWSVector3 GetParticleWorldPosition(FMaterialPixelParameters Parameters) { return Parameters.LWCData.ParticleWorldPosition; } FWSVector3 GetPrevParticleWorldPosition(FMaterialVertexParameters Parameters) { return Parameters.LWCData.PrevParticleWorldPosition; } FWSVector3 GetPrevParticleWorldPosition(FMaterialPixelParameters Parameters) { return Parameters.LWCData.PrevParticleWorldPosition; } // Periodic World Position float3 GetPeriodicWorldOrigin(float3 Scale) { // Following three lines can be replaced with `ScaledPos = DFDivide(ResolvedView.PreViewTranslation, Scale)` but this is faster and reasonably precise. float3 InvScale = rcp(Scale); FDFVector3 ScaledPos = DFTwoProduct(ResolvedView.PreViewTranslation.High, InvScale); ScaledPos.Low += ResolvedView.PreViewTranslation.Low * InvScale; return DFFracDemote(ScaledPos) * Scale; } float3 GetPeriodicWorldOrigin_Pow2(float3 Scale) { return DFFmodByPow2Demote(ResolvedView.PreViewTranslation, Scale); } // Local position float3 GetPositionPrimitiveSpace(FMaterialPixelParameters Parameters) { // See also: derivative computation of local position in material translator. return DFFastMultiplyDemote(Parameters.AbsoluteWorldPosition, GetPrimitiveData(Parameters).WorldToLocal); } float3 GetPositionPrimitiveSpace(FMaterialVertexParameters Parameters) { return Parameters.PositionPrimitiveSpace; } float3 GetPrevPositionPrimitiveSpace(FMaterialVertexParameters Parameters) { // Previous position and current position are sharing the // same attribute in Parameters, because in BasePassVertexShader.usf // and in VelocityShader.usf, we are regenerating a Parameters with // GetMaterialVertexParameters(..., bIsPreviousFrame=true) return Parameters.PositionPrimitiveSpace; } float3 GetPositionPrimitiveSpace_NoMaterialOffsets(FMaterialPixelParameters Parameters) { return DFFastMultiplyDemote(Parameters.WorldPosition_NoOffsets, GetPrimitiveData(Parameters).WorldToLocal); } float3 GetPositionInstanceSpace(FMaterialPixelParameters Parameters) { // See also: derivative computation of local position in material translator. return DFFastMultiplyDemote(Parameters.AbsoluteWorldPosition, GetWorldToInstanceDF(Parameters)); } float3 GetPositionInstanceSpace(FMaterialVertexParameters Parameters) { return Parameters.PositionInstanceSpace; } float3 GetPrevPositionInstanceSpace(FMaterialVertexParameters Parameters) { // Previous position and current position are sharing the // same attribute in Parameters, because in BasePassVertexShader.usf // and in VelocityShader.usf, we are regenerating a Parameters with // GetMaterialVertexParameters(..., bIsPreviousFrame=true) return Parameters.PositionInstanceSpace; } float3 GetPositionInstanceSpace_NoMaterialOffsets(FMaterialPixelParameters Parameters) { return DFFastMultiplyDemote(Parameters.WorldPosition_NoOffsets, GetWorldToInstanceDF(Parameters)); } // Screen position float4 GetScreenPosition(FMaterialVertexParameters Parameters) { return mul(float4(Parameters.WorldPosition, 1.0f), ResolvedView.TranslatedWorldToClip); } float4 GetScreenPosition(FMaterialPixelParameters Parameters) { return Parameters.ScreenPosition; } uint GetViewId(FMaterialPixelParameters Parameters) { #if MOBILE_MULTI_VIEW return Parameters.ViewId; #else return 0; #endif } // Returns the pixel's depth, in world units. Works for both orthographic and perspective projections: float GetPixelDepth(FMaterialVertexParameters Parameters) { return GetScreenPositionDepth(GetScreenPosition(Parameters)); } float GetPixelDepth(FMaterialPixelParameters Parameters) { return GetScreenPositionDepth(GetScreenPosition(Parameters)); } float2 GetSceneTextureUV(FMaterialVertexParameters Parameters) { return ScreenAlignedPosition(GetScreenPosition(Parameters)); } float2 GetSceneTextureUV(FMaterialPixelParameters Parameters) { return SvPositionToBufferUV(Parameters.SvPosition); } float2 GetViewportUV(FMaterialVertexParameters Parameters) { #if POST_PROCESS_MATERIAL return Parameters.WorldPosition.xy; #else return BufferUVToViewportUV(GetSceneTextureUV(Parameters)); #endif } float2 GetPixelPosition(FMaterialVertexParameters Parameters) { return GetViewportUV(Parameters) * View.ViewSizeAndInvSize.xy; } #if POST_PROCESS_MATERIAL float2 GetPixelPosition(FMaterialPixelParameters Parameters) { return Parameters.SvPosition.xy - float2(PostProcessOutput_ViewportMin); } float2 GetViewportUV(FMaterialPixelParameters Parameters) { return GetPixelPosition(Parameters) * PostProcessOutput_ViewportSizeInverse; } #else float2 GetPixelPosition(FMaterialPixelParameters Parameters) { return Parameters.SvPosition.xy - ResolvedView.ViewRectMin.xy; } float2 GetViewportUV(FMaterialPixelParameters Parameters) { return SvPositionToViewportUV(Parameters.SvPosition); } #endif float GetWaterWaveParamIndex(FMaterialPixelParameters Parameters) { #if WATER_MESH_FACTORY return (float)Parameters.WaterWaveParamIndex; #else return 0.0f; #endif } float GetWaterWaveParamIndex(FMaterialVertexParameters Parameters) { #if WATER_MESH_FACTORY return (float)Parameters.WaterWaveParamIndex; #else return 0.0f; #endif } float PostVolumeUserFlagTest(float CompareValue) { return (((int)1 << (int)CompareValue) & View.PostVolumeUserFlags) ? 1.0 : 0.0; } // Returns whether a scene texture id is a for a post process input or not. bool IsPostProcessInputSceneTexture(const uint SceneTextureId) { return (SceneTextureId >= PPI_PostProcessInput0 && SceneTextureId <= PPI_PostProcessInput6); } // Returns the view size and texel size in a given scene texture. float4 GetSceneTextureViewSize(const uint SceneTextureId) { #if POST_PROCESS_MATERIAL if (IsPostProcessInputSceneTexture(SceneTextureId)) { switch (SceneTextureId) { case PPI_PostProcessInput0: return float4(PostProcessInput_0_ViewportSize, PostProcessInput_0_ViewportSizeInverse); case PPI_PostProcessInput1: return float4(PostProcessInput_1_ViewportSize, PostProcessInput_1_ViewportSizeInverse); case PPI_PostProcessInput2: return float4(PostProcessInput_2_ViewportSize, PostProcessInput_2_ViewportSizeInverse); case PPI_PostProcessInput3: return float4(PostProcessInput_3_ViewportSize, PostProcessInput_3_ViewportSizeInverse); case PPI_PostProcessInput4: return float4(PostProcessInput_4_ViewportSize, PostProcessInput_4_ViewportSizeInverse); default: return float4(0, 0, 0, 0); } } #endif return ResolvedView.ViewSizeAndInvSize; } // Return the buffer UV min and max for a given scene texture id. float4 GetSceneTextureUVMinMax(const uint SceneTextureId) { #if POST_PROCESS_MATERIAL if (IsPostProcessInputSceneTexture(SceneTextureId)) { switch (SceneTextureId) { case PPI_PostProcessInput0: return float4(PostProcessInput_0_UVViewportBilinearMin, PostProcessInput_0_UVViewportBilinearMax); case PPI_PostProcessInput1: return float4(PostProcessInput_1_UVViewportBilinearMin, PostProcessInput_1_UVViewportBilinearMax); case PPI_PostProcessInput2: return float4(PostProcessInput_2_UVViewportBilinearMin, PostProcessInput_2_UVViewportBilinearMax); case PPI_PostProcessInput3: return float4(PostProcessInput_3_UVViewportBilinearMin, PostProcessInput_3_UVViewportBilinearMax); case PPI_PostProcessInput4: return float4(PostProcessInput_4_UVViewportBilinearMin, PostProcessInput_4_UVViewportBilinearMax); default: return float4(0, 0, 1, 1); } } #endif return View.BufferBilinearUVMinMax; } // Transforms viewport UV to scene texture's UV. MaterialFloat2 ViewportUVToSceneTextureUV(MaterialFloat2 ViewportUV, const uint SceneTextureId) { #if POST_PROCESS_MATERIAL if (IsPostProcessInputSceneTexture(SceneTextureId)) { switch (SceneTextureId) { case PPI_PostProcessInput0: return ViewportUV * PostProcessInput_0_UVViewportSize + PostProcessInput_0_UVViewportMin; case PPI_PostProcessInput1: return ViewportUV * PostProcessInput_1_UVViewportSize + PostProcessInput_1_UVViewportMin; case PPI_PostProcessInput2: return ViewportUV * PostProcessInput_2_UVViewportSize + PostProcessInput_2_UVViewportMin; case PPI_PostProcessInput3: return ViewportUV * PostProcessInput_3_UVViewportSize + PostProcessInput_3_UVViewportMin; case PPI_PostProcessInput4: return ViewportUV * PostProcessInput_4_UVViewportSize + PostProcessInput_4_UVViewportMin; default: return ViewportUV; } } #endif return ViewportUVToBufferUV(ViewportUV); } // Manually clamp scene texture UV as if using a clamp sampler. MaterialFloat2 ClampSceneTextureUV(MaterialFloat2 BufferUV, const uint SceneTextureId) { float4 MinMax = GetSceneTextureUVMinMax(SceneTextureId); return clamp(BufferUV, MinMax.xy, MinMax.zw); } // Get default scene texture's UV. MaterialFloat2 GetDefaultSceneTextureUV(FMaterialVertexParameters Parameters, const uint SceneTextureId) { return GetSceneTextureUV(Parameters); } // Get default scene texture's UV. MaterialFloat2 GetDefaultSceneTextureUV(FMaterialPixelParameters Parameters, const uint SceneTextureId) { #if POST_PROCESS_MATERIAL return ViewportUVToSceneTextureUV(GetViewportUV(Parameters), SceneTextureId); #else return GetSceneTextureUV(Parameters); #endif } #if DECAL_PRIMITIVE && NUM_MATERIAL_TEXCOORDS /* * Material node DecalMipmapLevel's code designed to avoid the 2x2 pixels artefacts on the edges around where the decal * is projected to. The technique is fetched from (http://www.humus.name/index.php?page=3D&ID=84). * * 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 * current depth of an differet mesh that the other pixel of the same groups. If this mesh is very far from the other * 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 * to avoid the 2x2 pixels mipmap artefacts on the edges. */ 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 // DECAL_PRIMITIVE && 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 // DECAL_PRIMITIVE && NUM_MATERIAL_TEXCOORDS FWSVector3 GetActorWorldPosition(FMaterialVertexParameters Parameters) { return Parameters.LWCData.ActorWorldPosition; } FWSVector3 GetActorWorldPosition(FMaterialPixelParameters Parameters) { return Parameters.LWCData.ActorWorldPosition; } FWSVector3 GetPreviousActorWorldPosition(FMaterialVertexParameters Parameters) { float3 InstanceToActor = WSMultiplyDemote(GetActorWorldPosition(Parameters), GetWorldToInstance(Parameters)); return WSMultiply(InstanceToActor, Parameters.LWCData.PreviousLocalToWorld); } FWSVector3 GetPreviousActorWorldPosition(FMaterialPixelParameters Parameters) { float3 InstanceToActor = WSMultiplyDemote(GetActorWorldPosition(Parameters), GetWorldToInstance(Parameters)); return WSMultiply(InstanceToActor, Parameters.LWCData.PreviousLocalToWorld); } float3 GetActorTranslatedWorldPosition(FMaterialVertexParameters Parameters) { #if PRIMITIVE_HAS_TILEOFFSET_DATA && VIEW_HAS_TILEOFFSET_DATA return LWCToFloat(LWCAdd(GetPrimitiveData(Parameters).ActorWorldPositionTO, ResolvedView.TileOffset.PreViewTranslation)); #else return DFFastToTranslatedWorld(GetPrimitiveData(Parameters).ActorWorldPosition, ResolvedView.PreViewTranslation); #endif } float3 GetActorTranslatedWorldPosition(FMaterialPixelParameters Parameters) { #if PRIMITIVE_HAS_TILEOFFSET_DATA && VIEW_HAS_TILEOFFSET_DATA return LWCToFloat(LWCAdd(GetPrimitiveData(Parameters).ActorWorldPositionTO, ResolvedView.TileOffset.PreViewTranslation)); #else return DFFastToTranslatedWorld(GetPrimitiveData(Parameters).ActorWorldPosition, ResolvedView.PreViewTranslation); #endif } float3 GetPreviousActorTranslatedWorldPosition(FMaterialVertexParameters Parameters) { float3 InstanceToActor = WSMultiplyDemote(GetActorWorldPosition(Parameters), GetWorldToInstance(Parameters)); return WSSubtractDemote(WSMultiply(InstanceToActor, Parameters.LWCData.PreviousLocalToWorld), WSNegate(GetPreViewTranslation(Parameters))); } float3 GetPreviousActorTranslatedWorldPosition(FMaterialPixelParameters Parameters) { float3 InstanceToActor = WSMultiplyDemote(GetActorWorldPosition(Parameters), GetWorldToInstance(Parameters)); return WSSubtractDemote(WSMultiply(InstanceToActor, Parameters.LWCData.PreviousLocalToWorld), WSNegate(GetPreViewTranslation(Parameters))); } float3 GetObjectOrientation(FMaterialVertexParameters Parameters) { #if DECAL_PRIMITIVE return DecalOrientation.xyz; #else return GetPrimitiveData(Parameters).ObjectOrientation; #endif } float3 GetObjectOrientation(FMaterialPixelParameters Parameters) { #if DECAL_PRIMITIVE return DecalOrientation.xyz; #else return GetPrimitiveData(Parameters).ObjectOrientation; #endif } float4 DecalColor() { #if DECAL_PRIMITIVE return DecalColorParam; #else return float4(1.0f, 1.0f, 1.0f, 1.0f); #endif } float DecalLifetimeOpacity() { #if DECAL_PRIMITIVE return DecalParams.y; #else return 0.0f; #endif } /** Per Instance Custom Data Getter (Pixel Shader Variant - Visibility Buffer) */ float GetPerInstanceCustomData(FMaterialPixelParameters Parameters, int Index, float DefaultValue) { #if VF_USE_PRIMITIVE_SCENE_DATA && USES_PER_INSTANCE_CUSTOM_DATA const uint FloatIndex = uint(Index); #if SHADER_USES_PRIMITIVE_UBO return LoadInstanceCustomDataElementUBO(Parameters.PrimitiveId, FloatIndex, DefaultValue); #else BRANCH if (FloatIndex < Parameters.CustomDataCount) { const uint Float4Offset = Parameters.CustomDataOffset + (FloatIndex >> 2u); const float4 Float4Packed = LoadInstancePayloadDataElement(Float4Offset); return Float4Packed[FloatIndex & 0x3u]; } #endif #elif USE_INSTANCING && USES_PER_INSTANCE_CUSTOM_DATA const uint FloatIndex = uint(Index); BRANCH if (FloatIndex < InstanceVF.NumCustomDataFloats) { const uint InstanceDataIndex = asuint(Parameters.PerInstanceParams.w); const uint BufferStartIndex = InstanceDataIndex * InstanceVF.NumCustomDataFloats; return InstanceVF.InstanceCustomDataBuffer[BufferStartIndex + FloatIndex]; } #endif return DefaultValue; } // Per Instance Custom Data Getter (Vertex Shader Only) /** Get the per-instance custom data when instancing */ float GetPerInstanceCustomData(FMaterialVertexParameters Parameters, int Index, float DefaultValue) { #if VF_USE_PRIMITIVE_SCENE_DATA && USES_PER_INSTANCE_CUSTOM_DATA const uint FloatIndex = uint(Index); #if SHADER_USES_PRIMITIVE_UBO return LoadInstanceCustomDataElementUBO(Parameters.PrimitiveId, FloatIndex, DefaultValue); #else BRANCH if (FloatIndex < Parameters.CustomDataCount) { const uint Float4Offset = Parameters.CustomDataOffset + (FloatIndex >> 2u); const float4 Float4Packed = LoadInstancePayloadDataElement(Float4Offset); return Float4Packed[FloatIndex & 0x3u]; } #endif #elif USE_INSTANCING && USES_PER_INSTANCE_CUSTOM_DATA const uint FloatIndex = uint(Index); BRANCH if (FloatIndex < InstanceVF.NumCustomDataFloats) { const uint InstanceDataIndex = Parameters.InstanceId + Parameters.InstanceOffset; const uint BufferStartIndex = InstanceDataIndex * InstanceVF.NumCustomDataFloats; return InstanceVF.InstanceCustomDataBuffer[BufferStartIndex + FloatIndex]; } #endif return DefaultValue; } /** Per Instance Custom Data Getter (Pixel Shader Variant - Visibility Buffer) */ /** Get the per-instance custom data when instancing */ MaterialFloat3 GetPerInstanceCustomData3Vector(FMaterialPixelParameters Parameters, int Index, MaterialFloat3 DefaultValue) { #if VF_USE_PRIMITIVE_SCENE_DATA && USES_PER_INSTANCE_CUSTOM_DATA return float3(GetPerInstanceCustomData(Parameters, Index + 0, DefaultValue.x), GetPerInstanceCustomData(Parameters, Index + 1, DefaultValue.y), GetPerInstanceCustomData(Parameters, Index + 2, DefaultValue.z)); #elif USE_INSTANCING && USES_PER_INSTANCE_CUSTOM_DATA const uint FloatIndex = uint(Index); BRANCH if (FloatIndex + 2 < InstanceVF.NumCustomDataFloats) { const uint InstanceDataIndex = asuint(Parameters.PerInstanceParams.w); const uint BufferStartIndex = InstanceDataIndex * InstanceVF.NumCustomDataFloats; return float3(InstanceVF.InstanceCustomDataBuffer[BufferStartIndex + FloatIndex], InstanceVF.InstanceCustomDataBuffer[BufferStartIndex + FloatIndex + 1], InstanceVF.InstanceCustomDataBuffer[BufferStartIndex + FloatIndex + 2]); } #endif return DefaultValue; } // Per Instance Custom Data Getter (Vertex Shader Only) /** Get the per-instance custom data when instancing */ MaterialFloat3 GetPerInstanceCustomData3Vector(FMaterialVertexParameters Parameters, int Index, MaterialFloat3 DefaultValue) { #if VF_USE_PRIMITIVE_SCENE_DATA && USES_PER_INSTANCE_CUSTOM_DATA return float3(GetPerInstanceCustomData(Parameters, Index, DefaultValue.x), GetPerInstanceCustomData(Parameters, Index + 1, DefaultValue.y), GetPerInstanceCustomData(Parameters, Index + 2, DefaultValue.z)); #elif USE_INSTANCING && USES_PER_INSTANCE_CUSTOM_DATA const uint FloatIndex = uint(Index); BRANCH if (FloatIndex + 2 < InstanceVF.NumCustomDataFloats) { const uint InstanceDataIndex = Parameters.InstanceId + Parameters.InstanceOffset; const uint BufferStartIndex = InstanceDataIndex * InstanceVF.NumCustomDataFloats; return float3(InstanceVF.InstanceCustomDataBuffer[BufferStartIndex + FloatIndex], InstanceVF.InstanceCustomDataBuffer[BufferStartIndex + FloatIndex + 1], InstanceVF.InstanceCustomDataBuffer[BufferStartIndex + FloatIndex + 2]); } #endif return DefaultValue; } /** 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); } /** Local to world */ FDFMatrix GetLocalToWorldDF(FMaterialVertexParameters Parameters) { #if DECAL_PRIMITIVE return MakeDFMatrix(DecalPositionHigh.xyz, DecalToWorld); #elif USE_INSTANCING || USE_INSTANCE_CULLING || IS_MESHPARTICLE_FACTORY return Parameters.InstanceLocalToWorld; #else return GetPrimitiveData(Parameters).LocalToWorld; #endif } FWSMatrix GetLocalToWorld(FMaterialVertexParameters Parameters) { return Parameters.LWCData.LocalToWorld; } FDFMatrix GetLocalToWorldDF(FMaterialPixelParameters Parameters) { #if DECAL_PRIMITIVE return MakeDFMatrix(DecalPositionHigh.xyz, DecalToWorld); #else return GetPrimitiveData(Parameters).LocalToWorld; #endif } FWSMatrix GetLocalToWorld(FMaterialPixelParameters Parameters) { return Parameters.LWCData.LocalToWorld; } FDFMatrix GetPrevLocalToWorldDF(FMaterialVertexParameters Parameters) { #if USE_INSTANCING || USE_INSTANCE_CULLING || IS_MESHPARTICLE_FACTORY return Parameters.PrevFrameLocalToWorld; #else return GetPrimitiveData(Parameters).PreviousLocalToWorld; #endif } FWSMatrix GetPrevLocalToWorld(FMaterialVertexParameters Parameters) { return Parameters.LWCData.PreviousLocalToWorld; } FDFMatrix GetPrevLocalToWorldDF(FMaterialPixelParameters Parameters) { return GetPrimitiveData(Parameters).PreviousLocalToWorld; } FWSMatrix GetPrevLocalToWorld(FMaterialPixelParameters Parameters) { return Parameters.LWCData.PreviousLocalToWorld; } /** World to local*/ FDFInverseMatrix GetWorldToLocalDF(FMaterialVertexParameters Parameters) { return GetPrimitiveData(Parameters).WorldToLocal; } FWSInverseMatrix GetWorldToLocal(FMaterialVertexParameters Parameters) { return Parameters.LWCData.WorldToLocal; } FDFInverseMatrix GetWorldToLocalDF(FMaterialPixelParameters Parameters) { return GetPrimitiveData(Parameters).WorldToLocal; } FWSInverseMatrix GetWorldToLocal(FMaterialPixelParameters Parameters) { return Parameters.LWCData.WorldToLocal; } FDFInverseMatrix GetPrevWorldToLocalDF(FMaterialVertexParameters Parameters) { return GetPrimitiveData(Parameters).PreviousWorldToLocal; } FWSInverseMatrix GetPrevWorldToLocal(FMaterialVertexParameters Parameters) { return Parameters.LWCData.PreviousWorldToLocal; } FDFInverseMatrix GetPrevWorldToLocalDF(FMaterialPixelParameters Parameters) { return GetPrimitiveData(Parameters).PreviousWorldToLocal; } FWSInverseMatrix GetPrevWorldToLocal(FMaterialPixelParameters Parameters) { return Parameters.LWCData.PreviousWorldToLocal; } /** Transforms a vector from local space to world space (PS version) */ MaterialFloat3 TransformLocalVectorToWorld(FMaterialPixelParameters Parameters, MaterialFloat3 InLocalVector) { return mul(InLocalVector, GetLocalToWorld3x3(Parameters)); } /** Transforms a vector from local space to world space (VS version) */ MaterialFloat3 TransformLocalVectorToWorld(FMaterialVertexParameters Parameters,MaterialFloat3 InLocalVector) { #if USE_INSTANCING || USE_INSTANCE_CULLING || IS_MESHPARTICLE_FACTORY return DFMultiplyVector(InLocalVector, Parameters.InstanceLocalToWorld); #else return mul(InLocalVector, GetLocalToWorld3x3(Parameters)); #endif } /** Transforms a vector from local space to previous frame world space (VS version) */ MaterialFloat3 TransformLocalVectorToPrevWorld(FMaterialVertexParameters Parameters, MaterialFloat3 InLocalVector) { #if USE_INSTANCING || USE_INSTANCE_CULLING || IS_MESHPARTICLE_FACTORY return DFMultiplyVector(InLocalVector, Parameters.PrevFrameLocalToWorld); #else return mul(InLocalVector, GetPreviousLocalToWorld3x3(Parameters)); #endif } /** Transforms a position from local space to absolute world space */ FWSVector3 TransformLocalPositionToWorld(FMaterialPixelParameters Parameters,float3 InLocalPosition) { return WSMultiply(InLocalPosition, GetLocalToWorld(Parameters)); } /** Transforms a position from local space to absolute world space */ FWSVector3 TransformLocalPositionToWorld(FMaterialVertexParameters Parameters,float3 InLocalPosition) { return WSMultiply(InLocalPosition, GetLocalToWorld(Parameters)); } /** Transforms a position from local space to previous frame absolute world space */ FWSVector3 TransformLocalPositionToPrevWorld(FMaterialVertexParameters Parameters,float3 InLocalPosition) { return WSMultiply(InLocalPosition, GetPrevLocalToWorld(Parameters)); } #if MATERIAL_DOMAIN_LIGHTFUNCTION && !LIGHT_ATLAS #ifndef CameraRelativeLightPosition float3 CameraRelativeLightPosition; #endif #endif /** Return the object's position in world space */ FWSVector3 GetObjectWorldPosition(FMaterialPixelParameters Parameters) { #if HAS_PRIMITIVE_UNIFORM_BUFFER || DECAL_PRIMITIVE return Parameters.LWCData.ObjectWorldPosition; #elif MATERIAL_DOMAIN_LIGHTFUNCTION && !LIGHT_ATLAS /** * Light functions return light position in the Pixel shader only. * Light Atlas cannot return positional values, so default to float3(0.0) */ return DFToWS(DFFastSubtract(CameraRelativeLightPosition, PrimaryView.PreViewTranslation)); #else return WSPromote(float3(0.0f, 0.0f, 0.0f)); #endif //HAS_PRIMITIVE_UNIFORM_BUFFER } /** Return the object's position in world space. For instanced meshes, this returns the instance position. */ FWSVector3 GetObjectWorldPosition(FMaterialVertexParameters Parameters) { return Parameters.LWCData.ObjectWorldPosition; } float3 GetObjectTranslatedWorldPosition(FMaterialPixelParameters Parameters) { #if HAS_PRIMITIVE_UNIFORM_BUFFER || DECAL_PRIMITIVE #if PRIMITIVE_HAS_TILEOFFSET_DATA && VIEW_HAS_TILEOFFSET_DATA return LWCToFloat(LWCAdd(GetPrimitiveData(Parameters).ObjectWorldPositionTO, ResolvedView.TileOffset.PreViewTranslation)); #else return DFFastToTranslatedWorld(GetPrimitiveData(Parameters).ObjectWorldPosition, ResolvedView.PreViewTranslation); #endif //PRIMITIVE_HAS_TILEOFFSET_DATA && VIEW_HAS_TILEOFFSET_DATA #elif MATERIAL_DOMAIN_LIGHTFUNCTION && !LIGHT_ATLAS /** * Light functions return light position in the Pixel shader only. * Light Atlas cannot return positional values, so default to float3(0.0) */ return CameraRelativeLightPosition; #else return float3(0.0f, 0.0f, 0.0f); #endif //HAS_PRIMITIVE_UNIFORM_BUFFER } float3 GetObjectTranslatedWorldPosition(FMaterialVertexParameters Parameters) { #if PRIMITIVE_HAS_TILEOFFSET_DATA && VIEW_HAS_TILEOFFSET_DATA return LWCToFloat(LWCAdd(GetPrimitiveData(Parameters).ObjectWorldPositionTO, ResolvedView.TileOffset.PreViewTranslation)); #else return DFFastToTranslatedWorld(GetPrimitiveData(Parameters).ObjectWorldPosition, ResolvedView.PreViewTranslation); #endif } FWSInverseMatrix GetWorldToParticle(FMaterialPixelParameters Parameters) { return Parameters.LWCData.WorldToParticle; } FWSInverseMatrix GetWorldToParticle(FMaterialVertexParameters Parameters) { return Parameters.LWCData.WorldToParticle; } FWSMatrix GetParticleToWorld(FMaterialPixelParameters Parameters) { return Parameters.LWCData.ParticleToWorld; } FWSMatrix GetParticleToWorld(FMaterialVertexParameters Parameters) { return Parameters.LWCData.ParticleToWorld; } FMaterialLWCData MakeMaterialLWCData(FMaterialVertexParameters Parameters) { FMaterialLWCData Result = (FMaterialLWCData)0; #if VIEW_HAS_TILEOFFSET_DATA && WSVECTOR_IS_TILEOFFSET Result.PreViewTranslation = ResolvedView.TileOffset.PreViewTranslation; Result.PrevPreViewTranslation = ResolvedView.TileOffset.PrevPreViewTranslation; Result.WorldViewOrigin = ResolvedView.TileOffset.WorldViewOrigin; Result.PrevWorldViewOrigin = ResolvedView.TileOffset.PrevWorldViewOrigin; Result.WorldCameraOrigin = ResolvedView.TileOffset.WorldCameraOrigin; Result.PrevWorldCameraOrigin = ResolvedView.TileOffset.PrevWorldCameraOrigin; #else Result.PreViewTranslation = DFToWS(ResolvedView.PreViewTranslation); Result.PrevPreViewTranslation = DFToWS(ResolvedView.PrevPreViewTranslation); Result.WorldViewOrigin = DFToWS(ResolvedView.WorldViewOrigin); Result.PrevWorldViewOrigin = DFToWS(ResolvedView.PrevWorldViewOrigin); Result.WorldCameraOrigin = DFToWS(ResolvedView.WorldCameraOrigin); Result.PrevWorldCameraOrigin = DFToWS(ResolvedView.PrevWorldCameraOrigin); #endif Result.AbsoluteWorldPosition = WSSubtract(Parameters.WorldPosition, Result.PreViewTranslation); Result.InstanceToWorld = DFToWS(GetInstanceToWorldDF(Parameters)); Result.WorldToInstance = DFToWS(GetWorldToInstanceDF(Parameters)); Result.PreviousInstanceToWorld = DFToWS(GetPrevInstanceToWorldDF(Parameters)); Result.LocalToWorld = DFToWS(GetLocalToWorldDF(Parameters)); Result.WorldToLocal = DFToWS(GetWorldToLocalDF(Parameters)); Result.PreviousLocalToWorld = DFToWS(GetPrevLocalToWorldDF(Parameters)); Result.PreviousWorldToLocal = DFToWS(GetPrevWorldToLocalDF(Parameters)); #if DECAL_PRIMITIVE /* * Deferred decal don't have a Primitive uniform buffer, because we don't know on which primitive the decal * is being projected to. But the user may still need to get the decal's actor world position. * 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. */ Result.ObjectWorldPosition = DFToWS(MakeDFVector3(DecalPositionHigh.xyz, DecalToWorld[3].xyz)); Result.ActorWorldPosition = Result.ObjectWorldPosition; #else //DECAL_PRIMITIVE #if USE_INSTANCING || USE_INSTANCE_CULLING || IS_MESHPARTICLE_FACTORY #if MATERIAL_LWC_ENABLED Result.ObjectWorldPosition = WSGetOrigin(DFToWS(Parameters.InstanceLocalToWorld)); #else //MATERIAL_LWC_ENABLED Result.ObjectWorldPosition = WSPromote(Parameters.InstanceLocalToWorld.PostTranslation + Parameters.InstanceLocalToWorld.M[3].xyz); #endif //MATERIAL_LWC_ENABLED #elif PRIMITIVE_HAS_TILEOFFSET_DATA && WSVECTOR_IS_TILEOFFSET Result.ObjectWorldPosition = GetPrimitiveData(Parameters).ObjectWorldPositionTO; #else Result.ObjectWorldPosition = DFToWS(GetPrimitiveData(Parameters).ObjectWorldPosition); #endif #if PRIMITIVE_HAS_TILEOFFSET_DATA && WSVECTOR_IS_TILEOFFSET Result.ActorWorldPosition = GetPrimitiveData(Parameters).ActorWorldPositionTO; #else Result.ActorWorldPosition = DFToWS(GetPrimitiveData(Parameters).ActorWorldPosition); #endif #endif //DECAL_PRIMITIVE Result.ParticleToWorld = DFFastToWS(Parameters.Particle.ParticleToWorld); Result.WorldToParticle = DFFastToWS(Parameters.Particle.WorldToParticle); Result.ParticleWorldPosition = WSSubtract(Parameters.Particle.TranslatedWorldPositionAndSize.xyz, Result.PreViewTranslation); Result.PrevParticleWorldPosition = WSSubtract(Parameters.Particle.PrevTranslatedWorldPositionAndSize.xyz, Result.PrevPreViewTranslation); return Result; } FMaterialLWCData MakeMaterialLWCData(FMaterialPixelParameters Parameters) { FMaterialLWCData Result = (FMaterialLWCData)0; #if VIEW_HAS_TILEOFFSET_DATA && WSVECTOR_IS_TILEOFFSET Result.PreViewTranslation = ResolvedView.TileOffset.PreViewTranslation; Result.PrevPreViewTranslation = ResolvedView.TileOffset.PrevPreViewTranslation; Result.WorldViewOrigin = ResolvedView.TileOffset.WorldViewOrigin; Result.PrevWorldViewOrigin = ResolvedView.TileOffset.PrevWorldViewOrigin; Result.WorldCameraOrigin = ResolvedView.TileOffset.WorldCameraOrigin; Result.PrevWorldCameraOrigin = ResolvedView.TileOffset.PrevWorldCameraOrigin; #else Result.PreViewTranslation = DFToWS(ResolvedView.PreViewTranslation); Result.PrevPreViewTranslation = DFToWS(ResolvedView.PrevPreViewTranslation); Result.WorldViewOrigin = DFToWS(ResolvedView.WorldViewOrigin); Result.PrevWorldViewOrigin = DFToWS(ResolvedView.PrevWorldViewOrigin); Result.WorldCameraOrigin = DFToWS(ResolvedView.WorldCameraOrigin); Result.PrevWorldCameraOrigin = DFToWS(ResolvedView.PrevWorldCameraOrigin); #endif Result.AbsoluteWorldPosition = WSSubtract(Parameters.WorldPosition_CamRelative, Result.PreViewTranslation); Result.WorldPosition_NoOffsets = WSSubtract(Parameters.WorldPosition_NoOffsets_CamRelative, Result.PreViewTranslation); Result.InstanceToWorld = DFToWS(GetInstanceToWorldDF(Parameters)); Result.WorldToInstance = DFFastToWS(GetWorldToInstanceDF(Parameters)); Result.PreviousInstanceToWorld = DFToWS(GetPrevInstanceToWorldDF(Parameters)); Result.LocalToWorld = DFToWS(GetLocalToWorldDF(Parameters)); Result.WorldToLocal = DFFastToWS(GetWorldToLocalDF(Parameters)); Result.PreviousLocalToWorld = DFToWS(GetPrevLocalToWorldDF(Parameters)); Result.PreviousWorldToLocal = DFFastToWS(GetPrevWorldToLocalDF(Parameters)); #if DECAL_PRIMITIVE /* * Deferred decal don't have a Primitive uniform buffer, because we don't know on which primitive the decal * is being projected to. But the user may still need to get the decal's actor world position. * 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. */ Result.ObjectWorldPosition = DFToWS(MakeDFVector3(DecalPositionHigh.xyz, DecalToWorld[3].xyz)); Result.ActorWorldPosition = Result.ObjectWorldPosition; #else //DECAL_PRIMITIVE #if PRIMITIVE_HAS_TILEOFFSET_DATA && WSVECTOR_IS_TILEOFFSET Result.ObjectWorldPosition = GetPrimitiveData(Parameters).ObjectWorldPositionTO; Result.ActorWorldPosition = GetPrimitiveData(Parameters).ActorWorldPositionTO; #else Result.ObjectWorldPosition = DFToWS(GetPrimitiveData(Parameters).ObjectWorldPosition); Result.ActorWorldPosition = DFToWS(GetPrimitiveData(Parameters).ActorWorldPosition); #endif #endif //DECAL_PRIMITIVE Result.ParticleToWorld = DFFastToWS(Parameters.Particle.ParticleToWorld); Result.WorldToParticle = DFFastToWS(Parameters.Particle.WorldToParticle); Result.ParticleWorldPosition = WSSubtract(Parameters.Particle.TranslatedWorldPositionAndSize.xyz, Result.PreViewTranslation); Result.PrevParticleWorldPosition = WSSubtract(Parameters.Particle.PrevTranslatedWorldPositionAndSize.xyz, Result.PrevPreViewTranslation); return Result; } /** Get the per-instance random value when instancing */ float GetPerInstanceRandom(FMaterialVertexParameters Parameters) { return Parameters.PerInstanceRandom; } /** Get the per-instance random value when instancing */ float GetPerInstanceRandom(FMaterialPixelParameters Parameters) { return Parameters.PerInstanceRandom; } /** Get the per-instance fade-out amount when instancing */ float GetPerInstanceFadeAmount(FMaterialPixelParameters Parameters) { #if USE_INSTANCING || USE_INSTANCE_CULLING return float(Parameters.PerInstanceParams.x); #else return float(1.0); #endif } /** Get the per-instance fade-out amount when instancing */ float GetPerInstanceFadeAmount(FMaterialVertexParameters Parameters) { #if USE_INSTANCING || USE_INSTANCE_CULLING return float(Parameters.PerInstanceParams.x); #else return float(1.0); #endif } MaterialFloat GetDistanceCullFade() { #if PIXELSHADER && !IS_NANITE_PASS // Nanite doesn't bind this UB return saturate(ResolvedView.RealTime * PrimitiveFade.FadeTimeScaleBias.x + PrimitiveFade.FadeTimeScaleBias.y); #else return 1.0f; #endif } MaterialFloat4 GetFontSignedDistanceData(FMaterialPixelParameters Parameters) { #if SHADER_TYPE == ST_GrayscaleFont || SHADER_TYPE == ST_ColorFont || SHADER_TYPE == ST_SdfFont || SHADER_TYPE == ST_MsdfFont return Parameters.FontSignedDistanceData; #else return float4(0.0f, 0.0f, 0.0f, 0.0f); #endif } /** Rotates Position about the given axis by the given angle, in radians, and returns the offset to Position. */ 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; } /** * Rotates Position about the given axis by the given angle, in radians, and returns the offset to Position. * Note that this returns an *offset*, so even though inputs are in LWC-space, the offset is returned as a regular float */ float3 RotateAboutAxis(float4 NormalizedRotationAxisAndAngle, FWSVector3 PositionOnAxis, FWSVector3 Position) { // Project Position onto the rotation axis and find the closest point on the axis to Position FWSVector3 ClosestPointOnAxis = WSAdd(PositionOnAxis, NormalizedRotationAxisAndAngle.xyz * dot(NormalizedRotationAxisAndAngle.xyz, WSSubtractDemote(Position, PositionOnAxis))); // Construct orthogonal axes in the plane of the rotation float3 UAxis = WSSubtractDemote(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; // Here we want to compute the following values: // FLWCVector3 RotatedPosition = LWCAdd(ClosestPointOnAxis, R); // return LWCSubtract(RotatedPosition, Position); // This can logically be written like this: // return ClosestPointOnAxis + R - Position // Notice that UAxis is already defined as (Position - ClosestPointOnAxis) // So we can simply this as R - UAxis, to avoid some conversions to/from LWC return R - UAxis; } float ViewScalarBlueNoise(uint2 ScreenCoord, uint FrameIndex) { uint3 WrappedCoordinate = uint3(ScreenCoord, FrameIndex) & View.BlueNoiseModuloMasks; uint3 TextureCoordinate = uint3(WrappedCoordinate.x, WrappedCoordinate.z * View.BlueNoiseDimensions.y + WrappedCoordinate.y, 0); return View.BlueNoiseScalarTexture.Load(TextureCoordinate, 0).x; } // 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 ) { // Technically this approximation to the Planckian locus is only valid within the 1000-15000 temperature range, // but we need to support temperatures outside this range too; especially the range between 0 and 1000. 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; // The XYZtoRGB transform can result in negative values, so we need to clamp here. return max( 0.0f, XYZ_2_LinearRGB( float3( X, Y, Z ) ) ) * pow( 0.0004 * Temp, 4 ); } float2 MaterialExpressionGetHairRootUV(FMaterialPixelParameters Parameters) { #if HAIR_STRAND_MESH_FACTORY return GetHairStrandsRootUV(Parameters.HairControlPointId); #elif HAIR_CARD_MESH_FACTORY return GetHairCardsRootUV(Parameters.HairPrimitiveUV, Parameters.HairPrimitiveRootUV); #else return float2(0, 0); #endif } float2 MaterialExpressionGetHairUV(FMaterialPixelParameters Parameters) { #if HAIR_STRAND_MESH_FACTORY return GetHairStrandsUV(Parameters.HairControlPointId, Parameters.HairPrimitiveUV); #elif HAIR_CARD_MESH_FACTORY return GetHairCardsUV(Parameters.HairPrimitiveUV); #else return float2(0,0); #endif } float2 MaterialExpressionGetHairDimensions(FMaterialPixelParameters Parameters) { #if HAIR_STRAND_MESH_FACTORY return GetHairStrandsDimensions(Parameters.HairControlPointId, Parameters.HairPrimitiveUV.x); #elif HAIR_CARD_MESH_FACTORY return GetHairCardsDimensions(Parameters.HairPrimitiveUV, Parameters.HairPrimitiveLength); #else return float2(0, 0); #endif } float MaterialExpressionGetHairSeed(FMaterialPixelParameters Parameters) { #if HAIR_STRAND_MESH_FACTORY return GetHairStrandsSeed(Parameters.HairControlPointId); #elif HAIR_CARD_MESH_FACTORY return GetHairCardsSeed(Parameters.HairPrimitiveUV); #else return 0; #endif } float3 MaterialExpressionGetHairClumpID(FMaterialPixelParameters Parameters) { #if HAIR_STRAND_MESH_FACTORY return GetHairStrandsClumpID(Parameters.HairControlPointId); #elif HAIR_CARD_MESH_FACTORY return GetHairCardsClumpID(Parameters.HairPrimitiveUV); #else return 0; #endif } float3 MaterialExpressionGetHairBaseColor(FMaterialPixelParameters Parameters) { #if HAIR_STRAND_MESH_FACTORY return GetHairStrandsColor(Parameters.HairControlPointId, Parameters.HairPrimitiveUV.x); #elif HAIR_CARD_MESH_FACTORY return GetHairCardsColor(Parameters.HairPrimitiveUV, Parameters.HairPrimitiveMaterial.xyz); #else return float3(0,0,0); #endif } float MaterialExpressionGetHairRoughness(FMaterialPixelParameters Parameters) { #if HAIR_STRAND_MESH_FACTORY return GetHairStrandsRoughness(Parameters.HairControlPointId, Parameters.HairPrimitiveUV.x); #elif HAIR_CARD_MESH_FACTORY return GetHairCardsRoughness(Parameters.HairPrimitiveUV, Parameters.HairPrimitiveMaterial.w); #else return 0; #endif } float MaterialExpressionGetHairAO(FMaterialPixelParameters Parameters) { #if HAIR_STRAND_MESH_FACTORY return GetHairStrandsAO(Parameters.HairControlPointId, Parameters.HairPrimitiveUV.x); #elif HAIR_CARD_MESH_FACTORY return GetHairCardsAO(Parameters.HairPrimitiveUV, 1.0f); #else return 0; #endif } float MaterialExpressionGetHairDepth(FMaterialVertexParameters Parameters) { return 0; } float MaterialExpressionGetHairDepth(FMaterialPixelParameters Parameters) { #if HAIR_STRAND_MESH_FACTORY return GetHairStrandsDepth(); #elif HAIR_CARD_MESH_FACTORY return GetHairCardsDepth(Parameters.HairPrimitiveUV, Parameters.SvPosition.z); #else return 0; #endif } float MaterialExpressionGetHairCoverage(FMaterialPixelParameters Parameters) { #if HAIR_STRAND_MESH_FACTORY return GetHairStrandsCoverage(); #elif HAIR_CARD_MESH_FACTORY return GetHairCardsCoverage(Parameters.HairPrimitiveUV); #else return 0; #endif } float3 MaterialExpressionGetHairTangent(FMaterialPixelParameters Parameters, bool bUseTangentSpace) { #if HAIR_STRAND_MESH_FACTORY return bUseTangentSpace ? float3(0,1,0) : Parameters.TangentToWorld[2]; #elif HAIR_CARD_MESH_FACTORY return GetHairCardsTangent(Parameters.HairPrimitiveUV, Parameters.TangentToWorld, bUseTangentSpace); #else return 0; #endif } float2 MaterialExpressionGetAtlasUVs(FMaterialPixelParameters Parameters) { #if HAIR_STRAND_MESH_FACTORY return float2(0,0); #elif HAIR_CARD_MESH_FACTORY return Parameters.HairPrimitiveUV; #else return 0; #endif } float4 MaterialExpressionGetHairAuxilaryData(FMaterialPixelParameters Parameters) { #if HAIR_CARD_MESH_FACTORY return GetHairCardsAuxilaryData(Parameters.HairPrimitiveUV); #else return 0; #endif } float MaterialExpressionGetHairGroupIndex(FMaterialPixelParameters Parameters) { #if HAIR_STRAND_MESH_FACTORY return GetHairStrandsGroupIndex(); #elif HAIR_CARD_MESH_FACTORY return GetHairCardsGroupIndex(Parameters.HairPrimitiveUV, Parameters.HairPrimitiveGroupIndex); #else return 0; #endif } float3 MaterialExpressionGetHairColorFromMelanin(float Melanin, float Redness, float3 DyeColor) { return GetHairColorFromMelanin(Melanin, Redness, DyeColor); } #define DEFINE_ATMOSPHERELIGHTVECTOR(MaterialParamType) float3 MaterialExpressionAtmosphericLightVector(MaterialParamType Parameters) {return ResolvedView.AtmosphereLightDirection[0].xyz;} DEFINE_ATMOSPHERELIGHTVECTOR(FMaterialPixelParameters) DEFINE_ATMOSPHERELIGHTVECTOR(FMaterialVertexParameters) float3 MaterialExpressionSkyAtmosphereLightIlluminanceOnGround(FMaterialPixelParameters Parameters, uint LightIndex) { #if PROJECT_SUPPORT_SKY_ATMOSPHERE // can be used anywhere, not only in sky material with MATERIAL_SKY_ATMOSPHERE return ResolvedView.AtmosphereLightIlluminanceOnGroundPostTransmittance[LightIndex].rgb; #else return float3(0.0f, 0.0f, 0.0f); #endif } float3 MaterialExpressionAtmosphericLightColor(FMaterialPixelParameters Parameters) // Deprecated but back compatible. { return MaterialExpressionSkyAtmosphereLightIlluminanceOnGround(Parameters, 0); } float3 MaterialExpressionSkyAtmosphereLightIlluminance(FMaterialPixelParameters Parameters, float3 TranslatedWorldPosition, uint LightIndex) { #if MATERIAL_SKY_ATMOSPHERE && PROJECT_SUPPORT_SKY_ATMOSPHERE const float3 PlanetCenterToTranslatedWorldPos = (TranslatedWorldPosition - ResolvedView.SkyPlanetTranslatedWorldCenterAndViewHeight.xyz) * CM_TO_SKY_UNIT; // GetAtmosphereTransmittance does a shadow test against the virtual planet. const float3 TransmittanceToLight = GetAtmosphereTransmittance( PlanetCenterToTranslatedWorldPos, ResolvedView.AtmosphereLightDirection[LightIndex].xyz, ResolvedView.SkyAtmosphereBottomRadiusKm, ResolvedView.SkyAtmosphereTopRadiusKm, View.TransmittanceLutTexture, View.TransmittanceLutTextureSampler); return ResolvedView.AtmosphereLightIlluminanceOuterSpace[LightIndex].rgb * TransmittanceToLight; #else return float3(0.0f, 0.0f, 0.0f); #endif } float3 MaterialExpressionSkyAtmosphereLightIlluminance(FMaterialPixelParameters Parameters, FWSVector3 WorldPosition, uint LightIndex) { const float3 TranslatedWorldPosition = DFFastToTranslatedWorld(WSToDF(WorldPosition), ResolvedView.PreViewTranslation); return MaterialExpressionSkyAtmosphereLightIlluminance(Parameters, TranslatedWorldPosition, LightIndex); } #if MATERIAL_SKY_ATMOSPHERE && PROJECT_SUPPORT_SKY_ATMOSPHERE #define DEFINE_SKYATMLIGHTDIRECTION(MaterialParamType) float3 MaterialExpressionSkyAtmosphereLightDirection(MaterialParamType Parameters, uint LightIndex) {return ResolvedView.AtmosphereLightDirection[LightIndex].xyz;} #else #define DEFINE_SKYATMLIGHTDIRECTION(MaterialParamType) float3 MaterialExpressionSkyAtmosphereLightDirection(MaterialParamType Parameters, uint LightIndex) {return float3(0.0f, 0.0f, 0.0f);} #endif DEFINE_SKYATMLIGHTDIRECTION(FMaterialPixelParameters) DEFINE_SKYATMLIGHTDIRECTION(FMaterialVertexParameters) float3 MaterialExpressionSkyAtmosphereLightDiskLuminance(FMaterialPixelParameters Parameters, uint LightIndex, float OverrideAtmosphereLightDiscCosHalfApexAngle) { float3 LightDiskLuminance = float3(0.0f, 0.0f, 0.0f); #if MATERIAL_SKY_ATMOSPHERE && PROJECT_SUPPORT_SKY_ATMOSPHERE if (ResolvedView.RenderingReflectionCaptureMask == 0.0f) // Do not render light disk when in reflection capture in order to avoid double specular. The sun contribution is already computed analyticaly. { const float3 ViewDir = -Parameters.CameraVector; float3 PlanetCenterToCameraTranslatedWorld = (ResolvedView.SkyCameraTranslatedWorldOrigin - ResolvedView.SkyPlanetTranslatedWorldCenterAndViewHeight.xyz); if (IsOrthoProjection()) { PlanetCenterToCameraTranslatedWorld = GetTranslatedWorldCameraPosFromView(Parameters.SvPosition.xy, true) + PlanetCenterToCameraTranslatedWorld; } PlanetCenterToCameraTranslatedWorld *= CM_TO_SKY_UNIT; // GetLightDiskLuminance does a test against the virtual planet but SkyCameraTranslatedWorldOrigin is always put safely setup above it (to never have the camera into the virtual planet with a black screen) LightDiskLuminance = GetLightDiskLuminance(PlanetCenterToCameraTranslatedWorld, ViewDir, ResolvedView.SkyAtmosphereBottomRadiusKm, ResolvedView.SkyAtmosphereTopRadiusKm, View.TransmittanceLutTexture, View.TransmittanceLutTextureSampler, ResolvedView.AtmosphereLightDirection[LightIndex].xyz, OverrideAtmosphereLightDiscCosHalfApexAngle >= 0.0f ? OverrideAtmosphereLightDiscCosHalfApexAngle : ResolvedView.AtmosphereLightDiscCosHalfApexAngle_PPTrans[LightIndex].x, ResolvedView.AtmosphereLightDiscLuminance[LightIndex].xyz); } #endif return LightDiskLuminance; } float3 MaterialExpressionSkyAtmosphereViewLuminance(FMaterialPixelParameters Parameters, float3 ViewDir) { #if MATERIAL_SKY_ATMOSPHERE && PROJECT_SUPPORT_SKY_ATMOSPHERE if (ResolvedView.RenderingReflectionCaptureMask == 0.0f && !IsSkyAtmosphereRenderedInMain(ResolvedView.EnvironmentComponentsFlags)) { return float3(0.0f, 0.0f, 0.0f); } // The referencial used to build the Sky View lut float3x3 LocalReferencial = GetSkyViewLutReferential(ResolvedView.SkyViewLutReferential); float ViewHeight = ResolvedView.SkyPlanetTranslatedWorldCenterAndViewHeight.w; if(IsOrthoProjection()) { float3 CameraPosition = GetTranslatedWorldCameraPosFromView(Parameters.SvPosition.xy, true); CameraPosition.z += ViewHeight; ViewHeight = mul(LocalReferencial, CameraPosition).z; } ViewHeight *= CM_TO_SKY_UNIT; // Compute inputs in this referential float3 WorldPosLocal = float3(0.0, 0.0, ViewHeight); float3 UpVectorLocal = float3(0.0, 0.0, 1.0); float3 WorldDirLocal = mul(LocalReferencial, ViewDir); float ViewZenithCosAngle = dot(WorldDirLocal, UpVectorLocal); float2 Sol = RayIntersectSphere(WorldPosLocal, WorldDirLocal, float4(0.0f, 0.0f, 0.0f, ResolvedView.SkyAtmosphereBottomRadiusKm)); const bool IntersectGround = any(Sol > 0.0f); float2 SkyViewLutUv; SkyViewLutParamsToUv(IntersectGround, ViewZenithCosAngle, WorldDirLocal, ViewHeight, ResolvedView.SkyAtmosphereBottomRadiusKm, ResolvedView.SkyViewLutSizeAndInvSize, SkyViewLutUv); float3 SkyAtmosphereViewLuminance = Texture2DSampleLevel(View.SkyViewLutTexture, View.SkyViewLutTextureSampler, SkyViewLutUv, 0.0f).rgb; SkyAtmosphereViewLuminance *= ResolvedView.SkyAtmosphereSkyLuminanceFactor.rgb; SkyAtmosphereViewLuminance *= ResolvedView.OneOverPreExposure; if (ResolvedView.RealTimeReflectionCapture == 0.0f) { SkyAtmosphereViewLuminance.rgb = QuantizeFloatColor(SkyAtmosphereViewLuminance.rgb, ResolvedView.SceneColorTextureFormatQuantizationError, Parameters.SvPosition.xy, QUANTIZE_NOISE_INTERLEAVEDGRADIENT); } #if SUPPORT_PRIMITIVE_ALPHA_HOLDOUT if (IsSkyAtmosphereHoldout(View.EnvironmentComponentsFlags) && View.bPrimitiveAlphaHoldoutEnabled) { SkyAtmosphereViewLuminance = 0.0f; // Disable sky dome luminance contribution. Throughput will be 0. } #endif return SkyAtmosphereViewLuminance; #else return float3(0.0f, 0.0f, 0.0f); #endif } float4 MaterialExpressionSkyAtmosphereAerialPerspective(FMaterialPixelParameters Parameters, float3 TranslatedWorldPosition) { #if MATERIAL_SKY_ATMOSPHERE && PROJECT_SUPPORT_SKY_ATMOSPHERE const float3 SkyTranslatedWorldPosition = TranslatedWorldPosition * CM_TO_SKY_UNIT; const float3 SkyCameraTranslatedWorldOrigin = ResolvedView.SkyCameraTranslatedWorldOrigin.xyz * CM_TO_SKY_UNIT; // NDCPosition is not computed using WorldPosition because it could result in position outside the frustum, // distorted uvs and bad visuals with artefact. Only the distance computation can actually be benefit from the surface position specified here. float4 NDCPosition = mul(float4(GetTranslatedWorldPosition(Parameters), 1.0f), ResolvedView.TranslatedWorldToClip); const float NearFadeOutRangeInvDepthKm = 1.0 / 0.00001f; // 1 centimeter fade region float4 AerialPerspective = GetAerialPerspectiveLuminanceTransmittance( ResolvedView.RealTimeReflectionCapture, ResolvedView.SkyAtmosphereCameraAerialPerspectiveVolumeSizeAndInvSize, NDCPosition, SkyTranslatedWorldPosition - SkyCameraTranslatedWorldOrigin, View.CameraAerialPerspectiveVolume, View.CameraAerialPerspectiveVolumeSampler, ResolvedView.SkyAtmosphereCameraAerialPerspectiveVolumeDepthResolutionInv, ResolvedView.SkyAtmosphereCameraAerialPerspectiveVolumeDepthResolution, ResolvedView.SkyAtmosphereAerialPerspectiveStartDepthKm, ResolvedView.SkyAtmosphereCameraAerialPerspectiveVolumeDepthSliceLengthKm, ResolvedView.SkyAtmosphereCameraAerialPerspectiveVolumeDepthSliceLengthKmInv, ResolvedView.OneOverPreExposure, NearFadeOutRangeInvDepthKm); #if SUPPORT_PRIMITIVE_ALPHA_HOLDOUT if (IsSkyAtmosphereHoldout(View.EnvironmentComponentsFlags) && View.bPrimitiveAlphaHoldoutEnabled) { AerialPerspective.rgb = 0.0f; } #endif return AerialPerspective; #else return float4(0.0f, 0.0f, 0.0f, 1.0f); // RGB= null scattering, A= null transmittance #endif } float4 MaterialExpressionSkyAtmosphereAerialPerspective(FMaterialPixelParameters Parameters, FWSVector3 WorldPosition) { const float3 TranslatedWorldPosition = WSAddDemote(WorldPosition, Parameters.LWCData.PreViewTranslation); return MaterialExpressionSkyAtmosphereAerialPerspective(Parameters, TranslatedWorldPosition); } float3 MaterialExpressionSkyAtmosphereDistantLightScatteredLuminance(FMaterialPixelParameters Parameters) { #if MATERIAL_SKY_ATMOSPHERE && PROJECT_SUPPORT_SKY_ATMOSPHERE return GetViewDistanceSkyLightColor(); #else return float3(0.0f, 0.0f, 0.0f); #endif } #if POST_PROCESS_MATERIAL uint bSceneDepthWithoutWaterTextureAvailable; Texture2D SceneDepthWithoutSingleLayerWaterTexture; SamplerState SceneDepthWithoutSingleLayerWaterSampler; float4 SceneWithoutSingleLayerWaterMinMaxUV; float2 SceneWithoutSingleLayerWaterTextureSize; float2 SceneWithoutSingleLayerWaterInvTextureSize; #endif /** Get the scene depth from the scene below the single layer water surface. This is only valid in the single layer water rendering pass or during post processing. Returns the chosen fallback depth if the material doesn't support reading back the correct depth. */ float MaterialExpressionSceneDepthWithoutWater(float2 ViewportUV, float FallbackDepth) { #if MATERIAL_SHADINGMODEL_SINGLELAYERWATER && !SCENE_TEXTURES_DISABLED && (SINGLE_LAYER_WATER_SHADING_QUALITY != SINGLE_LAYER_WATER_SHADING_QUALITY_MOBILE_WITH_DEPTH_BUFFER) const float2 ClampedViewportUV = clamp(ViewportUV, SingleLayerWater.SceneWithoutSingleLayerWaterMinMaxUV.xy, SingleLayerWater.SceneWithoutSingleLayerWaterMinMaxUV.zw); float WaterDeviceZ = WaterSampleSceneDepthWithoutWater(SingleLayerWater.SceneDepthWithoutSingleLayerWaterTexture, SingleLayerWaterSceneDepthSampler, ClampedViewportUV, SingleLayerWater.SceneWithoutSingleLayerWaterTextureSize, SingleLayerWater.SceneWithoutSingleLayerWaterInvTextureSize); return ConvertFromDeviceZ(WaterDeviceZ); #elif POST_PROCESS_MATERIAL if (bSceneDepthWithoutWaterTextureAvailable) { const float2 ClampedViewportUV = clamp(ViewportUV, SceneWithoutSingleLayerWaterMinMaxUV.xy, SceneWithoutSingleLayerWaterMinMaxUV.zw); float WaterDeviceZ = WaterSampleSceneDepthWithoutWater(SceneDepthWithoutSingleLayerWaterTexture, SceneDepthWithoutSingleLayerWaterSampler, ClampedViewportUV, SceneWithoutSingleLayerWaterTextureSize, SceneWithoutSingleLayerWaterInvTextureSize); return ConvertFromDeviceZ(WaterDeviceZ); } else { #if SHADING_PATH_DEFERRED return CalcSceneDepth(uint2(ViewportUV * View.BufferSizeAndInvSize.xy)); #elif SHADING_PATH_MOBILE return CalcSceneDepth(ViewportUV); #else return FallbackDepth; #endif } #else return FallbackDepth; #endif } float MaterialExpressionCloudSampleAltitude(FMaterialPixelParameters Parameters) { #if CLOUD_LAYER_PIXEL_SHADER return Parameters.CloudSampleAltitude; #else return 0.0f; #endif } float MaterialExpressionCloudSampleAltitudeInLayer(FMaterialPixelParameters Parameters) { #if CLOUD_LAYER_PIXEL_SHADER return Parameters.CloudSampleAltitudeInLayer; #else return 0.0f; #endif } float MaterialExpressionCloudSampleNormAltitudeInLayer(FMaterialPixelParameters Parameters) { #if CLOUD_LAYER_PIXEL_SHADER return Parameters.CloudSampleNormAltitudeInLayer; #else return 0.0f; #endif } float4 MaterialExpressionVolumeSampleConservativeDensity(FMaterialPixelParameters Parameters) { #if CLOUD_LAYER_PIXEL_SHADER return Parameters.VolumeSampleConservativeDensity; #else return float4(0.0f, 0.0f, 0.0f, 0.0f); #endif } float MaterialExpressionVolumeSampleShadowSampleDistance(FMaterialPixelParameters Parameters) { #if CLOUD_LAYER_PIXEL_SHADER return Parameters.ShadowSampleDistance; #else return 0.0f; #endif } float3 MaterialExpressionCloudEmptySpaceSkippingSphereCenterWorldPosition(FMaterialPixelParameters Parameters) { #if CLOUD_LAYER_PIXEL_SHADER return Parameters.CloudEmptySpaceSkippingSphereCenterWorldPosition; #else return 0.0f; #endif } float MaterialExpressionCloudEmptySpaceSkippingSphereRadius(FMaterialPixelParameters Parameters) { #if CLOUD_LAYER_PIXEL_SHADER return Parameters.CloudEmptySpaceSkippingSphereRadius; #else return 0.0f; #endif } // We use a forward declaration towards the real implementation of functions we need. This is always defined for base passes (opaque and translucent) #if defined(IS_BASE_PASS) && IS_MOBILE_BASE_PASS==0 float3 GetSkyLightReflectionSupportingBlend(float3 ReflectionVector, float Roughness, out float OutSkyAverageBrightness); #elif defined(IS_BASE_PASS) && IS_MOBILE_BASE_PASS==1 half3 GetMobileSkyLightReflection(half3 ReflectionVector, half Roughness, half CubemapMaxMip); #endif float3 MaterialExpressionSkyLightEnvMapSample(float3 Direction, float Roughness) { #if defined(IS_BASE_PASS) && IS_MOBILE_BASE_PASS==0 float SkyAverageBrightness = 1.0f; return GetSkyLightReflectionSupportingBlend(Direction, Roughness, SkyAverageBrightness); #elif defined(IS_BASE_PASS) && IS_MOBILE_BASE_PASS==1 return GetMobileSkyLightReflection(Direction, Roughness, MobileReflectionCapture.Params.y); #else return 0.0f; #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); } /** Accesses a shared material sampler or falls back if independent samplers are not supported. */ 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, // TextureSampler should have been set to the same sampler. This is not currently done. return TextureSampler; #endif } /** Calculate a reflection vector about the specified world space normal. Optionally normalize this normal **/ 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 /** * Calculates opacity for a billboard particle as if it were a sphere. * 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 = GetObjectTranslatedWorldPosition(Parameters); float ParticleRadius = max(0.000001f, GetPrimitiveData(Parameters).ObjectRadius); #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 - ResolvedView.NearPlane) / ParticleRadius)); #endif } #endif return Opacity; } #define LWCADDRESSMODE_CLAMP 0u #define LWCADDRESSMODE_WRAP 1u #define LWCADDRESSMODE_MIRROR 2u float LWCApplyAddressModeWrap(FLWCScalar V) { // Compute the fractional part of the tile, then add to the offset // Let the texture unit apply the final coordinate wrapping, which will allow derivatives to work correctly unless we cross a tile boundary const float FracTile = frac(LWCGetTile(V) * UE_LWC_RENDER_TILE_SIZE); return FracTile + V.Offset; } float LWCApplyAddressModeMirror(FLWCScalar v) { // Unclear what the best option is for MIRROR // We can apply the mirror logic directly, but that will break derivatives // We can use similar logic as WRAP, but in that case results will actually be incorrect at tile boundaries (not just wrong derivatives) // Or we can convert to float and accept precision loss for large values (but otherwise correct) // TODO - something better? //float t = LWCFrac(LWCMultiply(v, 0.5f)) * 2.0f; //return 1.0f - abs(t - 1.0f); return LWCToFloat(v); } float LWCApplyAddressModeClamp(FLWCScalar v) { // For the CLAMP case, a simple LWCToFloat() is sufficient. This will lose a ton of precision for large values, but we don't care about this since the GPU will clamp to [0,1) anyway // It's possible certain GPUs might need a special case if the ToFloat() conversion overflows return LWCToFloat(v); } float LWCApplyAddressMode(FLWCScalar v, uint AddressMode) { if(AddressMode == LWCADDRESSMODE_WRAP) return LWCApplyAddressModeWrap(v); else if(AddressMode == LWCADDRESSMODE_MIRROR) return LWCApplyAddressModeMirror(v); else return LWCApplyAddressModeClamp(v); } float2 LWCApplyAddressMode(FLWCVector2 UV, uint AddressX, uint AddressY) { return float2(LWCApplyAddressMode(LWCGetX(UV), AddressX), LWCApplyAddressMode(LWCGetY(UV), AddressY)); } float3 LWCApplyAddressMode(FLWCVector3 UV, uint AddressX, uint AddressY, uint AddressZ) { return float3(LWCApplyAddressMode(LWCGetX(UV), AddressX), LWCApplyAddressMode(LWCGetY(UV), AddressY), LWCApplyAddressMode(LWCGetZ(UV), AddressZ)); } float DFApplyAddressModeWrap(FDFScalar V) { // Hacky and certainly not flawless, but reasonably fast. // The resulting value is not in [0;1] so the texture unit apply the final coordinate wrapping, which will allow derivatives to work correctly unless cross the modulo boundary. const float Divisor = 0x1000; // Chosen somewhat arbitrarily. Large enough to not have seams everywhere, but small enough to have plenty of fractional precision in the result. return DFFmodByPow2Demote(V, Divisor); } float DFApplyAddressModeMirror(FDFScalar V) { // Unclear what the best option is for MIRROR // We can apply the mirror logic directly, but that will break derivatives // We can use similar logic as WRAP, but in that case results will actually be incorrect at tile boundaries (not just wrong derivatives) // Or we can convert to float and accept precision loss for large values (but otherwise correct) // TODO - something better? float a = DFFracDemote(DFDivideByPow2(V, 2)); float b = 2.0 * a; float c = 2.0 * (1.0 - a); return min(b, c); //DF_TODO: derivatives? Maybe use the same logic from Wrap instead? } float DFApplyAddressModeClamp(FDFScalar V) { // For the CLAMP case, a simple DFDemote() is sufficient. This will lose a ton of precision for large values, but we don't care about this since the GPU will clamp to [0,1) anyway // It's possible certain GPUs might need a special case if the ToFloat() conversion overflows return DFDemote(V); } float DFApplyAddressMode(FDFScalar v, uint AddressMode) { if(AddressMode == LWCADDRESSMODE_WRAP) return DFApplyAddressModeWrap(v); else if(AddressMode == LWCADDRESSMODE_MIRROR) return DFApplyAddressModeMirror(v); else return DFApplyAddressModeClamp(v); } float2 DFApplyAddressMode(FDFVector2 UV, uint AddressX, uint AddressY) { return float2(DFApplyAddressMode(DFGetX(UV), AddressX), DFApplyAddressMode(DFGetY(UV), AddressY)); } float3 DFApplyAddressMode(FDFVector3 UV, uint AddressX, uint AddressY, uint AddressZ) { return float3(DFApplyAddressMode(DFGetX(UV), AddressX), DFApplyAddressMode(DFGetY(UV), AddressY), DFApplyAddressMode(DFGetZ(UV), AddressZ)); } #if WSVECTOR_IS_TILEOFFSET #define WSApplyAddressMode LWCApplyAddressMode #elif WSVECTOR_IS_DOUBLEFLOAT #define WSApplyAddressMode DFApplyAddressMode #endif float2 RotateScaleOffsetTexCoords(float2 InTexCoords, float4 InRotationScale, float2 InOffset) { return float2(dot(InTexCoords, InRotationScale.xy), dot(InTexCoords, InRotationScale.zw)) + InOffset; } FWSVector2 RotateScaleOffsetTexCoords(FWSVector2 InTexCoords, float4 InRotationScale, float2 InOffset) { return WSAdd(MakeWSVector(WSDot(InTexCoords, InRotationScale.xy), WSDot(InTexCoords, InRotationScale.zw)), InOffset); } #if USES_SPEEDTREE /** Vertex offset for SpeedTree wind and LOD */ float3 GetSpeedTreeVertexOffsetInner(FMaterialVertexParameters Parameters, int GeometryType, int WindType, int LODType, float BillboardThreshold, bool bExtraBend, float3 ExtraBend, FSpeedTreeData STData) { #if (NUM_MATERIAL_TEXCOORDS_VERTEX < 6) || IS_MESHPARTICLE_FACTORY return float4(0,0,0); #endif #if USE_INSTANCING || USE_INSTANCE_CULLING float3x3 LocalToWorld = DFToFloat3x3(Parameters.InstanceLocalToWorld); float3 LocalPosition = Parameters.PositionInstanceSpace; // skip if this instance is hidden if (Parameters.PerInstanceParams.y < 1.f) { return float3(0,0,0); } #else float3x3 LocalToWorld = DFToFloat3x3(GetPrimitiveData(Parameters).LocalToWorld); float3 LocalPosition = WSMultiplyDemote(GetWorldPosition(Parameters), Parameters.LWCData.WorldToLocal); #endif FWSVector3 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 Dist = length(WSSubtractDemote(TreePos, DFToWS(ResolvedView.WorldCameraOrigin))); const float ScreenMultiple = 0.5 * max(ResolvedView.ViewToClip[0][0], ResolvedView.ViewToClip[1][1]); const float ScreenRadius = 2.0 * ScreenMultiple * GetPrimitiveData(Parameters).ObjectRadius / max(1.0, Dist); LodInterp = saturate((ScreenRadius - SpeedTreeLODInfo.x) / SpeedTreeLODInfo.z); } #endif float3 TreePosOffset = WSHackToFloat(WSDivideByPow2(TreePos, 1024)); // 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); // face camera-facing leaves to the camera, if needed if (GeometryType == SPEEDTREE_GEOMETRY_TYPE_FACINGLEAF) { // have to rotate the view into local space FinalPosition = LocalPosition - Anchor; FinalPosition = FinalPosition.x * ResolvedView.ViewRight + FinalPosition.y * ResolvedView.ViewUp + FinalPosition.z * ResolvedView.ViewForward; } Anchor = (mul(Anchor, LocalToWorld)) / TreeScale; if (GeometryType == SPEEDTREE_GEOMETRY_TYPE_LEAF) { 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 // 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, TreePosOffset, float4(VertBranchWind, 0, 0), WindType); } } // global wind can apply to the whole tree, even billboards bool bHasGlobal = (WindType != SPEEDTREE_WIND_TYPE_NONE); if (bExtraBend || bHasGlobal) { FinalPosition = GlobalWind(STData, FinalPosition, TreePosOffset, true, bHasGlobal, bExtraBend, ExtraBend); } // 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, bool bExtraBend, float3 ExtraBend) { #if VF_SUPPORTS_SPEEDTREE_WIND if (bUsePreviousFrame) { return GetSpeedTreeVertexOffsetInner(Parameters, GeometryType, WindType, LODType, BillboardThreshold, bExtraBend, ExtraBend, GetPreviousSpeedTreeData()); } return GetSpeedTreeVertexOffsetInner(Parameters, GeometryType, WindType, LODType, BillboardThreshold, bExtraBend, ExtraBend, GetCurrentSpeedTreeData()); #else return 0; #endif } #endif MaterialFloat2 GetLightmapUVs(FMaterialPixelParameters Parameters) { #if LIGHTMAP_UV_ACCESS return Parameters.LightmapUVs; #else return MaterialFloat2(0,0); #endif } MaterialFloat2 GetLightmapUVs_DDX(FMaterialPixelParameters Parameters) { #if LIGHTMAP_UV_ACCESS return Parameters.LightmapUVs_DDX; #else return MaterialFloat2(0, 0); #endif } MaterialFloat2 GetLightmapUVs_DDY(FMaterialPixelParameters Parameters) { #if LIGHTMAP_UV_ACCESS return Parameters.LightmapUVs_DDY; #else return MaterialFloat2(0, 0); #endif } //The post-process material needs to decode the scene color since it's encoded at PreTonemapMSAA if MSAA enabled on MetalMobilePlatorm //The POST_PROCESS_MATERIAL_BEFORE_TONEMAP is 1 for both BL_SceneColorBeforeDOF and BL_SceneColorAfterDOF post-process materials #if FEATURE_LEVEL <= FEATURE_LEVEL_ES3_1 && POST_PROCESS_MATERIAL && POST_PROCESS_MATERIAL_BEFORE_TONEMAP && METAL_ES3_1_PROFILE uint bMetalMSAAHDRDecode; #endif #if NEEDS_SCENE_TEXTURES #if POST_PROCESS_MATERIAL // SceneTextureId of UserSceneTexture SceneColor input, to allow SceneColor alpha to be dynamically propagated uint UserSceneTextureSceneColorInput; #endif #if SHADING_PATH_MOBILE MaterialFloat4 MobileSceneTextureLookup(inout FMaterialPixelParameters Parameters, int SceneTextureId, float2 UV) { #if SCENE_TEXTURES_DISABLED // When scene textures are disabled, the output is matched to the dummy scene texture defaults. switch(SceneTextureId) { case PPI_SceneDepth: case PPI_CustomDepth: return ConvertFromDeviceZ(0.0f); case PPI_MaterialAO: case PPI_CustomStencil: return 1.0f; default: return 0.0f; } #else #ifdef POST_PROCESS_PROPAGATE_ALPHA_INPUT static float PropagatedAlpha = 0.0f; #endif const float2 PixelPos = UV * View.BufferSizeAndInvSize.xy; FGBufferData GBuffer = MobileFetchAndDecodeGBuffer(UV, PixelPos, GetViewId(Parameters)); switch(SceneTextureId) { // order needs to match to ESceneTextureId case PPI_SceneDepth: return GBuffer.Depth; case PPI_DiffuseColor: return MaterialFloat4(GBuffer.DiffuseColor, 0); case PPI_SpecularColor: return MaterialFloat4(GBuffer.SpecularColor, 0); case PPI_SubsurfaceColor: return IsSubsurfaceModel(GBuffer.ShadingModelID) ? MaterialFloat4( ExtractSubsurfaceColor(GBuffer), GBuffer.CustomData.a ) : GBuffer.CustomData; case PPI_BaseColor: return MaterialFloat4(GBuffer.BaseColor, 0); case PPI_Specular: return GBuffer.Specular; case PPI_Metallic: return GBuffer.Metallic; case PPI_WorldNormal: return MaterialFloat4(GBuffer.WorldNormal, 0); case PPI_SeparateTranslucency: return MaterialFloat4(1, 1, 1, 1); // todo case PPI_Opacity: return GBuffer.CustomData.a; case PPI_Roughness: return GBuffer.Roughness; case PPI_MaterialAO: return GBuffer.GBufferAO; case PPI_CustomDepth: return GBuffer.CustomDepth; #if POST_PROCESS_MATERIAL case PPI_PostProcessInput0: { MaterialFloat4 Input0 = Texture2DSample(PostProcessInput_0_Texture, PostProcessInput_0_SharedSampler, UV); #if POST_PROCESS_MATERIAL_BEFORE_TONEMAP && METAL_ES3_1_PROFILE // Decode the input color since the color is encoded for MSAA // The decode instructions might be able to skip with dynamic branch if (bMetalMSAAHDRDecode) { Input0.rgb = Input0.rgb * rcp(Input0.r*(-0.299) + Input0.g*(-0.587) + Input0.b*(-0.114) + 1.0); } #endif #ifdef POST_PROCESS_PROPAGATE_ALPHA_INPUT // We do a comparison of the POST_PROCESS_PROPAGATE_ALPHA_INPUT define with the PPI input index, to support propagation of alpha for shaders with UserSceneTextures, // where the specific PPI_PostProcessInput that has SceneColor as an input is dynamic. Usually, POST_PROCESS_PROPAGATE_ALPHA_INPUT will be a constant, when a normal // SceneTexture node is used. if (POST_PROCESS_PROPAGATE_ALPHA_INPUT == PPI_PostProcessInput0) { PropagatedAlpha = Input0.a; } #endif return Input0; } #define RETURN_MOBILE_POST_PROCESS_INPUT_SAMPLE(Index) \ return Texture2DSample(PostProcessInput_##Index##_Texture, PostProcessInput_##Index##_SharedSampler, UV) case PPI_PostProcessInput1: RETURN_MOBILE_POST_PROCESS_INPUT_SAMPLE(1); case PPI_PostProcessInput2: RETURN_MOBILE_POST_PROCESS_INPUT_SAMPLE(2); case PPI_PostProcessInput3: RETURN_MOBILE_POST_PROCESS_INPUT_SAMPLE(3); case PPI_PostProcessInput4: RETURN_MOBILE_POST_PROCESS_INPUT_SAMPLE(4); #endif // POST_PROCESS_MATERIAL case PPI_DecalMask: return 0; // material compiler will return an error case PPI_ShadingModelColor: return MaterialFloat4(GetShadingModelColor(GBuffer.ShadingModelID), 1); case PPI_ShadingModelID: return MaterialFloat4(GBuffer.ShadingModelID, 0, 0, 0); case PPI_AmbientOcclusion: return 1; case PPI_CustomStencil: return GBuffer.CustomStencil; case PPI_WorldTangent: return MaterialFloat4(GBuffer.WorldTangent, 0); #ifdef POST_PROCESS_PROPAGATE_ALPHA_INPUT case PPI_PropagatedAlpha: return MaterialFloat4(PropagatedAlpha, 0, 0, 0); #endif default: return MaterialFloat4(0, 0, 0, 0); } #endif } #endif // SHADING_PATH_MOBILE #if SHADING_PATH_DEFERRED #if POST_PROCESS_MATERIAL /** Samples the screen-space velocity for the specified UV coordinates. */ float2 PostProcessVelocityLookup(float Depth, float2 UV) { float4 EncodedVelocity = Texture2DSampleLevel(SceneTexturesStruct.GBufferVelocityTexture, SceneTexturesStruct_GBufferVelocityTextureSampler, UV, 0); float2 Velocity; if( EncodedVelocity.x > 0.0 ) { Velocity = DecodeVelocityFromTexture(EncodedVelocity).xy; } else { float2 ScreenPos = ViewportUVToScreenPos(UV); float4 ThisClip = float4( ScreenPos, Depth, 1 ); float4 PrevClip = mul( ThisClip, View.ClipToPrevClip ); float2 PrevScreen = PrevClip.xy / PrevClip.w; Velocity = ScreenPos - PrevScreen; } return Velocity; } #endif /** Applies an offset to the scene texture lookup and decodes the HDR linear space color. */ float4 SceneTextureLookup(float2 UV, int SceneTextureIndex, bool bFiltered) { #if SCENE_TEXTURES_DISABLED // When scene textures are disabled, the output is matched to the dummy scene texture defaults. switch(SceneTextureIndex) { case PPI_SceneDepth: case PPI_CustomDepth: return ConvertFromDeviceZ(0.0f); case PPI_MaterialAO: case PPI_CustomStencil: return 1.0f; default: return 0.0f; } #elif MATERIAL_SHADINGMODEL_SINGLELAYERWATER if (SceneTextureIndex == PPI_CustomDepth) { MaterialFloat Depth = ConvertFromDeviceZ(Texture2DSample(SingleLayerWater.CustomDepthTexture, SingleLayerWater.CustomDepthSampler, UV).r); return MaterialFloat4(Depth.rrr, 0.f); } else if (SceneTextureIndex == PPI_CustomStencil) { MaterialFloat Stencil = SingleLayerWater.CustomStencilTexture.Load(int3(UV * View.BufferSizeAndInvSize.xy, 0)) STENCIL_COMPONENT_SWIZZLE; return MaterialFloat4(Stencil, Stencil, Stencil, 0.f); } #else // SCENE_TEXTURES_DISABLED #if MATERIAL_DOMAIN_SURFACE && MATERIALBLENDING_ANY_TRANSLUCENT // BufferToSceneTextureScale is only needed for translucent surfaces today and we know that here. const uint2 PixelPos = uint2(UV * View.BufferSizeAndInvSize.xy * View.BufferToSceneTextureScale.xy); #else const uint2 PixelPos = uint2(UV * View.BufferSizeAndInvSize.xy); #endif #ifdef POST_PROCESS_PROPAGATE_ALPHA_INPUT // Propagation of SceneColor alpha, to preserve it in materials that don't themselves write to alpha, is supported by storing the propagated alpha to a local variable, // which can be read from with a call to this function at the end of the shader. The big advantage of this approach is that it even works with Custom HLSL, since it // just requires the SceneTextureLookup code to be reached by any means. It also works well with UserSceneTexture fetches, where a given input can be dynamically set // to SceneColor. Some complexity is added in that SceneTexture or UserSceneTexture nodes feeding into custom HLSL, where the input pin value itself isn't used, now // need to be explicitly dead stripped, as the side effect of writing to PropagatedAlpha prevents the shader compiler from optimizing out calls to SceneTextureLookup // based on the return value being unused in downstream code. See UMaterialExpressionCustom::Compile for where that is handled. static float PropagatedAlpha = 0.0f; // There are three parts to the condition to detect whether we should automatically propagate SceneColor alpha: // 1. To make the shader compiler optimize out repeated reads of alpha, we set a local bool to ensure we only read alpha from a given scene texture index once. // 2. We want to support propagation of alpha for UserSceneTextures. A given input is a UserSceneTexture if it's not used as a regular SceneTexture, as // specified by the POST_PROCESS_USED_SCENE_TEXTURES mask compile define, so we skip propagation for any input with a bit set there. // PPI_PostProcessInput0 is an exception, which is the SceneColor itself when used as a SceneTexture, and should always support propagation. // 3. Finally, we test if the given input equals POST_PROCESS_PROPAGATE_ALPHA_INPUT, which will either be PPI_PostProcessInput0 if the material in question // directly uses that as a SceneTexture, or the dynamic UserSceneTextureSceneColorInput variable, if UserSceneTexture nodes are in use, and one of them // may be mapped to SceneColor. // If PostProcessInput0 is directly fetched in a SceneTexture node, the conditional will be compile time invariant, and no extra code is generated, making the alpha // propagation free. Otherwise, for UserSceneTextures, only the final part of the comparison is dynamic, and the rest will be compile time invariant, so it adds one // scalar ALU (equals) and one vector ALU (conditional move), although the first UserSceneTexture vector ALU is free, rolled into the output assignment. The material // can be set up to output opacity to optimize that minor overhead out completely if desired. // #define RETURN_POST_PROCESS_INPUT_SAMPLE(Index) { \ float4 Result = Texture2DSample(PostProcessInput_##Index##_Texture, bFiltered ? PostProcessInput_BilinearSampler : PostProcessInput_##Index##_SharedSampler, UV); \ static bool bFirstRead = true; \ if (bFirstRead && !(POST_PROCESS_USED_SCENE_TEXTURES & (1u << (Index + PPI_PostProcessInput0)) & ~(1u< 0) { switch (SceneTextureIndex) { // order needs to match to ESceneTextureId case PPI_DiffuseColor: return float4(SubstrateGBuffer.DiffuseColor, 0); case PPI_SpecularColor: return float4(SubstrateGBuffer.SpecularColor, 0); case PPI_SubsurfaceColor: return float4(SubstrateGBuffer.SubsurfaceColor, 0); case PPI_BaseColor: return float4(SubstrateGBuffer.BaseColor, 0); case PPI_Specular: return SubstrateGBuffer.Specular; case PPI_Metallic: return SubstrateGBuffer.Metallic; case PPI_WorldNormal: return float4(SubstrateGBuffer.WorldNormal, 0); case PPI_Opacity: return SubstrateGBuffer.Opacity; case PPI_Roughness: return SubstrateGBuffer.Roughness; case PPI_MaterialAO: return SubstrateGBuffer.MaterialAO; case PPI_ShadingModelColor: return float4(SubstrateGBuffer.ShadingModelColor, 1); case PPI_ShadingModelID: return float4(SubstrateGBuffer.ShadingModelID, 0, 0, 0); case PPI_StoredBaseColor: return float4(SubstrateGBuffer.StoredBaseColor, 0); case PPI_StoredSpecular: return float4(SubstrateGBuffer.StoredSpecular.rrr, 0); case PPI_WorldTangent: return float4(SubstrateGBuffer.WorldTangent, 0); case PPI_Anisotropy: return SubstrateGBuffer.Anisotropy; case PPI_IsFirstPerson: return SubstrateGBuffer.bIsFirstPerson ? 1.0f : 0.0f; } } } #else // SUBSTRATE_ENABLED { FScreenSpaceData ScreenSpaceData = GetScreenSpaceData(UV, false); switch (SceneTextureIndex) { // order needs to match to ESceneTextureId case PPI_DiffuseColor: return float4(ScreenSpaceData.GBuffer.DiffuseColor, 0); case PPI_SpecularColor: return float4(ScreenSpaceData.GBuffer.SpecularColor, 0); case PPI_SubsurfaceColor: return IsSubsurfaceModel(ScreenSpaceData.GBuffer.ShadingModelID) ? float4(ExtractSubsurfaceColor(ScreenSpaceData.GBuffer), ScreenSpaceData.GBuffer.CustomData.a) : ScreenSpaceData.GBuffer.CustomData; case PPI_BaseColor: return float4(ScreenSpaceData.GBuffer.BaseColor, 0); case PPI_Specular: return ScreenSpaceData.GBuffer.Specular; case PPI_Metallic: return ScreenSpaceData.GBuffer.Metallic; #if IS_DBUFFER_DECAL case PPI_WorldNormal: return GetDBufferReprojectedWorldNormal(UV); // DBuffer uses depth buffer gradients or reprojection of last frame normal. #else case PPI_WorldNormal: return float4(ScreenSpaceData.GBuffer.WorldNormal, 0); #endif case PPI_Opacity: return ScreenSpaceData.GBuffer.CustomData.a; case PPI_Roughness: return ScreenSpaceData.GBuffer.Roughness; case PPI_MaterialAO: return ScreenSpaceData.GBuffer.GBufferAO; case PPI_ShadingModelColor: return float4(GetShadingModelColor(ScreenSpaceData.GBuffer.ShadingModelID), 1); case PPI_ShadingModelID: return float4(ScreenSpaceData.GBuffer.ShadingModelID, 0, 0, 0); case PPI_StoredBaseColor: return float4(ScreenSpaceData.GBuffer.StoredBaseColor, 0); case PPI_StoredSpecular: return float4(ScreenSpaceData.GBuffer.StoredSpecular.rrr, 0); case PPI_WorldTangent: return float4(ScreenSpaceData.GBuffer.WorldTangent, 0); case PPI_Anisotropy: return ScreenSpaceData.GBuffer.Anisotropy; case PPI_IsFirstPerson: return IsFirstPerson(ScreenSpaceData.GBuffer) ? 1.0f : 0.0f; } } #endif // SUBSTRATE_ENABLED #endif // SCENE_TEXTURES_DISABLED return float4(0, 0, 0, 0); } #define SceneTextureFetch(SceneTextureIndex, PixelOffset) SceneTextureFetchFunc(Parameters, SceneTextureIndex, PixelOffset) #endif // SHADING_PATH_DEFERRED /** * Utility function and macro to streamline Custom HLSL fetching from SceneTextures or UserSceneTextures with a pixel offset from default UVs, including * multiplication by texture size and clamping. Example that fetches at offset 1,1 from the default sample: * * SceneTextureFetch(PPI_PostProcessInput0, float2(1,1)); */ float4 SceneTextureFetchFunc(FMaterialPixelParameters Parameters, int SceneTextureIndex, float2 PixelOffset) { #if SCENE_TEXTURES_DISABLED || !POST_PROCESS_MATERIAL return float4(0,0,0,0); #else float2 UV = ClampSceneTextureUV(GetDefaultSceneTextureUV(Parameters, SceneTextureIndex) + GetSceneTextureViewSize(SceneTextureIndex).zw * PixelOffset, SceneTextureIndex); #if SHADING_PATH_MOBILE return MobileSceneTextureLookup(Parameters, SceneTextureIndex, UV); #else return SceneTextureLookup(UV, SceneTextureIndex, true); #endif #endif } float4 SceneTextureFetchFunc(FMaterialPixelParameters Parameters, int SceneTextureIndex, float PixelOffsetX, float PixelOffsetY) { return SceneTextureFetchFunc(Parameters, SceneTextureIndex, float2(PixelOffsetX, PixelOffsetY)); } #endif // NEEDS_SCENE_TEXTURES #if SHADING_PATH_DEFERRED /** Applies an offset to the scene texture lookup and decodes the HDR linear space color and alpha. */ float4 DecodeSceneColorAndAlpharForMaterialNode(float2 ScreenUV) { #if !defined(SceneColorCopyTexture) // Hit proxies rendering pass doesn't have access to valid render buffers return float4(0.0f, 0.0f, 0.0f, 0.0f); #else float4 EncodedSceneColor = Texture2DSample(SceneColorCopyTexture, SceneColorCopySampler, ScreenUV); // Undo the function in EncodeSceneColorForMaterialNode float3 SampledColor = pow(EncodedSceneColor.rgb, 4) * 10; SampledColor *= View.OneOverPreExposure.xxx; return float4(SampledColor, EncodedSceneColor.a); #endif } /** Applies an offset to the scene texture lookup and decodes the HDR linear space color. */ float3 DecodeSceneColorForMaterialNode(float2 ScreenUV) { return DecodeSceneColorAndAlpharForMaterialNode(ScreenUV).rgb; } #endif // SHADING_PATH_DEFERRED float4 MaterialExpressionDBufferTextureLookup(FMaterialPixelParameters Parameters, float2 BufferUV, int DBufferTextureIndex) { if ((GetPrimitiveData(Parameters).Flags & PRIMITIVE_SCENE_DATA_FLAG_DECAL_RECEIVER) != 0 && View.ShowDecalsMask > 0) { uint2 PixelPos = uint2(BufferUV * View.BufferSizeAndInvSize.xy); uint ValidDBufferTargetMask = GetDBufferTargetMask(PixelPos) & (1u << DBufferTextureIndex); #if SUBSTRATE_ENABLED const FSubstrateDBuffer DBufferData = SubstrateGetDBufferData(BufferUV, ValidDBufferTargetMask); switch (DBufferTextureIndex) { // All data (BaseColor, WorldNormal, Roughness) are pre-multiplied by their coverage case 0: return float4(DBufferData.BaseColor, DBufferData.OneMinusCoverage_BaseColor); case 1: return float4(DBufferData.WorldNormal, DBufferData.OneMinusCoverage_WorldNormal); case 2: return float4(DBufferData.Roughness, DBufferData.Metallic, DBufferData.Specular, DBufferData.OneMinusCoverage_Roughness); } #else FDBufferData DBufferData = GetDBufferData(BufferUV, ValidDBufferTargetMask); switch (DBufferTextureIndex) { case 0: return float4(DBufferData.PreMulColor, DBufferData.ColorOpacity); case 1: return float4(DBufferData.PreMulWorldNormal, DBufferData.NormalOpacity); case 2: return float4(DBufferData.PreMulRoughness, DBufferData.PreMulMetallic, DBufferData.PreMulSpecular, DBufferData.RoughnessOpacity); } #endif } return float4(0, 0, 0, 1); } MaterialFloat2 GetDefaultPathTracingBufferTextureUV(FMaterialPixelParameters Parameters, const uint PathTracingBufferTextureIndex) { float2 ViewportUV = GetViewportUV(Parameters); #if POST_PROCESS_MATERIAL && SHADING_PATH_DEFERRED && MATERIAL_PATH_TRACING_BUFFER_READ && PATH_TRACING_POST_PROCESS_MATERIAL switch (PathTracingBufferTextureIndex) { case 0: return ViewportUV * PathTracingPostProcessInput_0_UVViewportSize + PathTracingPostProcessInput_0_UVViewportMin; case 1: return ViewportUV * PathTracingPostProcessInput_1_UVViewportSize + PathTracingPostProcessInput_1_UVViewportMin; case 2: return ViewportUV * PathTracingPostProcessInput_2_UVViewportSize + PathTracingPostProcessInput_2_UVViewportMin; case 3: return ViewportUV * PathTracingPostProcessInput_3_UVViewportSize + PathTracingPostProcessInput_3_UVViewportMin; case 4: return ViewportUV * PathTracingPostProcessInput_4_UVViewportSize + PathTracingPostProcessInput_4_UVViewportMin; } #endif return ViewportUV; } float4 MaterialExpressionPathTracingBufferTextureLookup(FMaterialPixelParameters Parameters, float2 BufferUV, int PathTracingBufferTextureIndex) { #if POST_PROCESS_MATERIAL && SHADING_PATH_DEFERRED && MATERIAL_PATH_TRACING_BUFFER_READ && PATH_TRACING_POST_PROCESS_MATERIAL switch (PathTracingBufferTextureIndex) { case 0: return Texture2DSample(PathTracingPostProcessInput_0_Texture, PathTracingPostProcessInput_0_Sampler, BufferUV); case 1: return Texture2DSample(PathTracingPostProcessInput_1_Texture, PathTracingPostProcessInput_1_Sampler, BufferUV); case 2: return Texture2DSample(PathTracingPostProcessInput_2_Texture, PathTracingPostProcessInput_2_Sampler, BufferUV); case 3: return Texture2DSample(PathTracingPostProcessInput_3_Texture, PathTracingPostProcessInput_3_Sampler, BufferUV); case 4: return Texture2DSample(PathTracingPostProcessInput_4_Texture, PathTracingPostProcessInput_4_Sampler, BufferUV); } #endif return float4(1, 0, 0, 1); } float4 NeuralTextureOutput(FMaterialPixelParameters Parameters, float2 ViewportUV) { #if POST_PROCESS_MATERIAL && SHADING_PATH_DEFERRED && MATERIAL_NEURAL_POST_PROCESS float2 BufferUV = ViewportUV * PostProcessInput_0_UVViewportSize + PostProcessInput_0_UVViewportMin; float4 TextureValue = Texture2DSampleLevel(NeuralTexture, PostProcessInput_BilinearSampler, BufferUV, 0); return TextureValue; #else return float4(0.0f, 0.0f, 0.0f, 1.0f); #endif } float4 NeuralBufferOutput(FMaterialPixelParameters Parameters, float4 BufferIndices) { #if POST_PROCESS_MATERIAL && SHADING_PATH_DEFERRED && MATERIAL_NEURAL_POST_PROCESS float3 TextureValue = 0; int Batch = (int)BufferIndices.x; int StartChannel = (int)BufferIndices.y; float2 BufferWH = BufferIndices.zw; ReadFromNeuralBuffer(Batch, StartChannel, BufferWH, TextureValue); return float4(TextureValue, 1.0f); #else return float4(0.0f, 0.0f, 0.0f, 1.0f); #endif } // DERIV_BASE_VALUE() is a disgusting macro to manage backwards compatibility while changing the generated materials. The existing nodes are not "aware" // of partial derivatives, and will use sprintf() to put the same line in both CalcPixelMaterialInputs() and CalcPixelMaterialInputsAnalyticDerivatives(). // If we have a line like this in CalcPixelMaterialInputs(): // float Local0 = ...; // It will look like this in CalcPixelMaterialInputsAnalyticDerivatives() // FloatDeriv Local0 = ...; // // That's ok, since the Local0 line is aware of derivatives. But if we have a line that isn't aware then we would want the CalcPixelMaterialInputs() version to be: // float Local1 = Local0 + 3.2; // But the CalcPixelMaterialInputsAnalyticDerivatives() version would be: // float Local1 = Local0.Value + 3.2; // // It's not possible to emit two different versions (with and without ".Value") unless we change every single material node. So the workaround is to always emit // DERIV_BASE_VALUE, and in one version #define it to ".Value" and in the other to "". That way, we can emit the same code to both functions. Ideally, once every // single node is at least aware of derivatives (and can output two different versions) then we can remove this #define and #undef for DERIV_BASE_VALUE #define DERIV_BASE_VALUE(_X) _X #define SwizzleDeriv1(_V, _MASK) ConstructFloatDeriv( _V.Value._MASK, _V.Ddx._MASK, _V.Ddy._MASK) #define SwizzleDeriv2(_V, _MASK) ConstructFloatDeriv2(_V.Value._MASK, _V.Ddx._MASK, _V.Ddy._MASK) #define SwizzleDeriv3(_V, _MASK) ConstructFloatDeriv3(_V.Value._MASK, _V.Ddx._MASK, _V.Ddy._MASK) #define SwizzleDeriv4(_V, _MASK) ConstructFloatDeriv4(_V.Value._MASK, _V.Ddx._MASK, _V.Ddy._MASK) // Uniform material expressions. SHADER_PUSH_WARNINGS_STATE SHADER_DISABLE_WARNINGS %{uniform_material_expressions} SHADER_POP_WARNINGS_STATE #if USE_ANALYTIC_DERIVATIVES && TEXTURE_SAMPLE_DEBUG MaterialFloat4 DebugTextureCommon(const int Mode, float2 UV, MaterialFloat2 DDX, MaterialFloat2 DDY, MaterialFloat Scale) { const float DerivScale = View.GeneralPurposeTweak2 * 100.0f; if (Mode == 2) { return MaterialFloat4(UV, 0.0f, 0.0f); } else if (Mode == 3) { return MaterialFloat4(DDX * DerivScale + 0.5f, 0.0f, 0.0f); } else if (Mode == 4) { const float2 FiniteDDX = ddx(UV) * Scale; return MaterialFloat4(FiniteDDX * DerivScale + 0.5f, 0.0f, 0.0f); } else if (Mode == 5) { return MaterialFloat4(DDY * DerivScale + 0.5f, 0.0f, 0.0f); } else if(Mode == 6) { const float2 FiniteDDY = ddy(UV) * Scale; return MaterialFloat4(FiniteDDY * DerivScale + 0.5f, 0.0f, 0.0f); } else { return MaterialFloat4(0.0f, 0.0f, 0.0f, 0.0f); } } MaterialFloat4 DebugTexture2DSampleGrad(Texture2D Tex, SamplerState Sampler, float2 UV, MaterialFloat2 DDX, MaterialFloat2 DDY, MaterialFloat Scale) { const int Mode = round(View.GeneralPurposeTweak); if (Mode > 1) return DebugTextureCommon(Mode, UV, DDX, DDY, Scale); else return Tex.SampleGrad(Sampler, UV, DDX, DDY); } #if NUM_VIRTUALTEXTURE_SAMPLES || LIGHTMAP_VT_ENABLED MaterialFloat4 DebugTextureVirtualSample( Texture2D Physical, SamplerState PhysicalSampler, VTPageTableResult PageTableResult, uint LayerIndex, VTUniform Uniform, MaterialFloat Scale) { const int Mode = round(View.GeneralPurposeTweak); if(Mode > 1) return DebugTextureCommon(Mode, PageTableResult.UV, PageTableResult.dUVdx, PageTableResult.dUVdy, Scale); else return TextureVirtualSample(Physical, PhysicalSampler, PageTableResult, LayerIndex, Uniform); } MaterialFloat4 DebugTextureVirtualSampleLevel( Texture2D Physical, SamplerState PhysicalSampler, VTPageTableResult PageTableResult, uint LayerIndex, VTUniform Uniform, MaterialFloat Scale) { const int Mode = round(View.GeneralPurposeTweak); if (Mode > 1) return MaterialFloat4(0.0f, 0.0f, 0.0f, 0.0f); else return TextureVirtualSampleLevel(Physical, PhysicalSampler, PageTableResult, LayerIndex, Uniform); } #endif #endif // 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 && !ES3_1_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 = ResolvedView.NormalOverrideParameter.xyz; #if !MATERIAL_TANGENTSPACENORMAL OverrideNormal = Parameters.TangentToWorld[2] * (1 - ResolvedView.NormalOverrideParameter.w); #endif RetNormal = RetNormal * ResolvedView.NormalOverrideParameter.w + OverrideNormal; } #endif return RetNormal; } half3 GetMaterialTangentRaw(FPixelMaterialInputs PixelMaterialInputs) { return PixelMaterialInputs.Tangent; } half3 GetMaterialTangent(FPixelMaterialInputs PixelMaterialInputs) { return GetMaterialTangentRaw(PixelMaterialInputs); } 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) { SHADER_PUSH_WARNINGS_STATE SHADER_DISABLE_WARNINGS %{get_material_emissive_for_cs}; SHADER_POP_WARNINGS_STATE } // Shading Model is an uint and represents a SHADINGMODELID_* in ShadingCommon.ush uint GetMaterialShadingModel(FPixelMaterialInputs PixelMaterialInputs) { return PixelMaterialInputs.ShadingModel; } 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 half Roughness = saturate(GetMaterialRoughnessRaw(PixelMaterialInputs)); #if (USE_EDITOR_SHADERS && !ES3_1_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) Roughness = Roughness * ResolvedView.RoughnessOverrideParameter.y + ResolvedView.RoughnessOverrideParameter.x; } #endif return Roughness; } half GetMaterialAnisotropyRaw(FPixelMaterialInputs PixelMaterialInputs) { return PixelMaterialInputs.Anisotropy; } half GetMaterialAnisotropy(FPixelMaterialInputs PixelMaterialInputs) { return clamp(GetMaterialAnisotropyRaw(PixelMaterialInputs), -1.0f, 1.0f); } SHADER_PUSH_WARNINGS_STATE SHADER_DISABLE_WARNINGS half GetMaterialTranslucencyDirectionalLightingIntensity() { %{get_material_translucency_directional_lighting_intensity}; } half GetMaterialTranslucentShadowDensityScale() { %{get_material_translucent_shadow_density_scale}; } half GetMaterialTranslucentSelfShadowDensityScale() { %{get_material_translucent_self_shadow_density_scale}; } half GetMaterialTranslucentSelfShadowSecondDensityScale() { %{get_material_translucent_self_shadow_second_density_scale}; } half GetMaterialTranslucentSelfShadowSecondOpacity() { %{get_material_translucent_self_shadow_second_opacity}; } half GetMaterialTranslucentBackscatteringExponent() { %{get_material_translucent_backscattering_exponent}; } half3 GetMaterialTranslucentMultipleScatteringExtinction() { %{get_material_translucent_multiple_scattering_extinction}; } // 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() { %{get_material_opacity_mask_clip_value}; } SHADER_POP_WARNINGS_STATE #if USES_DISPLACEMENT && IS_NANITE_PASS float GetMaterialDisplacement(FPixelMaterialInputs PixelMaterialInputs) { return saturate(PixelMaterialInputs.Displacement); } #endif // 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 // 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 // MaskClip value constant. If this value is <=0 the pixel should be killed. 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)); } #if TRANSLUCENT_SHADOW_WITH_MASKED_OPACITY half GetMaterialMaskedOpacity(FPixelMaterialInputs PixelMaterialInputs) { return GetMaterialOpacity(PixelMaterialInputs) - GetMaterialOpacityMaskClipValue(); } #endif bool IsFirstPerson_FromFlags(uint Flags) { #if SUPPORT_FIRST_PERSON_RENDERING return (Flags & PRIMITIVE_SCENE_DATA_FLAG_IS_FIRST_PERSON) != 0; #else return false; #endif } bool IsFirstPerson(FMaterialVertexParameters Parameters) { return IsFirstPerson_FromFlags(GetPrimitiveData(Parameters).Flags); } bool IsFirstPerson(FMaterialPixelParameters Parameters) { return IsFirstPerson_FromFlags(GetPrimitiveData(Parameters).Flags); } float3 TransformToFirstPerson(float3 TranslatedWorldPosition, float FirstPersonInterpolationAlpha) { #if SUPPORT_FIRST_PERSON_RENDERING const float3 FirstPersonPosition = mul(TranslatedWorldPosition, (float3x3)ResolvedView.FirstPersonTransform); const float3 InterpolatedPosition = lerp(TranslatedWorldPosition, FirstPersonPosition, FirstPersonInterpolationAlpha); return InterpolatedPosition; #else return TranslatedWorldPosition; #endif } float3 TransformToPreviousFirstPerson(float3 TranslatedWorldPosition, float FirstPersonInterpolationAlpha) { #if SUPPORT_FIRST_PERSON_RENDERING const float3 FirstPersonPosition = mul(TranslatedWorldPosition, (float3x3)ResolvedView.PrevFirstPersonTransform); const float3 InterpolatedPosition = lerp(TranslatedWorldPosition, FirstPersonPosition, FirstPersonInterpolationAlpha); return InterpolatedPosition; #else return TranslatedWorldPosition; #endif } float GetMaterialFirstPersonInterpolationAlpha(FMaterialVertexParameters Parameters) { #if SUPPORT_FIRST_PERSON_RENDERING && HAVE_GetFirstPersonOutput0 return saturate(GetFirstPersonOutput0(Parameters)); #else return 1.0f; #endif } float GetMaterialPreviousFirstPersonInterpolationAlpha(FMaterialVertexParameters Parameters) { #if SUPPORT_FIRST_PERSON_RENDERING && HAVE_GetFirstPersonOutput0Prev return saturate(GetFirstPersonOutput0Prev(Parameters)); #else return 1.0f; #endif } void ApplyMaterialFirstPersonTransform(FMaterialVertexParameters Parameters, inout float3 WorldPositionWithWPO) { #if SUPPORT_FIRST_PERSON_RENDERING BRANCH if (IsFirstPerson(Parameters)) { const float LerpAlpha = GetMaterialFirstPersonInterpolationAlpha(Parameters); const float3 FirstPersonPosition = TransformToFirstPerson(WorldPositionWithWPO, LerpAlpha); WorldPositionWithWPO = FirstPersonPosition; } #endif } void ApplyMaterialPreviousFirstPersonTransform(FMaterialVertexParameters Parameters, inout float3 PreviousWorldPositionWithWPO) { #if SUPPORT_FIRST_PERSON_RENDERING BRANCH if (IsFirstPerson(Parameters)) { const float LerpAlpha = GetMaterialPreviousFirstPersonInterpolationAlpha(Parameters); const float3 PreviousFirstPersonPosition = TransformToPreviousFirstPerson(PreviousWorldPositionWithWPO, LerpAlpha); PreviousWorldPositionWithWPO = PreviousFirstPersonPosition; } #endif } bool ShouldEnableWorldPositionOffset(FMaterialVertexParameters Parameters) { #if USES_WORLD_POSITION_OFFSET #if (FEATURE_LEVEL > FEATURE_LEVEL_ES3_1) // Non optional WPO on mobile if (!Parameters.bEvaluateWorldPositionOffset || (GetPrimitiveData(Parameters).Flags & PRIMITIVE_SCENE_DATA_FLAG_EVALUATE_WORLD_POSITION_OFFSET) == 0) { return false; } #endif return true; #else return false; #endif } float3 ClampWorldPositionOffset(FMaterialVertexParameters Parameters, float3 InOffset) { #if (FEATURE_LEVEL == FEATURE_LEVEL_ES3_1 && VF_SUPPORTS_PRIMITIVE_SCENE_DATA) // Do not use WPO clamping on mobile platforms as MaxWPOExtent has to be pulled from PrimitiveUB breaking auto-instancing return InOffset; #else const float MaxWPODim = GetPrimitiveData(Parameters).MaxWPOExtent; return MaxWPODim <= 0.0f ? InOffset : clamp(InOffset, -MaxWPODim.xxx, MaxWPODim.xxx); #endif } float3 GetMaterialWorldPositionOffsetRaw(FMaterialVertexParameters Parameters) { SHADER_PUSH_WARNINGS_STATE SHADER_DISABLE_WARNINGS %{get_material_world_position_offset_raw}; SHADER_POP_WARNINGS_STATE } float3 GetMaterialWorldPositionOffset(FMaterialVertexParameters Parameters) { BRANCH if (ShouldEnableWorldPositionOffset(Parameters)) { return ClampWorldPositionOffset(Parameters, GetMaterialWorldPositionOffsetRaw(Parameters)); } return float3(0, 0, 0); } float3 GetMaterialPreviousWorldPositionOffsetRaw(FMaterialVertexParameters Parameters) { SHADER_PUSH_WARNINGS_STATE SHADER_DISABLE_WARNINGS %{get_material_previous_world_position_offset_raw}; SHADER_POP_WARNINGS_STATE } float3 GetMaterialPreviousWorldPositionOffset(FMaterialVertexParameters Parameters) { BRANCH if (ShouldEnableWorldPositionOffset(Parameters)) { return ClampWorldPositionOffset(Parameters, GetMaterialPreviousWorldPositionOffsetRaw(Parameters)); } return float3(0, 0, 0); } // .rgb:SubsurfaceColor, .a:SSProfileId in 0..1 range MaterialFloat4 GetMaterialSubsurfaceDataRaw(FPixelMaterialInputs PixelMaterialInputs) { return PixelMaterialInputs.Subsurface; } MaterialFloat4 GetMaterialSubsurfaceData(FPixelMaterialInputs PixelMaterialInputs) { MaterialFloat4 OutSubsurface = GetMaterialSubsurfaceDataRaw(PixelMaterialInputs); OutSubsurface.rgb = saturate(OutSubsurface.rgb); return OutSubsurface; } SHADER_PUSH_WARNINGS_STATE SHADER_DISABLE_WARNINGS half GetMaterialCustomData0(FPixelMaterialInputs PixelMaterialInputs) { return PixelMaterialInputs.CustomData0; } half GetMaterialCustomData1(FPixelMaterialInputs PixelMaterialInputs) { return PixelMaterialInputs.CustomData1; } SHADER_POP_WARNINGS_STATE half GetMaterialAmbientOcclusionRaw(FPixelMaterialInputs PixelMaterialInputs) { half AmbientOcclusion = PixelMaterialInputs.AmbientOcclusion; #if (USE_EDITOR_SHADERS && !ES3_1_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) AmbientOcclusion = AmbientOcclusion * ResolvedView.AmbientOcclusionOverrideParameter.y + ResolvedView.AmbientOcclusionOverrideParameter.x; } #endif return AmbientOcclusion; } half GetMaterialAmbientOcclusion(FPixelMaterialInputs PixelMaterialInputs) { return saturate(GetMaterialAmbientOcclusionRaw(PixelMaterialInputs)); } struct FMaterialRefractionData { float2 Data; float RefractionDepthBias; }; float GetMaterialRefractionIOR(in FMaterialRefractionData RefractionData) { #if REFRACTION_USE_INDEX_OF_REFRACTION return RefractionData.Data.x; #else return 1.0f; #endif } float GetMaterialRefractionPixelNormalStrength(in FMaterialRefractionData RefractionData) { #if REFRACTION_USE_PIXEL_NORMAL_OFFSET return RefractionData.Data.x; #else return 1.0f; #endif } float2 GetMaterialRefraction2DOffset(in FMaterialRefractionData RefractionData) { #if REFRACTION_USE_2D_OFFSET return RefractionData.Data.xy; #else return float2(0.0f, 0.0f); #endif } FMaterialRefractionData GetMaterialRefraction(FPixelMaterialInputs PixelMaterialInputs) { FMaterialRefractionData Data; Data.Data = PixelMaterialInputs.Refraction.xy; Data.RefractionDepthBias = PixelMaterialInputs.Refraction.z; return Data; } #if NUM_TEX_COORD_INTERPOLATORS SHADER_PUSH_WARNINGS_STATE SHADER_DISABLE_WARNINGS void GetMaterialCustomizedUVs(FMaterialVertexParameters Parameters, inout float2 OutTexCoords[NUM_TEX_COORD_INTERPOLATORS]) { %{get_material_customized_u_vs} } void GetCustomInterpolators(FMaterialVertexParameters Parameters, inout float2 OutTexCoords[NUM_TEX_COORD_INTERPOLATORS]) { %{get_custom_interpolators} } SHADER_POP_WARNINGS_STATE #endif float GetMaterialPixelDepthOffset(FPixelMaterialInputs PixelMaterialInputs) { return PixelMaterialInputs.PixelDepthOffset; } #if DECAL_PRIMITIVE float3 TransformTangentNormalToWorld(MaterialFloat3x3 TangentToWorld, float3 TangentNormal) { // To transform the normals use tranpose(Inverse(DecalToWorld)) = transpose(WorldToDecal) // But we want to only rotate the normals (we don't want to non-uniformaly scale them). // We assume the matrix is only a scale and rotation, and we remove non-uniform scale: // Pre-multiply by the inverse of the non-uniform scale in DecalToWorld float4 ScaledNormal = float4(-TangentNormal.z * DecalToWorldInvScale.x, TangentNormal.y * DecalToWorldInvScale.y, TangentNormal.x * DecalToWorldInvScale.z, 0.f); // Compute the normal return normalize(mul(ScaledNormal, DecalToWorld).xyz); } #else //DECAL_PRIMITIVE float3 TransformTangentNormalToWorld(MaterialFloat3x3 TangentToWorld, float3 TangentNormal) { return normalize(float3(TransformTangentVectorToWorld(TangentToWorld, TangentNormal))); } #endif //DECAL_PRIMITIVE float3 CalculateAnisotropyTangent(in out FMaterialPixelParameters Parameters, FPixelMaterialInputs PixelMaterialInputs) { float3 Normal = Parameters.WorldNormal; #if CLEAR_COAT_BOTTOM_NORMAL && (NUM_MATERIAL_OUTPUTS_CLEARCOATBOTTOMNORMAL > 0) Normal = ClearCoatBottomNormal0(Parameters); #if MATERIAL_TANGENTSPACENORMAL Normal = TransformTangentVectorToWorld(Parameters.TangentToWorld, Normal); #endif #endif float3 Tangent = GetMaterialTangent(PixelMaterialInputs); #if MATERIAL_TANGENTSPACENORMAL Tangent = TransformTangentNormalToWorld(Parameters.TangentToWorld, Tangent); #endif float3 BiTangent = cross(Normal, Tangent); Tangent = normalize(cross(BiTangent, Normal)); return Tangent; } float NormalCurvatureToRoughness(float3 WorldNormal, float3 dNdx, float3 dNdy) { float x = dot(dNdx, dNdx); float y = dot(dNdy, dNdy); float CurvatureApprox = pow(max(x, y), View.NormalCurvatureToRoughnessScaleBias.z); return saturate(CurvatureApprox * View.NormalCurvatureToRoughnessScaleBias.x + View.NormalCurvatureToRoughnessScaleBias.y); } float NormalCurvatureToRoughness(float3 WorldNormal) { return NormalCurvatureToRoughness(WorldNormal, ddx(WorldNormal), ddy(WorldNormal)); } float GetRoughnessFromNormalCurvature(FMaterialPixelParameters InMaterialParameters) { #if MATERIAL_NORMAL_CURVATURE_TO_ROUGHNESS // Curvature-to-roughness uses derivatives of the WorldVertexNormal, which is incompatible with centroid interpolation because // the samples are not uniformly distributed. Therefore we use WorldVertexNormal_Center which is guaranteed to be center interpolated. #if USE_WORLDVERTEXNORMAL_CENTER_INTERPOLATION #if USE_ANALYTIC_DERIVATIVES return NormalCurvatureToRoughness(InMaterialParameters.WorldVertexNormal_Center, InMaterialParameters.WorldGeoNormal_DDX, InMaterialParameters.WorldGeoNormal_DDY); #else return NormalCurvatureToRoughness(InMaterialParameters.WorldVertexNormal_Center); #endif #else #if USE_ANALYTIC_DERIVATIVES return NormalCurvatureToRoughness(InMaterialParameters.TangentToWorld[2].xyz, InMaterialParameters.WorldGeoNormal_DDX, InMaterialParameters.WorldGeoNormal_DDY); #else return NormalCurvatureToRoughness(InMaterialParameters.TangentToWorld[2].xyz); #endif #endif #else return 0.f; #endif } SHADER_PUSH_WARNINGS_STATE SHADER_DISABLE_WARNINGS // EvaluateMaterialAttributes %{evaluate_material_attributes} SHADER_POP_WARNINGS_STATE // We must to flip the normal of two sided SLW material with normal specified in world space. #define TWO_SIDED_WORLD_SPACE_SINGLELAYERWATER_NORMAL (MATERIAL_SHADINGMODEL_SINGLELAYERWATER && !MATERIAL_TANGENTSPACENORMAL && MATERIAL_TWOSIDED) // Be very, very careful about changing CalcPixelMaterialInputs() or CalcPixelMaterialInputsAnalyticDerivatives(). They apply the same basic calculation, // but CalcPixelMaterialInputs() uses finite difference derivates from hardware whereas CalcPixelMaterialInputsAnalyticDerivatives() uses analytic // derivatives. If you change anything, make sure that both functions stay identical. void CalcPixelMaterialInputs(in out FMaterialPixelParameters Parameters, in out FPixelMaterialInputs PixelMaterialInputs) { SHADER_PUSH_WARNINGS_STATE SHADER_DISABLE_WARNINGS // Initial calculations (required for Normal) %{calc_pixel_material_inputs_initial_calculations} // 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 %{calc_pixel_material_inputs_normal} SHADER_POP_WARNINGS_STATE #if TEMPLATE_USES_SUBSTRATE Parameters.SubstratePixelFootprint = SubstrateGetPixelFootprint(Parameters.WorldPosition_CamRelative, GetRoughnessFromNormalCurvature(Parameters)); Parameters.SharedLocalBases = SubstrateInitialiseSharedLocalBases(); Parameters.SubstrateTree = GetInitialisedSubstrateTree(); #if SUBSTRATE_USE_FULLYSIMPLIFIED_MATERIAL == 1 Parameters.SharedLocalBasesFullySimplified = SubstrateInitialiseSharedLocalBases(); Parameters.SubstrateTreeFullySimplified = GetInitialisedSubstrateTree(); #endif #endif // Note that here MaterialNormal can be in world space or tangent space float3 MaterialNormal = GetMaterialNormal(Parameters, PixelMaterialInputs); #if MATERIAL_TANGENTSPACENORMAL #if FEATURE_LEVEL >= FEATURE_LEVEL_SM4 // Mobile will rely on only the final normalize for performance MaterialNormal = normalize(MaterialNormal); #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 = TransformTangentNormalToWorld(Parameters.TangentToWorld, MaterialNormal); #else //MATERIAL_TANGENTSPACENORMAL Parameters.WorldNormal = normalize(MaterialNormal); #endif //MATERIAL_TANGENTSPACENORMAL #if MATERIAL_TANGENTSPACENORMAL || TWO_SIDED_WORLD_SPACE_SINGLELAYERWATER_NORMAL // flip the normal for backfaces being rendered with a two-sided material Parameters.WorldNormal *= Parameters.TwoSidedSign; #endif Parameters.ReflectionVector = ReflectionAboutCustomWorldNormal(Parameters, Parameters.WorldNormal, false); #if !PARTICLE_SPRITE_FACTORY Parameters.Particle.MotionBlurFade = 1.0f; #endif // !PARTICLE_SPRITE_FACTORY SHADER_PUSH_WARNINGS_STATE SHADER_DISABLE_WARNINGS // Now the rest of the inputs %{calc_pixel_material_inputs_other_inputs} SHADER_POP_WARNINGS_STATE #if MATERIAL_USES_ANISOTROPY Parameters.WorldTangent = CalculateAnisotropyTangent(Parameters, PixelMaterialInputs); #else Parameters.WorldTangent = 0; #endif } #undef DERIV_BASE_VALUE #if USE_ANALYTIC_DERIVATIVES #define DERIV_BASE_VALUE(_X) _X.Value void CalcPixelMaterialInputsAnalyticDerivatives(in out FMaterialPixelParameters Parameters, in out FPixelMaterialInputs PixelMaterialInputs) { SHADER_PUSH_WARNINGS_STATE SHADER_DISABLE_WARNINGS // Initial calculations (required for Normal) %{calc_pixel_material_inputs_analytic_derivatives_initial} // 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 %{calc_pixel_material_inputs_analytic_derivatives_normal} SHADER_POP_WARNINGS_STATE #if TEMPLATE_USES_SUBSTRATE Parameters.SubstratePixelFootprint = SubstrateGetPixelFootprint(Parameters.WorldPosition_CamRelative, GetRoughnessFromNormalCurvature(Parameters)); Parameters.SharedLocalBases = SubstrateInitialiseSharedLocalBases(); Parameters.SubstrateTree = GetInitialisedSubstrateTree(); #if SUBSTRATE_USE_FULLYSIMPLIFIED_MATERIAL == 1 Parameters.SharedLocalBasesFullySimplified = SubstrateInitialiseSharedLocalBases(); Parameters.SubstrateTreeFullySimplified = GetInitialisedSubstrateTree(); #endif #endif // Note that here MaterialNormal can be in world space or tangent space float3 MaterialNormal = GetMaterialNormal(Parameters, PixelMaterialInputs); #if MATERIAL_TANGENTSPACENORMAL #if FEATURE_LEVEL >= FEATURE_LEVEL_SM4 // Mobile will rely on only the final normalize for performance MaterialNormal = normalize(MaterialNormal); #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 = TransformTangentNormalToWorld(Parameters.TangentToWorld, MaterialNormal); #else //MATERIAL_TANGENTSPACENORMAL Parameters.WorldNormal = normalize(MaterialNormal); #endif //MATERIAL_TANGENTSPACENORMAL #if MATERIAL_TANGENTSPACENORMAL || TWO_SIDED_WORLD_SPACE_SINGLELAYERWATER_NORMAL // flip the normal for backfaces being rendered with a two-sided material Parameters.WorldNormal *= Parameters.TwoSidedSign; #endif Parameters.ReflectionVector = ReflectionAboutCustomWorldNormal(Parameters, Parameters.WorldNormal, false); #if !PARTICLE_SPRITE_FACTORY Parameters.Particle.MotionBlurFade = 1.0f; #endif // !PARTICLE_SPRITE_FACTORY SHADER_PUSH_WARNINGS_STATE SHADER_DISABLE_WARNINGS // Now the rest of the inputs %{calc_pixel_material_inputs_analytic_derivatives_other_inputs} SHADER_POP_WARNINGS_STATE #if MATERIAL_USES_ANISOTROPY Parameters.WorldTangent = CalculateAnisotropyTangent(Parameters, PixelMaterialInputs); #else Parameters.WorldTangent = 0; #endif } #undef DERIV_BASE_VALUE #endif // 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 %{line_number} void ClipLODTransition(float2 SvPosition, float DitherFactor) { if (abs(DitherFactor) > .001) { float ArgCos = dot(floor(SvPosition.xy), float2(347.83451793, 3343.28371963)); #if FEATURE_LEVEL <= FEATURE_LEVEL_ES3_1 // Temporary workaround for precision issues on mobile when the argument is bigger than 10k ArgCos = fmod(ArgCos, 10000); #endif float RandCos = cos(ArgCos); 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_INSTANCE_CULLING) && USE_DITHERED_LOD_TRANSITION) #if (USE_INSTANCING || USE_INSTANCE_CULLING) && USE_DITHERED_LOD_TRANSITION void ClipLODTransition(FMaterialPixelParameters Parameters) { ClipLODTransition(Parameters, Parameters.PerInstanceParams.z); } #elif USE_DITHERED_LOD_TRANSITION && !USE_STENCIL_LOD_DITHER void ClipLODTransition(FMaterialPixelParameters Parameters) { if (PrimitiveDither.LODFactor != 0.0) { ClipLODTransition(Parameters, PrimitiveDither.LODFactor); } } void ClipLODTransition(float2 SvPosition) { if (PrimitiveDither.LODFactor != 0.0) { ClipLODTransition(SvPosition, PrimitiveDither.LODFactor); } } #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 TRANSLUCENT_SHADOW_WITH_MASKED_OPACITY clip(GetMaterialMaskedOpacity(PixelMaterialInputs)); #elif MATERIALBLENDING_TRANSLUCENT clip(GetMaterialOpacity(PixelMaterialInputs) - 1.0f / 255.0f); #endif } #if MATERIAL_DITHER_OPACITY_MASK float DitheredOpacityMaskToOpacity(float Mask) { // This represents the expected value of the function GetMaterialCoverageAndClipping // which randomly dithers the fragment on or off to produce the effect of opacity // The expected value of this dithering can be computed as: // Simplify[Integrate[If[Mask + Dither - 1/2 < 0, 0, 1], {Dither, 0, 1}]] // which is just: return saturate(Mask + 0.5); } #endif void GetMaterialCoverageAndClipping(FMaterialPixelParameters Parameters, FPixelMaterialInputs PixelMaterialInputs) { ClipLODTransition(Parameters); #if MATERIALBLENDING_MASKED && !SINGLE_LAYER_WATER_NO_DISCARD #if MATERIAL_DITHER_OPACITY_MASK #if 0 /* 5 value dither. Every value present in + 012 234 401 */ float2 Pos = Parameters.SvPosition.xy; float2 DepthGrad = { ddx( Parameters.SvPosition.z ), ddy( Parameters.SvPosition.z ) }; //Pos = floor( Pos + DepthGrad * float2( 4093, 3571 ) ); float Dither5 = frac( ( Pos.x + Pos.y * 2 - 1.5 + ResolvedView.TemporalAAParams.x ) / 5 ); float Noise = frac( dot( float2( 171.0, 231.0 ) / 71, Pos.xy ) ); float Dither = ( Dither5 * 5 + Noise ) * (1.0 / 6.0); #else // New dithering based on Blue noise float Noise2 = ViewScalarBlueNoise(Parameters.SvPosition.xy, View.StateFrameIndex); const float LegacyAdjustement = 0.83f; // This is required to get the same look and empty areas as the legacy one. float Dither = LegacyAdjustement * Noise2; #endif clip( GetMaterialMask(PixelMaterialInputs) + Dither - 0.5 ); #else clip(GetMaterialMask(PixelMaterialInputs)); #endif #endif } // The material blending mode alone is not enough information. We also need to know if the material is ThinTranslucent because in this case we should never clip. // Indeed, ThinTranslucent surfaces always have a coverage of 1 and opacity represent the coverage of the opaque lit material sitting on top of the translucent one. void GetMaterialClippingVelocity(FMaterialPixelParameters Parameters, FPixelMaterialInputs PixelMaterialInputs, bool bIsThinTranslucent, float MaterialOpacity) { #if MATERIALBLENDING_TRANSLUCENT || MATERIALBLENDING_ADDITIVE || MATERIALBLENDING_MODULATE || SUBSTRATE_TRANSLUCENT_MATERIAL ClipLODTransition(Parameters); clip(bIsThinTranslucent ? 1.0f : MaterialOpacity - 1.0 / 255.0 - GetMaterialOpacityMaskClipValue()); #else GetMaterialCoverageAndClipping(Parameters, PixelMaterialInputs); #endif } #define MATERIALBLENDING_MASKED_USING_COVERAGE (FORWARD_SHADING && MATERIALBLENDING_MASKED && SUPPORTS_PIXEL_COVERAGE && MATERIAL_USE_ALPHA_TO_COVERAGE) #if MATERIALBLENDING_MASKED_USING_COVERAGE uint GetDerivativeCoverageFromMask(float MaterialMask) { uint Coverage = 0x0; // MSAA sampler mask support up to 8X if (MaterialMask > 0.010) Coverage = 0x08; // 0000 1000 if (MaterialMask > 0.125) Coverage = 0x18; // 0001 1000 if (MaterialMask > 0.250) Coverage = 0x19; // 0001 1001 if (MaterialMask > 0.375) Coverage = 0x39; // 0011 1001 if (MaterialMask > 0.500) Coverage = 0x3D; // 0011 1101 if (MaterialMask > 0.625) Coverage = 0x7D; // 0111 1101 if (MaterialMask > 0.750) Coverage = 0x7F; // 0111 1111 if (MaterialMask > 0.875) Coverage = 0xFF; // 1111 1111 return Coverage; } // Returns the new pixel coverage according the material's mask and the current pixel's mask. uint DiscardMaterialWithPixelCoverage(FMaterialPixelParameters MaterialParameters, FPixelMaterialInputs PixelMaterialInputs) { ClipLODTransition(MaterialParameters); float OriginalMask = GetMaterialMaskInputRaw(PixelMaterialInputs); float MaskClip = GetMaterialOpacityMaskClipValue(); if (ResolvedView.NumSceneColorMSAASamples > 1) { float Mask = (OriginalMask - MaskClip) / (1.0 - MaskClip); uint CurrentPixelCoverage = GetDerivativeCoverageFromMask(Mask); // Discard pixel shader if all sample are masked to avoid computing other material inputs. clip(float(CurrentPixelCoverage) - 0.5); return CurrentPixelCoverage; } clip(OriginalMask - MaskClip); return 0xF; } #endif // MATERIALBLENDING_MASKED_USING_COVERAGE #define FrontFaceSemantic SV_IsFrontFace #define FIsFrontFace bool half GetFloatFacingSign(FIsFrontFace bIsFrontFace) { #if COMPILER_DXC && (COMPILER_VULKAN || COMPILER_GLSL_ES3_1) && (!RAYHITGROUPSHADER) && (!IS_NANITE_PASS) // We need to flip SV_IsFrontFace for Vulkan when compiling with DXC due to different coordinate systems. // HLSLcc did that by flipping SV_IsFrontFace in the high-level GLSL output. // NOTE: Raytracing and Nanite don't need an SV_IsFrontFace workaround. return bIsFrontFace ? -1 : +1; #else return bIsFrontFace ? +1 : -1; #endif } #define OPTIONAL_IsFrontFace , in FIsFrontFace bIsFrontFace : FrontFaceSemantic #ifndef VF_SUPPORTS_WORLD_POSITION_SHADER_OFFSETS #define VF_SUPPORTS_WORLD_POSITION_SHADER_OFFSETS 1 #endif // Helper macro to globally ignore requests for non-offset world positions in materials when lower than shader model 4. We do this // because we are using an extra interpolator for this second world position, and in < ES31 there may not be enough // NOTE: Forced on in development shaders because the Out-Of-Bounds Pixels visualizer uses it #define USE_WORLD_POSITION_EXCLUDING_SHADER_OFFSETS ((NEEDS_WORLD_POSITION_EXCLUDING_SHADER_OFFSETS || USE_DEVELOPMENT_SHADERS) && VF_SUPPORTS_WORLD_POSITION_SHADER_OFFSETS) /** Initializes the subset of Parameters that was not set in GetMaterialPixelParameters. */ void CalcMaterialParametersEx( in out FMaterialPixelParameters Parameters, in out FPixelMaterialInputs PixelMaterialInputs, float4 SvPosition, float4 ScreenPosition, FIsFrontFace bIsFrontFace, float3 TranslatedWorldPosition, float3 TranslatedWorldPositionExcludingShaderOffsets) { Parameters.WorldPosition_CamRelative = TranslatedWorldPosition; Parameters.WorldPosition_NoOffsets_CamRelative = TranslatedWorldPositionExcludingShaderOffsets; Parameters.LWCData = MakeMaterialLWCData(Parameters); #if WSVECTOR_IS_TILEOFFSET Parameters.AbsoluteWorldPosition = DFMultiplyLHSAndFastTwoSum(Parameters.LWCData.AbsoluteWorldPosition.Tile, UE_LWC_RENDER_TILE_SIZE, Parameters.LWCData.AbsoluteWorldPosition.Offset); #elif WSVECTOR_IS_DOUBLEFLOAT Parameters.AbsoluteWorldPosition = Parameters.LWCData.AbsoluteWorldPosition; #endif // If the material uses any non-offset world position expressions, calculate those parameters. If not, // the variables will have been initialised to 0 earlier. #if USE_WORLD_POSITION_EXCLUDING_SHADER_OFFSETS #if WSVECTOR_IS_TILEOFFSET Parameters.WorldPosition_NoOffsets = DFMultiplyLHSAndFastTwoSum(Parameters.LWCData.WorldPosition_NoOffsets.Tile, UE_LWC_RENDER_TILE_SIZE, Parameters.LWCData.WorldPosition_NoOffsets.Offset); #elif WSVECTOR_IS_DOUBLEFLOAT Parameters.WorldPosition_NoOffsets = Parameters.LWCData.WorldPosition_NoOffsets; #endif #endif Parameters.SvPosition = SvPosition; Parameters.ScreenPosition = ScreenPosition; Parameters.ViewBufferUV = ScreenPositionToBufferUV(ScreenPosition); // CameraVector is a normalised vector representing the "from surface to camera" direction. #if RAYHITGROUPSHADER Parameters.CameraVector = -WorldRayDirection(); #else Parameters.CameraVector = select(IsOrthoProjection(ResolvedView), -ResolvedView.ViewForward, normalize(-Parameters.WorldPosition_CamRelative.xyz)); #endif Parameters.LightVector = 0; #if IS_NANITE_PASS // Nanite does not support OPTIONAL_IsFrontFace, so Nanite sets the following to 1.0f or -1.0f in // GetMaterialPixelParameters - here we pull it out into our own front facing bool. const bool bNaniteIsFrontFace = Parameters.TwoSidedSign > 0.0f; #endif Parameters.TwoSidedSign = 1.0f; #if MATERIAL_TWOSIDED && HAS_PRIMITIVE_UNIFORM_BUFFER // #dxr: DirectX Raytracing's HitKind() intrinsic already accounts for negative scaling #if !RAYHITGROUPSHADER Parameters.TwoSidedSign *= ResolvedView.CullingSign * GetPrimitive_DeterminantSign(Parameters.PrimitiveId); #endif #endif #if MATERIAL_TWOSIDED || RAYHITGROUPSHADER // Either we have a two-sided material that needs a sign flip, or we have a ray tracing material // that needs to consider rays arriving from either side #if IS_NANITE_PASS Parameters.TwoSidedSign *= GetFloatFacingSign(bNaniteIsFrontFace); #else Parameters.TwoSidedSign *= GetFloatFacingSign(bIsFrontFace); #endif #endif #if MATERIAL_VIRTUALTEXTURE_FEEDBACK || LIGHTMAP_VT_ENABLED InitializeVirtualTextureFeedback(Parameters.VirtualTextureFeedback); #endif #if USE_ANALYTIC_DERIVATIVES if(!TEXTURE_SAMPLE_DEBUG || View.GeneralPurposeTweak >= 1.0f) CalcPixelMaterialInputsAnalyticDerivatives(Parameters, PixelMaterialInputs); else #endif { CalcPixelMaterialInputs(Parameters, PixelMaterialInputs); } } // convenience function to setup CalcMaterialParameters assuming we don't support TranslatedWorldPositionExcludingShaderOffsets // @param SvPosition from SV_Position when rendering the view, for other projections e.g. shadowmaps this function cannot be used and you need to call CalcMaterialParametersEx() 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); #if POST_PROCESS_MATERIAL_BEFORE_TONEMAP && !POST_PROCESS_MATERIAL_SSRINPUT // In this case, SVPositionToTranslatedWorld account for the correct dynamic resolution scaling. float3 TranslatedWorldPosition = SvPositionToTranslatedWorld(SvPosition); #else // Otherwise, we must update SVPosition to account for the resolution scaling not happening after at the tonemap stage and after. float3 TranslatedWorldPosition = SvPositionToTranslatedWorld(SvPosition * float4(View.ResolutionFractionAndInv.xx, 1.0f, 1.0f)); #endif CalcMaterialParametersEx(Parameters, PixelMaterialInputs, SvPosition, ScreenPosition, bIsFrontFace, TranslatedWorldPosition, TranslatedWorldPosition); } /** Assemble the transform from tangent space into world space */ half3x3 AssembleTangentToWorld( half3 TangentToWorld0, half4 TangentToWorld2 ) { // Will not be orthonormal after interpolation. This perfectly matches xNormal. // 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 && !IS_NANITE_PASS && ((MATERIALBLENDING_SOLID || MATERIALBLENDING_MASKED) || (TRANSLUCENT_WRITING_VELOCITY))) // 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) || COMPILER_PSSL || (COMPILER_METAL && FEATURE_LEVEL >= FEATURE_LEVEL_SM5) || SWITCH_PROFILE || SWITCH_PROFILE_FORWARD) #define USE_CONSERVATIVE_DEPTH_WRITES (OUTPUT_PIXEL_DEPTH_OFFSET && SUPPORTS_CONSERVATIVE_DEPTH_WRITES) #if USE_CONSERVATIVE_DEPTH_WRITES #if COMPILER_HLSL || COMPILER_METAL_SHADER_CONVERTER // Note: for some reason using SV_DepthLessEqual without these interpolation modifiers causes a compile error in d3d #define INPUT_POSITION_QUALIFIERS linear noperspective centroid // Use conservative depth output so we still get Z Cull. Note, this is a reversed Z depth surface. #define DEPTH_WRITE_SEMANTIC SV_DepthLessEqual #elif COMPILER_METAL #define INPUT_POSITION_QUALIFIERS #define DEPTH_WRITE_SEMANTIC SV_DepthLessEqual #elif COMPILER_PSSL #define INPUT_POSITION_QUALIFIERS #define DEPTH_WRITE_SEMANTIC S_DEPTH_LE_OUTPUT #elif SWITCH_PROFILE || SWITCH_PROFILE_FORWARD #define INPUT_POSITION_QUALIFIERS #define DEPTH_WRITE_SEMANTIC SV_DepthLessEqual #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 // Must match EPixelDepthOffsetMode from EngineTypes.h #define PODM_LEGACY 0 #define PODM_CAMERAVECTOR 1 float ApplyPixelDepthOffsetToMaterialParameters(inout FMaterialPixelParameters MaterialParameters, FPixelMaterialInputs PixelMaterialInputs, out float OutDepth) { float PixelDepthOffset = GetMaterialPixelDepthOffset(PixelMaterialInputs); // SvPosition.z contains device depth value normally written to depth buffer // ScreenPosition.z is 'SvPosition.z * SvPosition.w' // So here we compute a new device depth value with the given pixel depth offset, but clamp the value against the regular SvPosition.z // This clamp is important, even if PixelDepthOffset is 0.0f, the computed DeviceDepth may end up 'slightly' larger than SvPosition.z due to floating point whatever // Since we are outputing depth with SV_DepthLessEqual, this ends up as undefined behavior // In particular, this can cause problems on PS4....PS4 enables RE_Z when using depth output along with virtual texture UAV feedback buffer writes // RE_Z causes the HW to perform depth test twice, once before executing pixel shader, and once after // The PreZ pass will write depth buffer using depth offset, then the base pass will test against this value using both modified and unmodifed depth // If the unmodified depth is ever slightly less than the modified depth, the initial depth test will fail, which results in z-fighting/flickering type artifacts float DeviceDepth; if(IsOrthoProjection(ResolvedView)) { //Ortho projections have PDO applied to z rather than w. w should always stay as 1.0f float OriginalDeviceZ = MaterialParameters.SvPosition.z; float SceneDepth = ConvertFromDeviceZ(OriginalDeviceZ); DeviceDepth = max(SceneDepth + PixelDepthOffset, SceneDepth); PixelDepthOffset = SceneDepth - DeviceDepth; MaterialParameters.ScreenPosition.z = ConvertToDeviceZ(SceneDepth + PixelDepthOffset); MaterialParameters.SvPosition.z = MaterialParameters.ScreenPosition.z; DeviceDepth = ConvertToDeviceZ(DeviceDepth); PixelDepthOffset = MaterialParameters.SvPosition.z - OriginalDeviceZ; // In this Ortho case, CameraVector is always ViewForward. MaterialParameters.LWCData.AbsoluteWorldPosition = WSAdd(MaterialParameters.LWCData.AbsoluteWorldPosition, -MaterialParameters.CameraVector * PixelDepthOffset); } else { // We clamp PixelDepthOffset to 0 in order to not move the pixel towards the camera. const float3 CameraPixelForwardVector = -MaterialParameters.CameraVector; #if PIXEL_DEPTH_OFFSET_MODE == PODM_LEGACY // The legacy behavior applying a world space offset independently from the perspective projection. const float3 AbsoluteWorldOffsetVector = CameraPixelForwardVector * max(0.0, PixelDepthOffset); #elif PIXEL_DEPTH_OFFSET_MODE == PODM_CAMERAVECTOR // The change of depth will be less at the edge of the screen due to the perspective. const float PixelDepthOffetForPerspective = PixelDepthOffset * dot(CameraPixelForwardVector, ResolvedView.ViewForward); // This is the world space vector to offset the pixel world space position by according to the perspsective. // It clamps the offset to 0 in order to only push the pixel foward and match the depth clamping math below. const float3 AbsoluteWorldOffsetVector = CameraPixelForwardVector * max(0.0, PixelDepthOffetForPerspective); // Update the new pixel depth offset accounting for perspective projection. PixelDepthOffset = PixelDepthOffetForPerspective; #endif // Compute the new DeviceDepth accounting for the PixelDepthOffset. DeviceDepth = min(MaterialParameters.ScreenPosition.z / (MaterialParameters.ScreenPosition.w + PixelDepthOffset), MaterialParameters.SvPosition.z); // Once we've computed our (clamped) device depth, recompute PixelDepthOffset again to take the potential clamp into account PixelDepthOffset = (MaterialParameters.ScreenPosition.z - DeviceDepth * MaterialParameters.ScreenPosition.w) / DeviceDepth; // Update positions used for shading MaterialParameters.ScreenPosition.w += PixelDepthOffset; MaterialParameters.SvPosition.w = MaterialParameters.ScreenPosition.w; MaterialParameters.LWCData.AbsoluteWorldPosition = WSAdd(MaterialParameters.LWCData.AbsoluteWorldPosition, AbsoluteWorldOffsetVector); } MaterialParameters.AbsoluteWorldPosition = WSToDF(MaterialParameters.LWCData.AbsoluteWorldPosition); OutDepth = INVARIANT(DeviceDepth); return PixelDepthOffset; } float3 GetWorldBentNormalZero(in FMaterialPixelParameters MaterialParameters) { #if NUM_MATERIAL_OUTPUTS_GETBENTNORMAL > 0 #if MATERIAL_TANGENTSPACENORMAL return normalize(TransformTangentVectorToWorld(MaterialParameters.TangentToWorld, GetBentNormal0(MaterialParameters))); #else return GetBentNormal0(MaterialParameters); #endif #else return MaterialParameters.WorldNormal; #endif } // Return the Iris & Iris plane normal, used for eye shading model void GetEyeNormals( float IrisMask, float IrisDistance, in float3 InNormal, in float3 InClearCoatNormal, in float3 InCustomTangent, inout float3 OutIrisNormal, inout float3 OutIrisPlaneNormal) { #if IRIS_NORMAL #if NUM_MATERIAL_OUTPUTS_CLEARCOATBOTTOMNORMAL > 0 || SUBSTRATE_LEGACY_IRIS_NORMAL > 0 OutIrisNormal = normalize(InClearCoatNormal); #else OutIrisNormal = InNormal; #endif #if NUM_MATERIAL_OUTPUTS_GETTANGENTOUTPUT > 0 || SUBSTRATE_LEGACY_IRIS_TANGENT > 0 OutIrisPlaneNormal = normalize(InCustomTangent); #else OutIrisPlaneNormal = OutIrisNormal; #endif #else #if NUM_MATERIAL_OUTPUTS_GETTANGENTOUTPUT > 0 OutIrisNormal = normalize(InCustomTangent); // Weird that the tangent ends up in the normal, but that was the legacy behavior. OutIrisPlaneNormal = OutIrisNormal; #else OutIrisNormal = InNormal; OutIrisPlaneNormal = InNormal; #endif #endif }