// Copyright Epic Games, Inc. All Rights Reserved. #pragma once #include "/Engine/Private/Common.ush" #include "/Engine/Private/Landscape/LandscapeCommon.ush" #if defined (__INTELLISENSE__) // Uncomment the appropriate define for enabling syntax highlighting with HLSL Tools for Visual Studio : //#define APPLY_HEIGHT_PATCH 1 //#define OFFSET_HEIGHT_PATCH 1 //#define SIMPLE_TEXTURE_COPY 1 //#define CONVERT_TO_NATIVE_LANDSCAPE_PATCH 1 //#define CONVERT_BACK_FROM_NATIVE_LANDSCAPE_PATCH 1 //#define APPLY_WEIGHT_PATCH 1 //#define REINITIALIZE_PATCH 1 #endif // defined (__INTELLISENSE__) #if APPLY_HEIGHT_PATCH || APPLY_WEIGHT_PATCH // Given the falloff settings, gives the alpha to use for a pixel at the given patch UV coordinates. float GetFalloffAlpha(float FalloffWorldMargin, float2 PatchWorldDimensions, float2 PatchUVCoordinates, float2 EdgeUVDeadBorder, bool bRectangularFalloff) { static const float HALF_PI = 3.14159265f / 2; static const float FLOAT_TOLERANCE = 0.0001; // The falloff starts at the edges and goes inward, and shouldn't be higher than 1/2 of the smaller dimension, or // else it extends past the center. If it does extend past the center, we'll decrease the max alpha in the center // (imagine shaving off a cube with progressively flatter slopes- once you get past the center of the top, you get // a progressively shorter pyramid). Maybe we shouldn't even bother dealing with this case, but it's easy enough to do. float ClampedFalloffMargin = clamp(FalloffWorldMargin, 0, min(PatchWorldDimensions.x, PatchWorldDimensions.y) / 2); float bFalloffAlpha = FalloffWorldMargin > 0 ? ClampedFalloffMargin / FalloffWorldMargin : 1; // Alpha T is a parameter that we'll pass through a cos^2 function to get the alpha (to have it be smoother). It should be // 0 at the places where there is no falloff (ie alpha is 1), and go to 1 at places where falloff is complete (and falloff is 0) float AlphaT = 1; if (bRectangularFalloff) { float2 DistancesFromEdges = PatchWorldDimensions * min(PatchUVCoordinates - EdgeUVDeadBorder, 1 - EdgeUVDeadBorder - PatchUVCoordinates); bool bIsInsidePatch = all(DistancesFromEdges >= -FLOAT_TOLERANCE); AlphaT = bIsInsidePatch ? 0 : 1; if (bIsInsidePatch && ClampedFalloffMargin > 0) { // Interpret distances as proportions of falloff margin DistancesFromEdges = DistancesFromEdges / ClampedFalloffMargin; if (DistancesFromEdges.x > 0 && DistancesFromEdges.x < 1.0 && DistancesFromEdges.y > 0 && DistancesFromEdges.y < 1.0) { AlphaT = min(1.0, length(float2(1, 1) - DistancesFromEdges)); } else { AlphaT = 1 - min(1.0, min(DistancesFromEdges.x, DistancesFromEdges.y)); } } } else { float OuterRadius = min(PatchWorldDimensions.x, PatchWorldDimensions.y) / 2; float DistanceFromCenter = length(PatchWorldDimensions * (PatchUVCoordinates - float2(0.5, 0.5))); float DistanceFromOuterCircle = OuterRadius - DistanceFromCenter; bool bIsInsideCircle = DistanceFromOuterCircle > 0; AlphaT = bIsInsideCircle ? 0 : 1; if (bIsInsideCircle && ClampedFalloffMargin > 0) { AlphaT = 1 - DistanceFromOuterCircle / ClampedFalloffMargin; AlphaT = max(0, AlphaT); // clamp to 0 so we don't go past ClampedFalloffMargin } } float Alpha = cos(AlphaT * HALF_PI); Alpha = Alpha * Alpha * bFalloffAlpha; return Alpha; } #endif // APPLY_HEIGHT_PATCH || APPLY_WEIGHT_PATCH #if APPLY_HEIGHT_PATCH Texture2D InSourceHeightmap; uint2 InSourceHeightmapOffset; Texture2D InHeightPatch; SamplerState InHeightPatchSampler; float4x4 InHeightmapToPatch; float2 InPatchWorldDimensions; float2 InEdgeUVDeadBorder; float InFalloffWorldMargin; // Value that corresponds to 0 (i.e. to LANDSCAPE_MID_VALUE) in the patch data float InZeroInEncoding; // Scale to apply to patch values relative to the value representing zero float InHeightScale; // An offset to apply to the resulting height, after applying the height scale float InHeightOffset; // Enum defining blend modes, with values set in corresponding cpp file. uint InBlendMode; // A combination of flags, whose positions are set in the corresponding cpp file. uint InFlags; uint ConvertPatchBlendModeToHeightAlphaFlags() { switch (InBlendMode) { case ADDITIVE_MODE: return EHEIGHTMAPALPHAFLAGS_ADDITIVE; case ALPHA_BLEND_MODE: return (EHEIGHTMAPALPHAFLAGS_MIN | EHEIGHTMAPALPHAFLAGS_MAX); case MIN_MODE: return EHEIGHTMAPALPHAFLAGS_MIN; case MAX_MODE: return EHEIGHTMAPALPHAFLAGS_MAX; default: return EHEIGHTMAPALPHAFLAGS_NONE; } } void ApplyLandscapeTextureHeightPatch(in float4 SVPos : SV_POSITION, out float4 OutColor : SV_Target0) { // Unpack our flags. The flag choices are set in FApplyLandscapeTextureHeightPatchPS::ModifyCompilationEnvironment // so that they match to the cpp file bool bRectangularFalloff = InFlags & RECTANGULAR_FALLOFF_FLAG; bool bApplyPatchAlpha = InFlags & APPLY_PATCH_ALPHA_FLAG; bool bInputIsPackedHeight = InFlags & INPUT_IS_PACKED_HEIGHT_FLAG; // We need only the 2D affine transformation that goes from landscape XY heightmap integer coordinates // to patch UV coordinates. float2x2 HeightmapToPatchRotateScale = (float2x2) InHeightmapToPatch; float2 HeightmapToPatchTranslate = InHeightmapToPatch._m03_m13; float2 PatchUVCoordinates = mul(HeightmapToPatchRotateScale, SVPos.xy) + HeightmapToPatchTranslate; float4 PatchSampledValue = InHeightPatch.Sample(InHeightPatchSampler, PatchUVCoordinates); float PatchStoredHeight = bInputIsPackedHeight ? UnpackHeight(PatchSampledValue.xy) : PatchSampledValue.x; float PatchSignedHeight = InHeightScale * (PatchStoredHeight - InZeroInEncoding) + InHeightOffset; #if PERFORM_BLENDING int2 HeightmapCoordinates = floor(SVPos.xy) - InSourceHeightmapOffset; float4 CurrentPackedHeight = InSourceHeightmap.Load(int3(HeightmapCoordinates, 0)); float CurrentHeight = UnpackHeight(CurrentPackedHeight.xy); #endif // PERFORM_BLENDING float Alpha = GetFalloffAlpha(InFalloffWorldMargin, InPatchWorldDimensions, PatchUVCoordinates, InEdgeUVDeadBorder, bRectangularFalloff); if (bApplyPatchAlpha) { Alpha *= PatchSampledValue.a; } // TODO [jonathan.bard] Deprecate : #if PERFORM_BLENDING float NewHeight = 0; switch (InBlendMode) { case ADDITIVE_MODE: NewHeight = CurrentHeight + Alpha * PatchSignedHeight; break; case ALPHA_BLEND_MODE: NewHeight = lerp(CurrentHeight, LANDSCAPE_MID_VALUE + PatchSignedHeight, Alpha); break; case MIN_MODE: NewHeight = lerp(CurrentHeight, min(CurrentHeight, LANDSCAPE_MID_VALUE + PatchSignedHeight), Alpha); break; case MAX_MODE: NewHeight = lerp(CurrentHeight, max(CurrentHeight, LANDSCAPE_MID_VALUE + PatchSignedHeight), Alpha); break; } OutColor = float4(PackHeight(NewHeight), PackHeightAlpha(1.0f, EHEIGHTMAPALPHAFLAGS_NONE)); #else // PERFORM_BLENDING float OutputHeight = (InBlendMode == ADDITIVE_MODE) ? PatchSignedHeight : (LANDSCAPE_MID_VALUE + PatchSignedHeight); OutColor = float4(PackHeight(OutputHeight), PackHeightAlpha(Alpha, ConvertPatchBlendModeToHeightAlphaFlags())); #endif // !PERFORM_BLENDING } #endif // APPLY_HEIGHT_PATCH #if OFFSET_HEIGHT_PATCH Texture2D InHeightmap; float InHeightOffset; void ApplyOffsetToHeightmap(in float4 SVPos : SV_POSITION, out float4 OutColor : SV_Target0) { float4 StoredVector = InHeightmap.Load(int3(SVPos.xy, 0)); float StoredHeight = UnpackHeight(StoredVector.xy); OutColor = float4(PackHeight(StoredHeight + InHeightOffset), StoredVector.z, StoredVector.w); } #endif // OFFSET_HEIGHT_PATCH #if CONVERT_TO_NATIVE_LANDSCAPE_PATCH Texture2D InHeightmap; float InZeroInEncoding; float InHeightScale; float InHeightOffset; void ConvertToNativeLandscapePatch(in float4 SVPos : SV_POSITION, out float4 OutColor : SV_Target0) { float4 StoredVector = InHeightmap.Load(int3(SVPos.xy, 0)); float StoredHeight = StoredVector.x; // Apply the same kinds of transformations that we would apply for alpha blend mode in ApplyLandscapeTextureHeightPatch float InterpretedHeight = InHeightScale * (StoredHeight - InZeroInEncoding) + InHeightOffset + LANDSCAPE_MID_VALUE; OutColor = float4(PackHeight(InterpretedHeight), 0, StoredVector.w); } #endif // CONVERT_TO_NATIVE_LANDSCAPE_PATCH #if CONVERT_BACK_FROM_NATIVE_LANDSCAPE_PATCH Texture2D InHeightmap; float InZeroInEncoding; float InHeightScale; float InHeightOffset; void ConvertBackFromNativeLandscapePatch(in float4 SVPos : SV_POSITION, out float4 OutColor : SV_Target0) { float4 StoredVector = InHeightmap.Load(int3(SVPos.xy, 0)); float StoredHeight = UnpackHeight(StoredVector.xy); // Undo the transformation we do in ConvertToNativeLandscapePatch float PatchStoredHeight = InHeightScale ? (StoredHeight - LANDSCAPE_MID_VALUE - InHeightOffset) / InHeightScale + InZeroInEncoding : InZeroInEncoding; OutColor = float4(PatchStoredHeight, 0, 0, StoredVector.w); } #endif // CONVERT_BACK_FROM_NATIVE_LANDSCAPE_PATCH #if APPLY_WEIGHT_PATCH Texture2D InSourceWeightmap; uint2 InSourceWeightmapCoordOffset; Texture2D InWeightPatch; SamplerState InWeightPatchSampler; float4x4 InWeightmapToPatch; float2 InPatchWorldDimensions; float2 InEdgeUVDeadBorder; float InFalloffWorldMargin; // Enum defining blend modes, with values set in corresponding cpp file. uint InBlendMode; // A combination of flags, whose positions are set in the corresponding cpp file. uint InFlags; uint ConvertPatchBlendModeToWeightAlphaFlags() { switch (InBlendMode) { case ADDITIVE_MODE: return EWEIGHTMAPALPHAFLAGS_ADDITIVE; case ALPHA_BLEND_MODE: return (EWEIGHTMAPALPHAFLAGS_MIN | EWEIGHTMAPALPHAFLAGS_MAX); case MIN_MODE: return EWEIGHTMAPALPHAFLAGS_MIN; case MAX_MODE: return EWEIGHTMAPALPHAFLAGS_MAX; default: return EWEIGHTMAPALPHAFLAGS_NONE; } } void ApplyLandscapeTextureWeightPatch(in float4 SVPos : SV_POSITION, out float4 OutColor : SV_Target0) { // Unpack our flags. The flag choices are set in FApplyLandscapeTextureWeightPatchPS::ModifyCompilationEnvironment // so that they match to the cpp file bool bRectangularFalloff = InFlags & RECTANGULAR_FALLOFF_FLAG; bool bApplyPatchAlpha = InFlags & APPLY_PATCH_ALPHA_FLAG; // We need only the 2D affine transformation that goes from landscape weightmap integer coordinates // to patch UV coordinates. float2x2 WeightmapToPatchRotateScale = (float2x2) InWeightmapToPatch; float2 WeightmapToPatchTranslate = InWeightmapToPatch._m03_m13; float2 PatchUVCoordinates = mul(WeightmapToPatchRotateScale, SVPos.xy) + WeightmapToPatchTranslate; float4 PatchSampledValue = InWeightPatch.Sample(InWeightPatchSampler, PatchUVCoordinates); float PatchWeight = PatchSampledValue.x; #if PERFORM_BLENDING int2 SourceWeightmapCoordinates = floor(SVPos.xy) - InSourceWeightmapCoordOffset; float CurrentWeight = InSourceWeightmap.Load(int3(SourceWeightmapCoordinates, 0)).x; #endif // PERFORM_BLENDING float Alpha = GetFalloffAlpha(InFalloffWorldMargin, InPatchWorldDimensions, PatchUVCoordinates, InEdgeUVDeadBorder, bRectangularFalloff); if (bApplyPatchAlpha) { Alpha *= PatchSampledValue.a; } // TODO [jonathan.bard] Deprecate : #if PERFORM_BLENDING float NewWeight = 0; switch (InBlendMode) { case ADDITIVE_MODE: NewWeight = clamp(CurrentWeight + Alpha * PatchWeight, 0, 1); break; case ALPHA_BLEND_MODE: NewWeight = lerp(CurrentWeight, PatchWeight, Alpha); break; case MIN_MODE: NewWeight = lerp(CurrentWeight, min(CurrentWeight, PatchWeight), Alpha); break; case MAX_MODE: NewWeight = lerp(CurrentWeight, max(CurrentWeight, PatchWeight), Alpha); break; } OutColor = float4(PackWeight(NewWeight), PackWeightAlpha(1.0f, EWEIGHTMAPALPHAFLAGS_NONE)); #else // PERFORM_BLENDING OutColor = float4(PackWeight(PatchWeight), PackWeightAlpha(Alpha, ConvertPatchBlendModeToWeightAlphaFlags())); #endif // !PERFORM_BLENDING } #endif // APPLY_WEIGHT_PATCH #if REINITIALIZE_PATCH Texture2D InSource; SamplerState InSourceSampler; float4x4 InPatchToSource; void ReinitializePatch(in float4 SVPos : SV_POSITION, out float4 OutColor : SV_Target0) { // We need only the 2D affine transformation that goes from patch integer coordinates // to source UV coordinates. float2x2 PatchToSourceRotateScale = (float2x2) InPatchToSource; float2 PatchToSourceTranslate = InPatchToSource._m03_m13; float2 SourceUVCoordinates = mul(PatchToSourceRotateScale, SVPos.xy) + PatchToSourceTranslate; OutColor = InSource.Sample(InSourceSampler, SourceUVCoordinates); #if HEIGHT_PATCH // Unpacking and repacking is an easy way to make sure that we interpolate // between the two bytes of height correctly. OutColor = float4(PackHeight(UnpackHeight(OutColor.xy)), 0, 1); #endif } #endif // REINITIALIZE_PATCH #if SIMPLE_TEXTURE_COPY Texture2D InSource; SamplerState InSourceSampler; float2 InDestinationResolution; void SimpleTextureCopy(in float4 SVPos : SV_POSITION, out float4 OutColor : SV_Target0) { float2 UV = SVPos.xy / InDestinationResolution; OutColor = InSource.Sample(InSourceSampler, UV); } #endif // SIMPLE_TEXTURE_COPY