You've already forked UnrealEngineUWP
mirror of
https://github.com/izzy2lost/UnrealEngineUWP.git
synced 2026-03-26 18:15:20 -07:00
#lockdown Nick.Penwarden
==========================
MAJOR FEATURES + CHANGES
==========================
Change 3301794 on 2017/02/14 by Josh.Adams
Fixed a crash with clothing on platforms that don't support NV_CLOTH
Change 3302696 on 2017/02/14 by Chad.Garyet
adding dev-console json
Change 3306418 on 2017/02/16 by Ben.Woodhouse
Fix prepass/basepass zfighting, caused by bad vertex welding in depth-only indexbuffer. Requires bumping the staticmesh DDC key
Duplicated from Fortnite/Main CL 3302965
#jira UE-34332
Change 3308922 on 2017/02/17 by Josh.Adams
- Disabled the game analytics anon usage data sent to Epic on the console platforms
Change 3311506 on 2017/02/20 by Keith.Judge
Replicate fix for FD3D12UniqueDescriptorTable leak in async compute contexts from another branch.
Change 3313445 on 2017/02/20 by Josh.Adams
- Various Vulkan fixes:
- Compiles in Linux
- Many cubemap bugs squashed
- Changed the scratch reflection cubemap clear to SetRenderTargestsAndClear, instead of SetRenderTarget() / Clear()
- Added compute fences
Change 3314916 on 2017/02/21 by Josh.Adams
- Fixed an issue with 4 and 8 vertex instanced particles using the wrong VertexFactory objects (D3D didn't even need separate VFs due to the VertexDecl updating the stride at draw call time)
Change 3315398 on 2017/02/21 by Ben.Woodhouse
Fix GPUTestbed packaging
Change 3316340 on 2017/02/22 by Ben.Woodhouse
Duplicate hotfix from Release-4.15:
CL 3316322
Fix for GPU Cubemap copy crash - Guard for invalid indices before marking cubemap indices as removed
#jira UE-42165
Change 3317345 on 2017/02/22 by Ben.Woodhouse
Integrate from //UE4/Main/...@3316239
Change 3319186 on 2017/02/23 by Josh.Adams
Added /VIRTUALIZEDIRECTX option to XgConsole for XGE shader compiling to work on remote machines without DX installed
Change 3323514 on 2017/02/27 by Chad.Garyet
adding populate ddc for dev-console, removing RDU agent type
Change 3335889 on 2017/03/07 by Luke.Thatcher
[CONSOLE] [STREAMS] [^] Merge //UE4/Main (CL 3335229) to //UE4/Dev-Console
#tests Build Win64 Editor, run QAGame editor, Launch on PS4.
Change 3336550 on 2017/03/07 by Ben.Woodhouse
Duplicate CL 3336456
#jira UE-42468
Fix a bug in the rendertargetpool handling of fastVRAM targets, reported on UDN
Change 3340385 on 2017/03/09 by Ben.Woodhouse
Optimized fastVRAM layout and configurability. CVars can be configured based title rendering requirements and resolution
With these changes, we try to store the GBuffer in Fast VRAM if possible. Transient/non perf critical surfaces are now disabled by default
In content w/ dynamic lighting @ 900p we see a 1.8ms gain. In RenderTestMap QAGame @ 1080p we see 0.4ms gains (further improvements may be possible with additional tweaking).
Change 3355982 on 2017/03/21 by Ben.Woodhouse
Duplicate from CL 3354688:
Fix async SSAO not actually running asynchronously. This was because bHZBBeforeBasePass was set to false even though we had a full prepass (EDepthDrawingMode::DDM_AllOpaque), so we didn't process it until after the basepass.
This saved 0.6ms in GPUTestbed
Change 3356166 on 2017/03/21 by Ben.Woodhouse
Duplicate from 3347033
Subsurface postprocess optimization, courtesy of Mike O'Connor at Iron Galaxy Studios.
Add a branch to reduce bandwidth. Halved the cost of the setup pass according to PIX (0.3ms to 0.15ms)
Change 3360243 on 2017/03/23 by Luke.Thatcher
[CONSOLE] [STREAMS] [^] Merge //UE4/Main (CL 3358685) to //UE4/Dev-Console
#tests Build Win64 Editor, run FortGPUTestbed editor, Launch on PS4.
Change 3365746 on 2017/03/27 by Joe.Barnes
- Handle NULL source data.
- Log failed surround conversion.
Change 3368022 on 2017/03/28 by Ben.Woodhouse
Cherry pick reflection capture hotfix from release-4.15 CL 3365830:
Fixed reflection capture crash when repeatedly adding/removing captures
Previously we used an array of indices (CubemapIndicesRemovedSinceLastRealloc) to keep track of indices which had been removed, however this caused issues when those indices were reused by subsequent allocations before the array was reallocated
The new method uses a simple bitfield to track usage (one bit per cubemap slot index).
Also fixed order(N^2) index search in the index allocator - now just a fast bit scan
#jira UE-42165
#jira UE-42911
Change 3371568 on 2017/03/30 by Luke.Thatcher
[CONSOLE] [STREAMS] [^] Merging //UE4/Dev-Main (CL 3371054) to Dev-Console (//UE4/Dev-Console)
Change 3372780 on 2017/03/30 by Joe.Barnes
Add support for multi-channel ADPCM encoding. Format based on game side ADPCM decompressor.
Change 3374847 on 2017/03/31 by Ben.Woodhouse
Fix shipping warning
#jira UE-43522
Change 3376442 on 2017/04/03 by Ben.Woodhouse
Fix FortGPUTestbed animnotify cook errors (delete the offending animnotifies)
[CL 3378288 by Luke Thatcher in Main branch]
506 lines
16 KiB
Plaintext
506 lines
16 KiB
Plaintext
// Copyright 1998-2017 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"
|
|
|
|
/** one profile per line, sample set left to right: high/med/low */
|
|
Texture2D SSProfilesTexture;
|
|
// x:Radius*DistanceToProjectionWindow/KernelSize*0.5, y:DistanceToProjectionWindow, zw: unused
|
|
float4 SSSParams;
|
|
|
|
// 0: (testing, could be faster), 1: higher quality
|
|
#define FULLRES_WHERE_POSSIBLE 1
|
|
|
|
#ifndef RECOMBINE_QUALITY
|
|
#define RECOMBINE_QUALITY 0
|
|
#endif
|
|
|
|
// ------------------------------------------
|
|
|
|
// setup for "SeparableSSS.usf"
|
|
|
|
float ComputeMaskFromDepthInAlpha(float Alpha)
|
|
{
|
|
return Alpha > 0;
|
|
}
|
|
|
|
// can be optimized
|
|
float GetSubsurfaceStrength(float2 UV)
|
|
{
|
|
FScreenSpaceData ScreenSpaceData = GetScreenSpaceData(UV);
|
|
|
|
float Mask = ScreenSpaceData.GBuffer.ShadingModelID == SHADINGMODELID_SUBSURFACE_PROFILE;
|
|
return Mask * ScreenSpaceData.GBuffer.CustomData.a;
|
|
}
|
|
|
|
// 0:full res (slower but reference) / 1:faster half res with some quality loss
|
|
// #define HALF_RES 1
|
|
|
|
// set by C++ for SubsurfacePS
|
|
// #define SSS_DIRECTION 0/1
|
|
|
|
#define SSSS_N_KERNELSUBSURFACECOLOROFFSET 0
|
|
|
|
#if SSS_SAMPLESET == 0
|
|
#define SSSS_N_KERNELWEIGHTCOUNT 6
|
|
#define SSSS_N_KERNELWEIGHTOFFSET (1 + 13 + 9)
|
|
#elif SSS_SAMPLESET == 1
|
|
#define SSSS_N_KERNELWEIGHTCOUNT 9
|
|
#define SSSS_N_KERNELWEIGHTOFFSET (1 + 13)
|
|
#else // SSS_SAMPLESET == 2
|
|
#define SSSS_N_KERNELWEIGHTCOUNT 13
|
|
#define SSSS_N_KERNELWEIGHTOFFSET (1)
|
|
#endif
|
|
|
|
// 0: faster
|
|
// 1: no color bleeding in z direction
|
|
#define SSSS_FOLLOW_SURFACE 1
|
|
|
|
// @param In needs to be in 0..1 range
|
|
// @return 0..1
|
|
float4 Quantize8Bit(float4 In)
|
|
{
|
|
return floor(In * 255.999f) / 255.0f;
|
|
}
|
|
|
|
// @return .rgb is the weight for color channel, .a is the sample location
|
|
float4 GetKernel(uint SampleIndex, uint SubsurfaceProfileInt)
|
|
{
|
|
const float4 TableMax = float4(1, 1, 1, SUBSURFACE_KERNEL_SIZE);
|
|
|
|
// profiled on NV670 fullscreen, one pass (total: 2 pass), samples: 13+1+13, large object filling the screen
|
|
|
|
// texture lookup
|
|
float4 Value = SSProfilesTexture.Load(int3(SampleIndex, SubsurfaceProfileInt, 0)) * TableMax; // 0.88ms for 8bit
|
|
// simulated 8 bit lookup
|
|
// float4 Value = Quantize8Bit(kernel[SampleIndex] / TableMax) * TableMax; // 1.33ms same look as texture
|
|
// float reference
|
|
// float4 Value = kernel[SampleIndex]; // 0.85ms, quite a bit different in look, need to use more than 8 bit?
|
|
|
|
// debug if the kernal was a box filter
|
|
// Value.rgb = 1.0f / (SSSS_N_KERNELWEIGHTCOUNT * 2 - 1);
|
|
|
|
return Value;
|
|
}
|
|
|
|
float GetProfileRadiusScale(FGBufferData GBufferData)
|
|
{
|
|
// 0..255, which SubSurface profile to pick
|
|
uint SubsurfaceProfileInt = ExtractSubsurfaceProfileInt(GBufferData);
|
|
|
|
return GetKernel(SSSS_N_KERNELWEIGHTOFFSET + SSSS_N_KERNELWEIGHTCOUNT - 1, SubsurfaceProfileInt).a;
|
|
}
|
|
|
|
float3 GetProfileSubsurfaceColor(FGBufferData GBufferData)
|
|
{
|
|
// 0..255, which SubSurface profile to pick
|
|
uint SubsurfaceProfileInt = ExtractSubsurfaceProfileInt(GBufferData);
|
|
|
|
return GetKernel(SSSS_N_KERNELSUBSURFACECOLOROFFSET, SubsurfaceProfileInt).rgb;
|
|
}
|
|
|
|
// 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"
|
|
|
|
// ------------------------------------------
|
|
|
|
bool InUnitBox(float2 UV)
|
|
{
|
|
return UV.x >= 0 && UV.y >= 0 && UV.y < 1 && UV.y < 1;
|
|
}
|
|
|
|
|
|
// @return 0=don't blend in, 1:fully blend in
|
|
float ComputeFullResLerp(FScreenSpaceData ScreenSpaceData, float2 UVSceneColor, float4 FullResInputSize)
|
|
{
|
|
float SSSScaleX = SSSParams.x;
|
|
|
|
float scale = SSSScaleX / CalcSceneDepth(UVSceneColor);
|
|
|
|
float HorizontalScaler = SUBSURFACE_RADIUS_SCALE;
|
|
|
|
// Calculate the final step to fetch the surrounding pixels:
|
|
float finalStep = scale * HorizontalScaler;
|
|
|
|
finalStep *= GetProfileRadiusScale(ScreenSpaceData.GBuffer);
|
|
|
|
float PixelSizeRadius = finalStep / (FullResInputSize.z * 0.5f);
|
|
|
|
// tweaked for skin, a more flat kernel might need a smaller value, around 2 seems reasonable because we do half res
|
|
const float PixelSize = 4.0f;
|
|
|
|
float Ret = 1.0f;
|
|
|
|
//
|
|
Ret *= saturate(PixelSizeRadius - PixelSize);
|
|
// opacity allows to scale the radius - at some point we should fade in the full resolution, we don't have a masking other than that.
|
|
Ret *= saturate(ScreenSpaceData.GBuffer.CustomData.a * 10);
|
|
// todo: Subsurface has some non scatter contribution - all that should come from the Full res
|
|
|
|
return Ret;
|
|
}
|
|
|
|
// visualization (doesn't have to be fast)
|
|
void VisualizePS(in noperspective float4 UVAndScreenPos : TEXCOORD0, float4 SvPosition : SV_POSITION, out float4 OutColor : SV_Target0)
|
|
{
|
|
float2 UV = UVAndScreenPos.xy;
|
|
|
|
OutColor = Texture2DSample(PostprocessInput0, PostprocessInput0Sampler, UV);
|
|
|
|
int2 PixelPos = (int2)SvPosition.xy;
|
|
|
|
float2 ViewLocalUV = (PixelPos - View.ViewRectMin.xy) * View.ViewSizeAndInvSize.zw;
|
|
|
|
float2 IDAreaLocalUV = ViewLocalUV * 2 - 1.0f;
|
|
|
|
if (InUnitBox(IDAreaLocalUV))
|
|
{
|
|
float2 UV = View.ViewRectMin.xy * View.BufferSizeAndInvSize.zw + IDAreaLocalUV * (View.ViewSizeAndInvSize.xy * View.BufferSizeAndInvSize.zw);
|
|
|
|
FScreenSpaceData ScreenSpaceData = GetScreenSpaceData(UV);
|
|
|
|
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) * 0.5f;
|
|
}
|
|
if (SubsurfaceProfileInt == 2)
|
|
{
|
|
OutColor = float4(0, 1, 0, 0) * 0.5f;
|
|
}
|
|
if (SubsurfaceProfileInt == 3)
|
|
{
|
|
OutColor = float4(0, 0, 1, 0) * 0.5f;
|
|
}
|
|
if (SubsurfaceProfileInt == 4)
|
|
{
|
|
OutColor = float4(1, 0, 1, 0) * 0.5f;
|
|
}
|
|
if (SubsurfaceProfileInt == 5)
|
|
{
|
|
OutColor = float4(0, 1, 1, 0) * 0.5f;
|
|
}
|
|
if (SubsurfaceProfileInt == 6)
|
|
{
|
|
OutColor = float4(1, 1, 0, 0) * 0.5f;
|
|
}
|
|
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);
|
|
|
|
OutColor.rgb *= ComputeFullResLerp(ScreenSpaceData, UV, PostprocessInput0Size);
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
|
|
struct SDiffuseAndSpecular
|
|
{
|
|
float3 Diffuse;
|
|
float3 Specular;
|
|
};
|
|
|
|
// can be moved/shared
|
|
half3 LookupSceneColor(float2 SceneUV, int2 PixelOffset)
|
|
{
|
|
#if ES2_PROFILE && COMPILER_GLSL_ES2
|
|
// slower but always works
|
|
// to prevent "error: Texture offset not supported on GLSL ES"
|
|
return Texture2DSample(PostprocessInput0, PostprocessInput0Sampler, SceneUV + PixelOffset * PostprocessInput0Size.zw).rgb;
|
|
#else
|
|
// faster
|
|
return PostprocessInput0.SampleLevel(PostprocessInput0Sampler, SceneUV, 0, PixelOffset).rgb;
|
|
#endif
|
|
}
|
|
|
|
// @param UVSceneColor for the full res rendertarget (BufferSize) e.g. SceneColor or GBuffers
|
|
// @param ReconstructMethod 0/1/2/3 (should be a literal constant to allow compiler optimizations)
|
|
SDiffuseAndSpecular ReconstructLighting(float2 UVSceneColor, uint ReconstructMethod)
|
|
{
|
|
SDiffuseAndSpecular Ret;
|
|
|
|
// If SUBSURFACE_CHANNEL_MODE is 0, checkerboard is forced on
|
|
#if SUBSURFACE_PROFILE_CHECKERBOARD || SUBSURFACE_CHANNEL_MODE == 0
|
|
{
|
|
bool bChecker = CheckerFromSceneColorUV(UVSceneColor);
|
|
|
|
// todo: We could alternate the diagonal with TemporalAA or even only only 1 sample for low spec or 4 for high spec
|
|
|
|
float3 Quant0 = PostprocessInput0.SampleLevel(PostprocessInput0Sampler, UVSceneColor, 0).rgb;
|
|
|
|
// todo: expose as scalability setting (can be evaluate best without TemporalAA)
|
|
// 0:fast but pattern can appear, 1:better, 2: even better, 3: best but expensive
|
|
float3 Quant1;
|
|
|
|
if(ReconstructMethod == 0)
|
|
{
|
|
// cheap, crappy
|
|
Quant1 = LookupSceneColor(UVSceneColor, int2(1, 0));
|
|
}
|
|
else if(ReconstructMethod == 1)
|
|
{
|
|
// acceptable but not perfect
|
|
Quant1 = 0.5f * (
|
|
LookupSceneColor(UVSceneColor, int2( 1, 0)) +
|
|
LookupSceneColor(UVSceneColor, int2(-1, 0)));
|
|
}
|
|
else if(ReconstructMethod == 2)
|
|
{
|
|
// almost same as 1?
|
|
Quant1 = 0.25f * (
|
|
LookupSceneColor(UVSceneColor, int2( 1, 0)) +
|
|
LookupSceneColor(UVSceneColor, int2( 0, 1)) +
|
|
LookupSceneColor(UVSceneColor, int2(-1, 0)) +
|
|
LookupSceneColor(UVSceneColor, int2( 0, -1)));
|
|
}
|
|
else if(ReconstructMethod == 3)
|
|
{
|
|
// very good
|
|
float3 A = LookupSceneColor(UVSceneColor, int2( 1, 0));
|
|
float3 B = LookupSceneColor(UVSceneColor, int2(-1, 0));
|
|
float3 C = LookupSceneColor(UVSceneColor, int2( 0, 1));
|
|
float3 D = LookupSceneColor(UVSceneColor, int2( 0, -1));
|
|
|
|
// Luminance could be green channel only
|
|
float a = Luminance(A);
|
|
float b = Luminance(B);
|
|
float c = Luminance(C);
|
|
float d = Luminance(D);
|
|
|
|
float ab = abs(a - b);
|
|
float cd = abs(c - d);
|
|
|
|
// take the average in the direction that avoids dither pattern
|
|
Quant1 = 0.5f * lerp(A + B, C + D, ab > cd);
|
|
}
|
|
|
|
Ret.Diffuse = lerp(Quant1, Quant0, bChecker);
|
|
Ret.Specular = lerp(Quant0, Quant1, bChecker);
|
|
}
|
|
#else // SUBSURFACE_PROFILE_CHECKERBOARD
|
|
{
|
|
// If we're not doing checkerboard encoding, we just need to read a single pixel and decode (combined diffuse/spec in RGB)
|
|
float4 CenterSample = PostprocessInput0.SampleLevel(PostprocessInput0Sampler, UVSceneColor, 0);
|
|
float3 CombinedColor = CenterSample.rgb;
|
|
float DiffuseLuminance = CenterSample.a;
|
|
|
|
float CombinedLuminance = Luminance(CombinedColor);
|
|
float DiffuseFactor = saturate(DiffuseLuminance / CombinedLuminance);
|
|
float SpecularFactor = 1.0f - DiffuseFactor;
|
|
|
|
Ret.Diffuse = CombinedColor * DiffuseFactor;
|
|
Ret.Specular = CombinedColor * SpecularFactor;
|
|
}
|
|
#endif // !SUBSURFACE_PROFILE_CHECKERBOARD
|
|
|
|
return Ret;
|
|
}
|
|
|
|
// @param UVSceneColor for the full res rendertarget (BufferSize) e.g. SceneColor or GBuffers
|
|
// @return .RGB Color that should be scattared, .A:1 for subsurface scattering material, 0 for not
|
|
float4 SetupSubsurfaceForOnePixel(float2 UVSceneColor)
|
|
{
|
|
float4 Ret = 0;
|
|
|
|
FScreenSpaceData ScreenSpaceData = GetScreenSpaceData(UVSceneColor);
|
|
|
|
BRANCH if (ScreenSpaceData.GBuffer.ShadingModelID == SHADINGMODELID_SUBSURFACE_PROFILE)
|
|
{
|
|
// '1' is lower quality but that is acceptable here
|
|
SDiffuseAndSpecular DiffuseAndSpecular = ReconstructLighting(UVSceneColor, 1);
|
|
|
|
Ret.rgb = DiffuseAndSpecular.Diffuse;
|
|
|
|
// it's a valid sample
|
|
Ret.a = 1;
|
|
}
|
|
|
|
return Ret;
|
|
}
|
|
|
|
|
|
void SetupPS(in noperspective float4 UVAndScreenPos : TEXCOORD0, out float4 OutColor : SV_Target0)
|
|
{
|
|
float2 UV = UVAndScreenPos.xy;
|
|
|
|
#if HALF_RES
|
|
// order aligned with Gather() hardware implementation
|
|
// RGB: color*A, A:weight 0 if no subsurface scattering
|
|
float4 A = SetupSubsurfaceForOnePixel(UV + float2(-0.5, 0.5f) * PostprocessInput0Size.zw);
|
|
float4 B = SetupSubsurfaceForOnePixel(UV + float2( 0.5, 0.5f) * PostprocessInput0Size.zw);
|
|
float4 C = SetupSubsurfaceForOnePixel(UV + float2( 0.5, -0.5f) * PostprocessInput0Size.zw);
|
|
float4 D = SetupSubsurfaceForOnePixel(UV + float2(-0.5, -0.5f) * PostprocessInput0Size.zw);
|
|
|
|
float4 Sum = (A + B) + (C + D);
|
|
|
|
float Div = 1.0f / max(Sum.a, 0.00001f);
|
|
|
|
OutColor.rgb = Sum.rgb * Div;
|
|
|
|
float4 FourDepth = GatherSceneDepth(UV, PostprocessInput0Size.zw);
|
|
|
|
// average all valid depth values to a single one
|
|
float SingleDepth = dot(FourDepth, float4(A.a, B.a, C.a, D.a)) * Div;
|
|
|
|
OutColor.a = SingleDepth;
|
|
#else // HALF_RES
|
|
OutColor.rgb = SetupSubsurfaceForOnePixel(UV).rgb;
|
|
OutColor.a = CalcSceneDepth(UV);
|
|
#endif // HALF_RES
|
|
|
|
#if 0
|
|
float SSSScaleX = SSSParams.x;
|
|
|
|
float scale = SSSScaleX / SingleDepth;
|
|
// float scale = SSSScaleX / min(min(FourDepth.x, FourDepth.y), min(FourDepth.z, FourDepth.w));
|
|
|
|
float HorizontalScaler = SUBSURFACE_RADIUS_SCALE;
|
|
|
|
// Calculate the final step to fetch the surrounding pixels:
|
|
float finalStep = scale * HorizontalScaler;
|
|
|
|
FScreenSpaceData ScreenSpaceData = GetScreenSpaceData(UV);
|
|
|
|
finalStep *= GetProfileRadiusScale(ScreenSpaceData.GBuffer);
|
|
|
|
// in full resolution
|
|
float PixelFracationThreshold = 3.0f;
|
|
|
|
// *0.5f as we read in half res in the blur pass
|
|
if(finalStep < PixelFracationThreshold * PostprocessInput0Size.z * 0.5f)
|
|
{
|
|
// very small radius doesn't require work
|
|
OutColor = 0;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
// input0 is created by the SetupPS shader
|
|
void MainPS(noperspective float4 UVAndScreenPos : TEXCOORD0, out float4 OutColor : SV_Target0)
|
|
{
|
|
float2 ViewportUV = UVAndScreenPos.xy;
|
|
|
|
// call into "SeparableSSS.usf"
|
|
|
|
// the viewport is only a fraction of the buffersize, here we compensate for that
|
|
|
|
#if SSS_DIRECTION == 0
|
|
// horizontal
|
|
float2 ViewportDirectionUV = float2(1, 0) * SUBSURFACE_RADIUS_SCALE;
|
|
#else
|
|
// vertical
|
|
float2 ViewportDirectionUV = float2(0, 1) * SUBSURFACE_RADIUS_SCALE * (View.ViewSizeAndInvSize.x * View.ViewSizeAndInvSize.w);
|
|
#endif
|
|
|
|
// can be optimized
|
|
float2 GBufferUV = (ViewportUV * View.ViewSizeAndInvSize.xy + View.ViewRectMin.xy) * View.BufferSizeAndInvSize.zw;
|
|
|
|
OutColor = SSSSBlurPS(GBufferUV, ViewportUV, ViewportDirectionUV, false);
|
|
|
|
#if SSS_DIRECTION == 1
|
|
// second pass prepares the setup from the recombine pass which doesn't need depth but wants to reconstruct the color
|
|
OutColor.a = ComputeMaskFromDepthInAlpha(OutColor.a);
|
|
#endif
|
|
}
|
|
|
|
|
|
|
|
// Recombines the half res Subsurface filtered lighting contribution (upsampled and renormalized with the alpha)
|
|
// with the SceneColor.
|
|
void SubsurfaceRecombinePS(noperspective float4 UVAndScreenPos : TEXCOORD0, float4 SvPosition : SV_POSITION, out float4 OutColor : SV_Target0)
|
|
{
|
|
float2 ViewportUV = UVAndScreenPos.xy;
|
|
|
|
// recombine with the scene color
|
|
|
|
// can be optimized
|
|
float2 UVSceneColor = SvPosition.xy * View.BufferSizeAndInvSize.zw;
|
|
|
|
FScreenSpaceData ScreenSpaceData = GetScreenSpaceData(UVSceneColor);
|
|
|
|
if (ScreenSpaceData.GBuffer.ShadingModelID != SHADINGMODELID_SUBSURFACE_PROFILE)
|
|
{
|
|
OutColor = Texture2DSample(PostprocessInput0, PostprocessInput0Sampler, UVSceneColor);
|
|
return;
|
|
}
|
|
|
|
// bilinear filtering
|
|
float3 SSSColor;
|
|
{
|
|
float4 SSSColorWithAlpha = Texture2DSample(PostprocessInput1, PostprocessInput1Sampler, ViewportUV);
|
|
|
|
// renormalize to dilate RGB to fix half res upsampling artifacts
|
|
SSSColor = SSSColorWithAlpha.rgb / max(SSSColorWithAlpha.a, 0.00001f);
|
|
}
|
|
|
|
float LerpFactor = 1;
|
|
|
|
#if HALF_RES
|
|
#if 1
|
|
// fade out subsurface scattering if radius is too small to be more crips (not blend with half resolution)
|
|
// minor quality improvement (faces are more detailed in distance)
|
|
LerpFactor = ComputeFullResLerp(ScreenSpaceData, UVSceneColor, PostprocessInput1Size);
|
|
|
|
// hack to debug if the fade is happening at the same time the VisualizeSSS shows it
|
|
// SSSColor = 0;
|
|
#endif
|
|
#endif // HALF_RES
|
|
|
|
#if !RECOMBINE_SUBSURFACESCATTER
|
|
// Scalability requests no Scatter, but we still need to reconstruct a color
|
|
LerpFactor = 0;
|
|
#endif
|
|
|
|
// we multiply the base color later in to get more crips human skin textures (scanned data always has Subsurface included)
|
|
float3 StoredBaseColor = ScreenSpaceData.GBuffer.StoredBaseColor;
|
|
float StoredSpecular = ScreenSpaceData.GBuffer.StoredSpecular;
|
|
|
|
uint ReconstructMethod = RECOMBINE_QUALITY ? 3 : 1;
|
|
|
|
SDiffuseAndSpecular DiffuseAndSpecular = ReconstructLighting(UVSceneColor, ReconstructMethod);
|
|
|
|
float3 ExtractedNonSubsurface = DiffuseAndSpecular.Specular;
|
|
|
|
// asset specific color
|
|
float3 SubsurfaceColor = GetProfileSubsurfaceColor(ScreenSpaceData.GBuffer);
|
|
float3 FadedSubsurfaceColor = GetProfileSubsurfaceColor(ScreenSpaceData.GBuffer) * LerpFactor;
|
|
|
|
// hack to debug if the fade is happening at the same time the VisualizeSSS shows it
|
|
// SSSColor = float3(0,1,0);
|
|
|
|
// combine potentially half res with full res
|
|
float3 SubsurfaceLighting = lerp(DiffuseAndSpecular.Diffuse, SSSColor, FadedSubsurfaceColor);
|
|
|
|
|
|
OutColor = float4(SubsurfaceLighting * StoredBaseColor + ExtractedNonSubsurface, 0);
|
|
}
|