// Copyright 1998-2014 Epic Games, Inc. All Rights Reserved. /*============================================================================= PostProcessSubsurface.usf: Screenspace subsurface scattering shaders. =============================================================================*/ #include "Common.usf" #include "PostProcessCommon.usf" #include "DeferredShadingCommon.usf" // for VisualizeSSS #include "MiniFontCommon.usf" /** RGBA8 linear texture containing random normals */ Texture2D SSProfilesTexture; SamplerState SSProfilesTextureSampler; // x:Radius, y:DistanceToProjectionWindow, zw:xy scale correction for BufferSize!=ViewportSize float4 SSSParams; // ------------------------------------------ // setup for "SeparableSSS.usf" #define SSSS_HLSL_4 1 // can be optimized float GetSubsurfaceStrength(float2 UV) { FScreenSpaceData ScreenSpaceData = GetScreenSpaceData(UV); float Mask = ScreenSpaceData.GBuffer.ShadingModelID >= SHADINGMODELID_SUBSURFACE_PROFILE; return Mask * ScreenSpaceData.GBuffer.Opacity; } //#define SSSS_STREGTH_SOURCE 1 // todo: masking fom LightingModel or SSSColor #define SSSS_STREGTH_SOURCE GetSubsurfaceStrength(texcoord) // 0:speculars leak into SSS / 1:requires alpha channel for scenecolor // #define SSSS_SPECULAR_CORRECTION 1 // set by C++ for SubsurfaceRecombinePS // #define SSS_RECOMBINE_METHOD 0:without reconstruct specular/1:with reconstruct specular // set by C++ for SubsurfacePS // #define SSS_DIRECTION 0/1 // define by C++ for the setup pass #ifndef SETUP_MODE #define SETUP_MODE 0 #endif #if SETUP_MODE == 0 #define SSSS_SPECULAR_CORRECTION 0 #define SETUP_HALF_RES 0 #elif SETUP_MODE == 1 #define SSSS_SPECULAR_CORRECTION 1 #define SETUP_HALF_RES 0 #elif SETUP_MODE == 3 #define SSSS_SPECULAR_CORRECTION 0 #define SETUP_HALF_RES 1 #elif SETUP_MODE == 4 #define SSSS_SPECULAR_CORRECTION 1 #define SETUP_HALF_RES 1 #else // doesn't matter #define SSSS_SPECULAR_CORRECTION 0 #endif #if SSS_SAMPLESET == 0 #define SSSS_N_KERNELWEIGHTCOUNT 6 #define SSSS_N_KERNELWEIGHTOFFSET (13 + 9) #elif SSS_SAMPLESET == 1 #define SSSS_N_KERNELWEIGHTCOUNT 9 #define SSSS_N_KERNELWEIGHTOFFSET 13 #else // SSS_SAMPLESET == 2 #define SSSS_N_KERNELWEIGHTCOUNT 13 #define SSSS_N_KERNELWEIGHTOFFSET 0 #endif // 0: faster // 1: no color bleeding in z direction #define SSSS_FOLLOW_SURFACE 1 // from https://github.com/iryoku/separable-sss/tree/master/Demo // Jorge Jimenez http://www.iryoku.com/ // http://www.iryoku.com/translucency/downloads/Real-Time-Realistic-Skin-Translucency.pdf #include "SeparableSSS.usf" // ------------------------------------------ #if SSSS_SPECULAR_CORRECTION == 1 // requires alpha channel for scenecolor #elif SSSS_SPECULAR_CORRECTION == 0 // speculars leak into SSS #else error #endif // 0 / 1 #define VISUALIZE_KERNEL 0 bool InUnitBox(float2 UV) { return UV.x >= 0 && UV.y >= 0 && UV.y < 1 && UV.y < 1; } float3 SetupSubsurfaceForOnePixel(float2 UV) { float3 Ret = 0; FScreenSpaceData ScreenSpaceData = GetScreenSpaceData(UV); FLATTEN if (ScreenSpaceData.GBuffer.ShadingModelID >= SHADINGMODELID_SUBSURFACE_PROFILE) { float4 SceneColor4 = Texture2DSample(PostprocessInput0, PostprocessInput0Sampler, UV); #if SSSS_SPECULAR_CORRECTION == 1 // we take out the specular highlights Ret = LightAccumulator_ReconstructDiffuseLighting(SceneColor4); #else Ret = SceneColor4.rgb; #endif } return Ret; } void SetupPS(in float4 UVAndScreenPos : TEXCOORD0, out float4 OutColor : SV_Target0) { float2 UV = UVAndScreenPos.xy; OutColor = 0; // if not VisualizeSSS if (SETUP_MODE != 2) { #if SETUP_HALF_RES OutColor.rgb += SetupSubsurfaceForOnePixel(UV + float2(-0.5, -0.5f) * PostprocessInput0Size.zw); OutColor.rgb += SetupSubsurfaceForOnePixel(UV + float2( 0.5, -0.5f) * PostprocessInput0Size.zw); OutColor.rgb += SetupSubsurfaceForOnePixel(UV + float2(-0.5, 0.5f) * PostprocessInput0Size.zw); OutColor.rgb += SetupSubsurfaceForOnePixel(UV + float2( 0.5, 0.5f) * PostprocessInput0Size.zw); OutColor *= 0.25f; #else OutColor.rgb = SetupSubsurfaceForOnePixel(UV); #endif } else { // visualization (doesn't have to be fast) OutColor = Texture2DSample(PostprocessInput0, PostprocessInput0Sampler, UV); int2 PixelPos = (int2)(UVAndScreenPos.zw * ScreenPosToPixel.xy + ScreenPosToPixel.zw + 0.5f); float2 ViewLocalUV = (PixelPos - View.ViewRectMin.xy) * View.ViewSizeAndSceneTexelSize.zw; float2 IDAreaLocalUV = ViewLocalUV * 2 - 1.0f; // OutColor = float4(IDAreaLocalUV.xy, 0, 0); return; if (InUnitBox(IDAreaLocalUV)) { FScreenSpaceData ScreenSpaceData = GetScreenSpaceData(View.ViewRectMin.xy * View.ViewSizeAndSceneTexelSize.zw + IDAreaLocalUV * (View.ViewSizeAndSceneTexelSize.xy * View.ViewSizeAndSceneTexelSize.zw)); int SubsurfaceProfileInt = ExtractSubsurfaceProfileInt(ScreenSpaceData.GBuffer); OutColor = float4(0.5f, 0.5f, 0.5f, 0); BRANCH if (ScreenSpaceData.GBuffer.ShadingModelID == SHADINGMODELID_SUBSURFACE_PROFILE) { if (SubsurfaceProfileInt == 0) { // default (no Profile) OutColor = float4(0.8f, 0.7f, 0.6f, 0); } if (SubsurfaceProfileInt == 1) { OutColor = float4(1, 0, 0, 0); } if (SubsurfaceProfileInt == 2) { OutColor = float4(0, 1, 0, 0); } if (SubsurfaceProfileInt == 3) { OutColor = float4(0, 0, 1, 0); } if (SubsurfaceProfileInt == 4) { OutColor = float4(1, 0, 1, 0); } if (SubsurfaceProfileInt == 5) { OutColor = float4(0, 1, 1, 0); } if (SubsurfaceProfileInt == 6) { OutColor = float4(1, 1, 0, 0); } if (SubsurfaceProfileInt == 100) { OutColor = float4(0, 0.2f, 0, 0); } if (SubsurfaceProfileInt == 255) { OutColor = float4(1, 1, 1, 0); } int2 LeftTop = (PixelPos / 8) * 8; PrintCharacter(PixelPos, OutColor.rgb, float3(1, 1, 1), LeftTop, SubsurfaceProfileInt); } } } } // input0 is created by the SetupPS shader void MainPS(float4 UVAndScreenPos : TEXCOORD0, out float4 OutColor : SV_Target0) { float2 UV = UVAndScreenPos.xy; // call into "SeparableSSS.usf" // in world units, *0.01 to scale from units(cm) to m float sssWidth = SSSParams.x; // the viewport is only a fraction of the buffersize, here we compensate for that // *1000 as we store the sample distances / 1000 to allows for scaling float Scaler = View.ViewSizeAndSceneTexelSize.x * View.ViewSizeAndSceneTexelSize.z * 1000.0f; #if SSS_DIRECTION == 0 // horizontal float2 Direction = float2(1, 0) * Scaler; #else // vertical float2 Direction = float2(0, View.ViewSizeAndSceneTexelSize.w / View.ViewSizeAndSceneTexelSize.z) * Scaler; #endif OutColor.rgb = SSSSBlurPS(UV, sssWidth, Direction, false).rgb; OutColor.a = 1.0f; } void SubsurfaceRecombinePS(float4 UVAndScreenPos : TEXCOORD0, out float4 OutColor : SV_Target0) { float2 UV = UVAndScreenPos.xy; OutColor.rgb = Texture2DSample(PostprocessInput0, PostprocessInput0Sampler, UV).rgb; OutColor.a = 1.0f; // recombine with the scene color // can be optimized float2 PixelPos = UVAndScreenPos.zw * ScreenPosToPixel.xy + ScreenPosToPixel.zw; float2 UVSceneColor = (PixelPos + 0.5f) * View.ViewSizeAndSceneTexelSize.zw; float4 SceneColor4 = Texture2DSample(PostprocessInput1, PostprocessInput1Sampler, UVSceneColor); float4 SSSColor = OutColor; FScreenSpaceData ScreenSpaceData = GetScreenSpaceData(UVSceneColor); SSSColor.a = (ScreenSpaceData.GBuffer.ShadingModelID >= SHADINGMODELID_SUBSURFACE_PROFILE); #if SSS_RECOMBINE_METHOD // we took the specular highlights out, now we add them back in SSSColor.rgb += LightAccumulator_ReconstructNonDiffuseLighting(SceneColor4); #endif // alpha channel can be anything - we consumed the value and likely output to a format that doesn't need the value OutColor = float4(lerp(SceneColor4.rgb, SSSColor.rgb, SSSColor.a), 0); }