// Copyright Epic Games, Inc. All Rights Reserved. /*============================================================================= DiaphragmDOF/DOFHybridScatterPixelShader.usf: Scattering's pixel shader. =============================================================================*/ #include "DOFHybridScatterCommon.ush" #include "DOFVignetteCommon.ush" #include "../FastMath.ush" #include "/Engine/Public/DualPixelVectorization.ush" //------------------------------------------------------- COMPILE TIME CONFIG // Whether to point mirror the bokeh. #if DIM_LAYER_PROCESSING == LAYER_PROCESSING_FOREGROUND_ONLY #define CONFIG_POINT_MIRROR_BOKEH -1 #elif DIM_LAYER_PROCESSING == LAYER_PROCESSING_BACKGROUND_ONLY #define CONFIG_POINT_MIRROR_BOKEH 1 #endif //------------------------------------------------------- PARAMETERS float ScatteringScaling; StructuredBuffer ScatterDrawList; Texture2D BokehLUT; float4 ScatterOcclusionSize; Texture2D ScatterOcclusion; float BarrelRadius; float BarrelLength; //------------------------------------------------------- FUNCTIONS half2 HalfSafeRcp(half2 Value) { return half2( Value.x > 0.0 ? rcp(Value.x) : 0.0, Value.y > 0.0 ? rcp(Value.y) : 0.0 ); } half2x2 dpv_mul_vec_mat(half2x2 InVector, half2x2 InMatrix) { return dpv_interleave_registers( mul(dpv_lo(InVector), InMatrix), mul(dpv_hi(InVector), InMatrix) ); } /// Calculate the visibility of the pixel for a specific bokeh through the lens barrel half2 GetPixelVisibility_DPV(half2x2 PixelOffset, uint2 VignetteDataOffset) { #if CONFIG_VIGNETTE bool bOpticalVignetting = BarrelLength >= 0; if(bOpticalVignetting) { float4 VignetteData[VIGNETTE_DATA_PER_PIXEL][DPV_PIXEL_PER_LANE] = { { ScatterDrawList[VignetteDataOffset[0] + 0], ScatterDrawList[VignetteDataOffset[1] + 0] }, { ScatterDrawList[VignetteDataOffset[0] + 1], ScatterDrawList[VignetteDataOffset[1] + 1] } }; uint2 BarrelData[DPV_PIXEL_PER_LANE] = { asuint(VignetteData[0][0].xy), asuint(VignetteData[0][1].xy) }; uint2 ProjectedMatteBoxFlags[MAX_MATTE_BOX_FLAGS][DPV_PIXEL_PER_LANE] = { { asuint(VignetteData[0][0].zw), asuint(VignetteData[0][1].zw) }, { asuint(VignetteData[1][0].xy), asuint(VignetteData[1][1].xy) }, { asuint(VignetteData[1][0].zw), asuint(VignetteData[1][1].zw) } }; half2x2 SamplePositionAtBarrelEnd; half2 Visibility = HitTestProjectedCylinder_DPV(PixelOffset, BarrelData, BarrelRadius, /* out */ SamplePositionAtBarrelEnd); UNROLL for (int Index = 0; Index < MAX_MATTE_BOX_FLAGS; ++Index) { Visibility *= HitTestProjectedMatteBoxFlag_DPV(SamplePositionAtBarrelEnd, ProjectedMatteBoxFlags[Index]); } return Visibility; } #endif return half2(1.0, 1.0); } void ScatterMainPS( in nointerpolation float4 BokehCenterScreenAndClipPos : TEXCOORD0, in nointerpolation float4 ColorAndAbsCocRadius0 : TEXCOORD1, in nointerpolation float4 ColorAndAbsCocRadius1 : TEXCOORD2, in nointerpolation uint3 GlobalSpriteIdAndPetzvalTransformPacked : TEXCOORD3, #if TAYLOR_EXPENDED_COC_INTERSECTION in nointerpolation float4 TaylorExpensionMAD0 : TEXCOORD4, in nointerpolation float4 TaylorExpensionMAD1 : TEXCOORD5, #endif in float4 SvPosition : SV_Position, out float4 OutColor : SV_Target0) { OutColor = 0; const float2 BokehCenter0 = BokehCenterScreenAndClipPos.xy; const float2 BokehCenterClipPos = BokehCenterScreenAndClipPos.zw; half3 Color[PIXEL_COUNT_PER_SCATTER_GROUP]; half AbsCocRadius[PIXEL_COUNT_PER_SCATTER_GROUP]; { half2 Unpacked0; half2 Unpacked1; Unpacked0 = (half2)UnpackFloat2FromUInt(asuint(ColorAndAbsCocRadius0.x)); Unpacked1 = (half2)UnpackFloat2FromUInt(asuint(ColorAndAbsCocRadius0.y)); Color[0] = half3(Unpacked0.xy, Unpacked1.x); AbsCocRadius[0] = Unpacked1.y; Unpacked0 = (half2)UnpackFloat2FromUInt(asuint(ColorAndAbsCocRadius0.z)); Unpacked1 = (half2)UnpackFloat2FromUInt(asuint(ColorAndAbsCocRadius0.w)); Color[1] = half3(Unpacked0.xy, Unpacked1.x); AbsCocRadius[1] = Unpacked1.y; Unpacked0 = (half2)UnpackFloat2FromUInt(asuint(ColorAndAbsCocRadius1.x)); Unpacked1 = (half2)UnpackFloat2FromUInt(asuint(ColorAndAbsCocRadius1.y)); Color[2] = half3(Unpacked0.xy, Unpacked1.x); AbsCocRadius[2] = Unpacked1.y; Unpacked0 = (half2)UnpackFloat2FromUInt(asuint(ColorAndAbsCocRadius1.z)); Unpacked1 = (half2)UnpackFloat2FromUInt(asuint(ColorAndAbsCocRadius1.w)); Color[3] = half3(Unpacked0.xy, Unpacked1.x); AbsCocRadius[3] = Unpacked1.y; } #if TAYLOR_EXPENDED_COC_INTERSECTION half2 TaylorExpension[4] = { half2(TaylorExpensionMAD0.xy), half2(TaylorExpensionMAD0.zw), half2(TaylorExpensionMAD1.xy), half2(TaylorExpensionMAD1.zw), }; #endif #if DIM_SCATTER_OCCLUSION float4 ScatterOcclusionSample = ScatterOcclusion.SampleLevel(GlobalBilinearWrappedSampler, SvPosition.xy * ScatterOcclusionSize.zw, 0); half CocRadiusAvg = half(ScatterOcclusionSample.x); half CocRadiusVariance = half(ScatterOcclusionSample.y); #endif half2 PixelOffset0 = half2(SvPosition.xy - BokehCenter0); uint GlobalSpriteId = GlobalSpriteIdAndPetzvalTransformPacked.x; #if CONFIG_PETZVAL half2x2 PetzvalTransform = half2x2( UnpackFloat2FromUInt(GlobalSpriteIdAndPetzvalTransformPacked.y), UnpackFloat2FromUInt(GlobalSpriteIdAndPetzvalTransformPacked.z) ); #endif // Evaluate all bokeh of the scattering group. // Dual Pixel Vectorization: evaluate 2 pixels at a time using packed 16-bit float instructions on supported platforms. UNROLL for (uint i = 0; i < PIXEL_COUNT_PER_SCATTER_GROUP; i += DPV_PIXEL_PER_LANE) { // Pixel coordinate of the center of the bokeh. half2x2 PixelLocalOffset = dpv_interleave_registers(half2(kSquare2x2[i]), half2(kSquare2x2[i + 1])); half2x2 PixelOffset = dpv_sub(PixelOffset0, dpv_scale(half(ScatteringScaling), PixelLocalOffset)); half2 PixelAbsCocRadius = dpv_interleave_registers(AbsCocRadius[i], AbsCocRadius[i+1]); #if CONFIG_PETZVAL PixelOffset = dpv_mul_vec_mat(PixelOffset, PetzvalTransform); #endif PixelOffset[0] *= half(CocSqueeze); half2 Intersection; #if DIM_BOKEH_SIMULATION { half2 PixelDistance = dpv_length(PixelOffset); half2x2 LookUpUV = dpv_scale(PixelOffset, half(CONFIG_POINT_MIRROR_BOKEH * (0.5 - 1.0 / float(BOKEH_LUT_SIZE))) * HalfSafeRcp(PixelDistance)); // TODO: might be faster without using view uniform buffer. float4 LookupSample0 = BokehLUT.SampleLevel(GlobalBilinearWrappedSampler, dpv_lo(LookUpUV), 0); float4 LookupSample1 = BokehLUT.SampleLevel(GlobalBilinearWrappedSampler, dpv_hi(LookUpUV), 0); half2 CocRadiusToBokehEdgeFactor = dpv_interleave_registers(half(LookupSample0.x), half(LookupSample1.x)); Intersection = saturate(PixelAbsCocRadius * CocRadiusToBokehEdgeFactor - PixelDistance + ANTI_ALIASING_FEATHER_OFFSET); } #elif TAYLOR_EXPENDED_COC_INTERSECTION { half2 TaylorExpansionX = dpv_interleave_registers(TaylorExpension[i].x, TaylorExpension[i+1].x); half2 TaylorExpansionY = dpv_interleave_registers(TaylorExpension[i].y, TaylorExpension[i+1].y); Intersection = saturate(dpv_dot(PixelOffset, PixelOffset) * TaylorExpansionX + TaylorExpansionY); } #else { // Pretty simple sphere intersection. half2 PixelDistance = dpv_length(PixelOffset); Intersection = saturate(PixelAbsCocRadius - PixelDistance + ANTI_ALIASING_FEATHER_OFFSET); } #endif BRANCH if (any(Intersection > 0)) { half2 Visibility = 1; #if DIM_SCATTER_OCCLUSION { half2 CocRadiusComparison = max(PixelAbsCocRadius * half(1 - FAST_GATHERING_COC_ERROR) - CocRadiusAvg, (half2)0); Visibility = CocRadiusVariance / (CocRadiusVariance + CocRadiusComparison * CocRadiusComparison); } #endif uint2 ScatterDrawListOffset = { SCATTER_DRAW_LIST_GROUP_STRIDE * GlobalSpriteId + 1 + ((i + 0) * SCATTER_DRAW_LIST_DATA_PER_PIXEL), SCATTER_DRAW_LIST_GROUP_STRIDE * GlobalSpriteId + 1 + ((i + 1) * SCATTER_DRAW_LIST_DATA_PER_PIXEL) }; uint2 VignetteDataOffset = ScatterDrawListOffset + 1; Visibility *= GetPixelVisibility_DPV(PixelOffset, VignetteDataOffset); half3x2 PixelColor = dpv_interleave_registers(Color[i].rgb, Color[i + 1].rgb); PixelColor = dpv_scale(PixelColor, Intersection * Visibility); half3 ColorAcc = dpv_lo(PixelColor) + dpv_hi(PixelColor); OutColor.rgb += ColorAcc; } } #if DEBUG_HYBRID_SCATTERING_SPRITE == 1 if (any(OutColor.rgb > 0)) OutColor = float4(0, 0.1, 0, 0); else OutColor = float4(0.1, 0, 0, 0); #elif DEBUG_HYBRID_SCATTERING_SPRITE == 2 OutColor *= float4(1, 0, 0, 0); #endif }