467 lines
14 KiB
HLSL
467 lines
14 KiB
HLSL
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
#include "MPCDIUtils.ush"
|
|
|
|
static const float DefaultChromakeyTileMultiplier = 10.0;
|
|
|
|
OutputVS IcvfxWarpVS(InputVS IN)
|
|
{
|
|
OutputVS OUT;
|
|
|
|
#if MESH_WARP
|
|
OUT.PFMPosition = mul(float4(IN.Position.xyz,1.f), MeshToStageProjectionMatrix).xyz;
|
|
OUT.UV_Chromakey = float4(IN.UV_Chromakey, 0.f, 1.f);
|
|
float4 ScreenPosition = float4(IN.UV.xy, 0, 1);
|
|
#else
|
|
float4 ScreenPosition = IN.Position;
|
|
#endif
|
|
|
|
DrawRectangle(ScreenPosition, IN.UV, OUT.Position, OUT.UV.xy);
|
|
OUT.Position.zw = float2(0.f, 1.f);
|
|
OUT.UV = float4(IN.UV, 0.f, 1.f);
|
|
return OUT;
|
|
}
|
|
|
|
OutputPS Passthrough_PS(OutputVS IN)
|
|
{
|
|
OutputPS OUT;
|
|
|
|
float4 ViewportUV = mul(IN.UV, ViewportTextureProjectionMatrix);
|
|
float4 ViewportColor = InputTexture.Sample(InputSampler, ViewportUV.xy);
|
|
|
|
OUT.Color = ViewportColor;
|
|
return OUT;
|
|
}
|
|
|
|
float3 ComposeOverlayColor(float4 InColor, float4 InOverlayColor)
|
|
{
|
|
return InOverlayColor.xyz + InColor.xyz * InOverlayColor.w;
|
|
}
|
|
|
|
/** Apply final gamma for LightCard color. */
|
|
float4 GetFinalLightcardColor(float4 InColor)
|
|
{
|
|
// Lightcard color is premultiplied. Unpremultiply before gamma correction.
|
|
float4 OutColor = InvertedUnpremultiply(InColor);
|
|
|
|
// Apply gamma conversion for unpremultiplied color
|
|
OutColor.rgb = DisplayGammaCorrection(OutColor.rgb, LightCardGamma);
|
|
|
|
// And finally premultiply it back
|
|
OutColor = InvertedPremultiply(OutColor);
|
|
|
|
return OutColor;
|
|
}
|
|
|
|
// Additive render pass with 2 overlays
|
|
// a = in\out, b,c = ordered overlays (a + b + c)
|
|
// c.xyz + (b.xyz + a.xyz * b.w)*c.w = a.xyz * (b.w *c.w) + b.xyz*c.w + c.xyz
|
|
float4 ComposeRenderPassTwoOverlays(float4 InOverlay1, float4 InOverlay2)
|
|
{
|
|
return float4(ComposeOverlayColor(InOverlay1, InOverlay2), InOverlay1.w * InOverlay2.w);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
// LightCard
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
float4 GetLightCardUnderTextureColor(float2 InUV)
|
|
{
|
|
return LightCardUnderTexture.Sample(LightCardUnderSampler, InUV);
|
|
}
|
|
|
|
float4 GetLightCardOverTextureColor(float2 InUV)
|
|
{
|
|
return LightCardOverTexture.Sample(LightCardOverSampler, InUV);
|
|
}
|
|
|
|
float4 GetUVLightCardUnderTextureColor(float2 InUV)
|
|
{
|
|
float4 UVLightCardUnderColor = UVLightCardUnderTexture.Sample(UVLightCardUnderSampler, InUV);
|
|
|
|
if (LightCardMode & UVLIGHTCARD_MERGE)
|
|
{
|
|
// UVLIGHTCARD_MERGE - special rendering mode, for Outer viewports that are forced to render LightCards only in "over" or "under" mode.
|
|
// both UV lightcard textures are rendered under or over the inner frustum.
|
|
|
|
float4 UVLightCardOverColor= UVLightCardOverTexture.Sample(UVLightCardOverSampler, InUV);
|
|
|
|
return ComposeRenderPassTwoOverlays(UVLightCardUnderColor, UVLightCardOverColor);
|
|
}
|
|
|
|
return UVLightCardUnderColor;
|
|
}
|
|
|
|
float4 GetUVLightCardOverTextureColor(float2 InUV)
|
|
{
|
|
float4 UVLightCardOverColor = UVLightCardOverTexture.Sample(UVLightCardOverSampler, InUV);
|
|
|
|
if (LightCardMode & UVLIGHTCARD_MERGE)
|
|
{
|
|
// UVLIGHTCARD_MERGE - special rendering mode, for Outer viewports that are forced to render LightCards only in "over" or "under" mode.
|
|
// both UV lightcard textures are rendered under or over the inner frustum.
|
|
|
|
float4 UVLightCardUnderColor = UVLightCardUnderTexture.Sample(UVLightCardUnderSampler, InUV);
|
|
|
|
return ComposeRenderPassTwoOverlays(UVLightCardUnderColor, UVLightCardOverColor);
|
|
}
|
|
|
|
return UVLightCardOverColor;
|
|
}
|
|
|
|
// This function expects that (LightCardMode & (LIGHTCARD_UNDER | UVLIGHTCARD_UNDER)) != 0
|
|
float4 GetLightCardUnderColor(OutputVS IN, float2 OverlayUV)
|
|
{
|
|
// Only the mesh has a UV_Chromakey parameter.
|
|
#if MESH_WARP
|
|
if( (LightCardMode & (LIGHTCARD_UNDER | UVLIGHTCARD_UNDER)) == (LIGHTCARD_UNDER | UVLIGHTCARD_UNDER))
|
|
{
|
|
return ComposeRenderPassTwoOverlays(GetLightCardUnderTextureColor(OverlayUV), GetUVLightCardUnderTextureColor(IN.UV_Chromakey.xy));
|
|
}
|
|
|
|
if(LightCardMode & UVLIGHTCARD_UNDER)
|
|
{
|
|
return GetUVLightCardUnderTextureColor(IN.UV_Chromakey.xy);
|
|
}
|
|
#endif
|
|
|
|
return GetLightCardUnderTextureColor(OverlayUV);
|
|
}
|
|
|
|
|
|
// This function expects that (LightCardMode & (LIGHTCARD_OVER | UVLIGHTCARD_OVER)) != 0
|
|
float4 GetLightCardOverColor(OutputVS IN, float2 OverlayUV)
|
|
{
|
|
// Only the mesh has a UV_Chromakey parameter.
|
|
#if MESH_WARP
|
|
if( (LightCardMode & (LIGHTCARD_OVER | UVLIGHTCARD_OVER)) == (LIGHTCARD_OVER | UVLIGHTCARD_OVER))
|
|
{
|
|
return ComposeRenderPassTwoOverlays(GetLightCardOverTextureColor(OverlayUV), GetUVLightCardOverTextureColor(IN.UV_Chromakey.xy));
|
|
}
|
|
|
|
if(LightCardMode & UVLIGHTCARD_OVER)
|
|
{
|
|
return GetUVLightCardOverTextureColor(IN.UV_Chromakey.xy);
|
|
}
|
|
#endif
|
|
|
|
return GetLightCardOverTextureColor(OverlayUV);
|
|
}
|
|
|
|
float3 ComposeLightCardUnderColor(float4 InColor, OutputVS IN, float2 OverlayUV)
|
|
{
|
|
if( LightCardMode & (LIGHTCARD_UNDER | UVLIGHTCARD_UNDER))
|
|
{
|
|
float4 LightCardUnderColor = GetLightCardUnderColor(IN, OverlayUV);
|
|
|
|
// Convert final LC color from linear to the output color space
|
|
LightCardUnderColor = GetFinalLightcardColor(LightCardUnderColor);
|
|
|
|
return ComposeOverlayColor(InColor, LightCardUnderColor);
|
|
}
|
|
|
|
return InColor.xyz;
|
|
}
|
|
|
|
float3 ComposeLightCardOverColor(float4 InColor, OutputVS IN, float2 OverlayUV)
|
|
{
|
|
if( LightCardMode & (LIGHTCARD_OVER | UVLIGHTCARD_OVER))
|
|
{
|
|
float4 LightCardOverColor = GetLightCardOverColor(IN, OverlayUV);
|
|
|
|
// Convert final LC color from linear to the output color space
|
|
LightCardOverColor = GetFinalLightcardColor(LightCardOverColor);
|
|
|
|
return ComposeOverlayColor(InColor, LightCardOverColor);
|
|
}
|
|
|
|
return InColor.xyz;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
float2 TileUV(float2 InUV, float InScale)
|
|
{
|
|
return frac(InUV * InScale);
|
|
}
|
|
|
|
float GetFeatherAlpha(float2 EdgeOffset, float Feather)
|
|
{
|
|
float Alpha = 1;
|
|
|
|
// do not change the alpha within an in-camera frame
|
|
if (EdgeOffset.x < 1)
|
|
{
|
|
Alpha *= smoothstep(0, Feather, EdgeOffset.x); // horizontal component
|
|
}
|
|
|
|
// do not change the alpha within an in-camera frame
|
|
if (EdgeOffset.y < 1)
|
|
{
|
|
Alpha *= smoothstep(0, Feather, EdgeOffset.y); // vertical component
|
|
}
|
|
|
|
return Alpha;
|
|
}
|
|
|
|
float GetInnerCameraSoftEdgeAlpha(float2 CameraUV, float4 InInnerCameraSoftEdge)
|
|
{
|
|
float2 EdgeOffset = float2(1, 1);
|
|
|
|
// InInnerCameraSoftEdge.x values are used to pass left/right soft edge values
|
|
if(CameraUV.x < InInnerCameraSoftEdge.x) // left
|
|
{
|
|
float CameraTextureAlphaLeft = Pow2(saturate(abs(CameraUV.x) / InInnerCameraSoftEdge.x));
|
|
EdgeOffset.x = CameraTextureAlphaLeft;
|
|
}
|
|
else
|
|
if(CameraUV.x > (1 - InInnerCameraSoftEdge.x)) // right
|
|
{
|
|
float CameraTextureAlphaRight = Pow2(saturate(abs(1 - CameraUV.x) / InInnerCameraSoftEdge.x));
|
|
EdgeOffset.x = CameraTextureAlphaRight;
|
|
}
|
|
|
|
// InInnerCameraSoftEdge.y values are used to pass top/bottom soft edge values
|
|
if(CameraUV.y < (InInnerCameraSoftEdge.y)) // top
|
|
{
|
|
float CameraTextureAlphaTop = Pow2(saturate(abs(CameraUV.y) / InInnerCameraSoftEdge.y));
|
|
EdgeOffset.y = CameraTextureAlphaTop;
|
|
}
|
|
else
|
|
if(CameraUV.y > (1 - InInnerCameraSoftEdge.y)) // bottom
|
|
{
|
|
float CameraTextureAlphaBottom = Pow2(saturate(abs(1 - CameraUV.y) / InInnerCameraSoftEdge.y));
|
|
EdgeOffset.y = CameraTextureAlphaBottom;
|
|
}
|
|
|
|
// InInnerCameraSoftEdge.z are used to pass Feather soft edge values
|
|
float Feather = InInnerCameraSoftEdge.z;
|
|
|
|
return GetFeatherAlpha(EdgeOffset, Feather);
|
|
}
|
|
|
|
float4 GetChromakeyMarkerColor(float2 InMarkersUV)
|
|
{
|
|
// Tile scale is inversely proprotional to the distance
|
|
float TileScale = DefaultChromakeyTileMultiplier / ChromakeyMarkerDistance;
|
|
|
|
// Invert vertical axis for more intuitive controls
|
|
float2 InvertedChromakeyMarkerOffset = ChromakeyMarkerOffset;
|
|
InvertedChromakeyMarkerOffset.y = -ChromakeyMarkerOffset.y;
|
|
|
|
float2 ChromakeyMarkerUV = TileUV(InMarkersUV - InvertedChromakeyMarkerOffset/TileScale, TileScale);
|
|
|
|
// Scale individual tile from its center, and independently from the distance
|
|
float2 HalfTile = float2(0.5, 0.5);
|
|
ChromakeyMarkerUV -= HalfTile;
|
|
ChromakeyMarkerUV *= ChromakeyMarkerDistance / ChromakeyMarkerScale;
|
|
float4 ChromakeyMarkerTextureColor = ChromakeyMarkerTexture.Sample(ChromakeyMarkerSampler, ChromakeyMarkerUV + HalfTile);
|
|
|
|
// Anything outside scaled UV bounds is untextured
|
|
if(any(abs(ChromakeyMarkerUV) > HalfTile))
|
|
{
|
|
return float4(ChromakeyMarkerColor.xyz, 0);
|
|
}
|
|
|
|
return float4(ChromakeyMarkerColor.xyz, ChromakeyMarkerTextureColor.w);
|
|
}
|
|
|
|
float4 GetInnerCameraColor(float4 WarpedUV, OutputVS IN)
|
|
{
|
|
// Transform WarpedUV to Camera ScreenSpaceUV
|
|
float4 CameraUVW = mul(WarpedUV, InnerCameraProjectionMatrix);
|
|
float2 CameraUV = CameraUVW.xy / CameraUVW.w;
|
|
|
|
#if CHROMAKEYFRAMECOLOR
|
|
float4 CameraColor = float4(0,0,0,0);
|
|
#else
|
|
// ** Incamera Frame **
|
|
float3 CameraBaseColor = InnerCameraTexture.Sample(InnerCameraSampler, CameraUV).rgb;
|
|
float4 CameraColor = float4(CameraBaseColor, 0);
|
|
#endif
|
|
|
|
// ********* Compose camera alpha **********
|
|
if (CameraUVW.w > 0) // clip back plane
|
|
{
|
|
//Defined texel:
|
|
float2 ToEdge = (CameraUV.xy * 2) - 1.0f; // -1..1
|
|
float Weight = 1 - max(abs(ToEdge.x), abs(ToEdge.y));
|
|
|
|
// Near clip Plane tests
|
|
if (Weight >= 0)
|
|
{
|
|
// *** InCamera Soft Edges ***
|
|
CameraColor.w = GetInnerCameraSoftEdgeAlpha(CameraUV, InnerCameraSoftEdge);
|
|
|
|
// **** disable incamera render inside overlapped areas
|
|
#if ENABLE_INNER_CAMERA_OVERLAP && !INNER_CAMERA_OVERLAP
|
|
if(OverlappedInnerCamerasNum > 0 && CameraColor.w < 1)
|
|
{
|
|
for(uint OverlappedCameraIndex = 0; OverlappedCameraIndex < OverlappedInnerCamerasNum; OverlappedCameraIndex++)
|
|
{
|
|
float4x4 InnerCameraProjectionMatrix2 = OverlappedInnerCamerasProjectionMatrices[OverlappedCameraIndex];
|
|
|
|
// Transform WarpedUV to Camera ScreenSpaceUV
|
|
float4 CameraUVW2 = mul(WarpedUV, InnerCameraProjectionMatrix2);
|
|
if (CameraUVW2.w > 0) // clip back plane
|
|
{
|
|
float2 CameraUV2 = CameraUVW2.xy / CameraUVW2.w;
|
|
float2 ToEdge2 = (CameraUV2.xy * 2) - 1.0f; // -1..1
|
|
float Weight2 = 1 - max(abs(ToEdge2.x), abs(ToEdge2.y));
|
|
if (Weight2 >= 0)
|
|
{
|
|
float UnderCameraAlpha = GetInnerCameraSoftEdgeAlpha(CameraUV2, OverlappedInnerCameraSoftEdges[OverlappedCameraIndex]);
|
|
|
|
//!
|
|
if(UnderCameraAlpha > 0.1)
|
|
{
|
|
// This pixel was overlapped. Do not render
|
|
CameraColor.w = 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
// ********* Compose camera colors **********
|
|
#if CHROMAKEYFRAMECOLOR || CHROMAKEY
|
|
// Use frame color by default
|
|
float4 OverlayChromakeyColor = float4(ChromakeyColor.xyz,0);
|
|
|
|
#if CHROMAKEY
|
|
float4 ChromakeyCameraTextureColor = ChromakeyCameraTexture.Sample(ChromakeyCameraSampler, CameraUV);
|
|
|
|
// Get alpha from chromakey rtt frame
|
|
OverlayChromakeyColor.w = ChromakeyCameraTextureColor.w;
|
|
#endif
|
|
|
|
#if CHROMAKEY_MARKER
|
|
float4 ChromakeyMarkerTextureColor = GetChromakeyMarkerColor(IN.UV_Chromakey.xy);
|
|
|
|
// Blend marker color with chromakey
|
|
OverlayChromakeyColor.xyz = lerp(OverlayChromakeyColor.xyz, ChromakeyMarkerTextureColor.xyz, ChromakeyMarkerTextureColor.w);
|
|
#endif
|
|
|
|
// ********* When camera overlap we render chromakey **********
|
|
#if ENABLE_INNER_CAMERA_OVERLAP && INNER_CAMERA_OVERLAP
|
|
if(CameraColor.w >= 0)
|
|
{
|
|
OverlayChromakeyColor.w = 0;
|
|
|
|
// disable chromakey
|
|
float OverlayAlphaMult = 0;
|
|
|
|
for(uint OverlappedCameraIndex = 0; OverlappedCameraIndex < OverlappedInnerCamerasNum; OverlappedCameraIndex++)
|
|
{
|
|
float4x4 InnerCameraProjectionMatrix2 = OverlappedInnerCamerasProjectionMatrices[OverlappedCameraIndex];
|
|
|
|
// Transform WarpedUV to Camera ScreenSpaceUV
|
|
float4 CameraUVW2 = mul(WarpedUV, InnerCameraProjectionMatrix2);
|
|
if (CameraUVW2.w > 0) // clip back plane
|
|
{
|
|
float2 CameraUV2 = CameraUVW2.xy / CameraUVW2.w;
|
|
float2 ToEdge2 = (CameraUV2.xy * 2) - 1.0f; // -1..1
|
|
float Weight2 = 1 - max(abs(ToEdge2.x), abs(ToEdge2.y));
|
|
if (Weight2 >= 0)
|
|
{
|
|
// This pixel was overlapped
|
|
// force to render Chromakey
|
|
OverlayAlphaMult = max(OverlayAlphaMult, GetInnerCameraSoftEdgeAlpha(CameraUV2, OverlappedInnerCameraSoftEdges[OverlappedCameraIndex]));
|
|
}
|
|
}
|
|
}
|
|
|
|
CameraColor.w = CameraColor.w * OverlayAlphaMult;
|
|
}
|
|
#endif
|
|
|
|
CameraColor.xyz = lerp(CameraColor.xyz, OverlayChromakeyColor.xyz, 1-OverlayChromakeyColor.w);
|
|
|
|
#endif
|
|
|
|
// *** InCamera Border ***
|
|
if(InnerCameraBorderThickness>0)
|
|
{
|
|
if((ToEdge.x < (InnerCameraBorderThickness / InnerCameraFrameAspectRatio - 1)) || (ToEdge.x > (1 - InnerCameraBorderThickness / InnerCameraFrameAspectRatio)) ||
|
|
(ToEdge.y < (InnerCameraBorderThickness - 1)) || (ToEdge.y > (1 - InnerCameraBorderThickness)))
|
|
{
|
|
CameraColor.rgb = InnerCameraBorderColor.xyz;
|
|
CameraColor.w = 1.0f;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return CameraColor;
|
|
}
|
|
|
|
OutputPS IcvfxWarpPS(OutputVS IN)
|
|
{
|
|
OutputPS OUT;
|
|
|
|
// Load warped UV
|
|
#if MESH_WARP
|
|
float4 WarpedUV = float4(IN.PFMPosition, 1.f);
|
|
#else
|
|
float4 WarpedUV = WarpMapTexture.Sample(WarpMapSampler, IN.UV.xy);
|
|
#endif
|
|
|
|
// Transform WarpedUV to ScreenSpaceUV
|
|
float4 ViewportUVW = mul(WarpedUV, ViewportTextureProjectionMatrix);
|
|
float2 ViewportUV = ViewportUVW.xy / ViewportUVW.w;
|
|
|
|
|
|
float4 OverlayUVW = mul(WarpedUV, OverlayProjectionMatrix);
|
|
float2 OverlayUV = OverlayUVW.xy / OverlayUVW.w;
|
|
|
|
// Compose all:
|
|
#if VIEWPORT_INPUT
|
|
|
|
// First and single pass renders:
|
|
|
|
#if VIEWPORT_INPUT_ALPHA
|
|
float4 OutColor = float4(InputTexture.Sample(InputSampler, ViewportUV));
|
|
#else
|
|
float4 OutColor = float4(InputTexture.Sample(InputSampler, ViewportUV).rgb, 1.0f);
|
|
#endif
|
|
|
|
|
|
// Compose a "Under" lightcard, if used.
|
|
OutColor.xyz = ComposeLightCardUnderColor(OutColor, IN, OverlayUV);
|
|
|
|
#if INNER_CAMERA
|
|
float4 CameraColor = GetInnerCameraColor(WarpedUV, IN);
|
|
OutColor.xyz = lerp(OutColor.xyz, CameraColor.xyz, CameraColor.w);
|
|
#endif
|
|
|
|
|
|
// Compose a "Over" lightcard, if used.
|
|
OutColor.xyz = ComposeLightCardOverColor(OutColor,IN, OverlayUV);
|
|
|
|
#else
|
|
float4 OutColor;
|
|
|
|
// MultiCam or Final renderpass (only cam or overlay per pass))
|
|
#if INNER_CAMERA
|
|
OutColor = GetInnerCameraColor(WarpedUV, IN);
|
|
#else
|
|
if( LightCardMode & (LIGHTCARD_OVER | UVLIGHTCARD_OVER))
|
|
{
|
|
OutColor = GetLightCardOverColor(IN, OverlayUV);
|
|
|
|
// Convert final LC color from linear to the output color space
|
|
OutColor = GetFinalLightcardColor(OutColor);
|
|
}
|
|
else
|
|
{
|
|
OutColor = float4(0,0,0,0);
|
|
}
|
|
#endif
|
|
|
|
#endif
|
|
|
|
//@todo: add LUT here
|
|
|
|
// Apply final mpcdi color blending
|
|
OUT.Color = ApplyBlending(OutColor.xyz, IN, OutColor.w);
|
|
return OUT;
|
|
}
|