221 lines
8.4 KiB
HLSL
221 lines
8.4 KiB
HLSL
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
#pragma once
|
|
|
|
/*=============================================================================
|
|
TransmissionThickness.ush: Calc Transmission Thickness shaders.
|
|
=============================================================================*/
|
|
#include "TransmissionCommon.ush"
|
|
/*-------------------------------------------------------------------------------
|
|
Functions for Transmission Thickness
|
|
-------------------------------------------------------------------------------*/
|
|
float4x4 ScreenToShadowMatrix;
|
|
|
|
// x is start, z is end,From 1 to 32 sample
|
|
// .x:DepthBias, .y:SlopeDepthBias, .z:MaxSlopeDepthBias, .w: MaxSubjectZ - MinSubjectZ
|
|
float4 ProjectionDepthBiasParameters;
|
|
|
|
// Poisson disks generated with http://www.coderhaus.com/?p=11
|
|
// Combine all Poisson disk togeather, borrow idea from Jorge's multiple sample single scattering
|
|
// http://www.iryoku.com/translucency/
|
|
|
|
int PoissonMethod;
|
|
static int2 PoissonStartAndEnd[] =
|
|
{
|
|
int2(0, 1),
|
|
int2(1, 3),
|
|
int2(3, 7),
|
|
int2(7, 15),
|
|
int2(15, 31),
|
|
int2(31, 63),
|
|
};
|
|
|
|
static const float2 PoissonDisk[] =
|
|
{
|
|
float2(0.402211, 0.126575),
|
|
float2(0.297056, 0.616830),
|
|
float2(0.298156, -0.001704),
|
|
float2(0.019369, 0.395482),
|
|
float2(-0.066918, -0.367739),
|
|
float2(-0.955010, 0.372377),
|
|
float2(0.800057, 0.120602),
|
|
float2(-0.7494944, 0.1827986),
|
|
float2(-0.8572887, -0.4169083),
|
|
float2(-0.1087135, -0.05238153),
|
|
float2(0.1045462, 0.9657645),
|
|
float2(-0.0135659, -0.698451),
|
|
float2(-0.4942278, 0.7898396),
|
|
float2(0.7970678, -0.4682421),
|
|
float2(0.8084122, 0.533884),
|
|
float2(0.4036454, -0.793536),
|
|
float2(0.3826454, -0.2730118),
|
|
float2(-0.04124885, -0.5971786),
|
|
float2(-0.3709261, -0.9179904),
|
|
float2(-0.3795351, -0.3353493),
|
|
float2(-0.3154466, 0.1069074),
|
|
float2(-0.7671808, -0.6143452),
|
|
float2(-0.4865215, 0.6395131),
|
|
float2(0.2359872, 0.1510548),
|
|
float2(0.03092861, 0.7309022),
|
|
float2(-0.82846, -0.1055831),
|
|
float2(-0.8732378, 0.3034171),
|
|
float2(0.9268684, -0.116035),
|
|
float2(0.6980102, 0.3764873),
|
|
float2(0.8239923, -0.515003),
|
|
float2(0.5084407, 0.7533528),
|
|
float2(-0.975402, -0.0711386),
|
|
float2(-0.920347, -0.41142),
|
|
float2(-0.883908, 0.217872),
|
|
float2(-0.884518, 0.568041),
|
|
float2(-0.811945, 0.90521),
|
|
float2(-0.792474, -0.779962),
|
|
float2(-0.614856, 0.386578),
|
|
float2(-0.580859, -0.208777),
|
|
float2(-0.53795, 0.716666),
|
|
float2(-0.515427, 0.0899991),
|
|
float2(-0.454634, -0.707938),
|
|
float2(-0.420942, 0.991272),
|
|
float2(-0.261147, 0.588488),
|
|
float2(-0.211219, 0.114841),
|
|
float2(-0.146336, -0.259194),
|
|
float2(-0.139439, -0.888668),
|
|
float2(0.0116886, 0.326395),
|
|
float2(0.0380566, 0.625477),
|
|
float2(0.0625935, -0.50853),
|
|
float2(0.125584, 0.0469069),
|
|
float2(0.169469, -0.997253),
|
|
float2(0.320597, 0.291055),
|
|
float2(0.359172, -0.633717),
|
|
float2(0.435713, -0.250832),
|
|
float2(0.507797, -0.916562),
|
|
float2(0.545763, 0.730216),
|
|
float2(0.56859, 0.11655),
|
|
float2(0.743156, -0.505173),
|
|
float2(0.736442, -0.189734),
|
|
float2(0.843562, 0.357036),
|
|
float2(0.865413, 0.763726),
|
|
float2(0.872005, -0.927)
|
|
};
|
|
|
|
float CalcShadowLinearDepth(float shadowDepth, float2 param)
|
|
{
|
|
return 1.0f / (shadowDepth * param.x - param.y);
|
|
}
|
|
|
|
float CalculateThickness(float ShadowmapDepth, float SceneDepth, float DensityMulConstant, float NormalScale, float NoL)
|
|
{
|
|
// Determine the distance that the light traveled through the subsurface object
|
|
// This assumes that anything between this subsurface pixel and the light was also a subsurface material,
|
|
// As a result, subsurface materials receive leaked light based on how transparent they are
|
|
float Thickness = (SceneDepth - ShadowmapDepth) * DensityMulConstant;
|
|
Thickness = clamp(abs(Thickness > 0 ? Thickness + NormalScale : max(0.0, Thickness * NoL + NormalScale)), 0.15, 5) + 0.25;
|
|
// Never shadow from depths that were never written to (max depth value)
|
|
return Thickness;
|
|
}
|
|
|
|
float CalcTransmissionThickness(float3 ViewPos, float3 LightPos, float3 WorldNormal, FTransmissionProfileParams TransmissionParams, FPCFSamplerSettings Settings)
|
|
{
|
|
///Get -NoL, maybe unnecessary.
|
|
float4 HomogeneousWorldPosition = mul(float4(ViewPos.xyz, 1), DFHackToFloat(PrimaryView.ScreenToWorld));
|
|
float3 WorldPosition = HomogeneousWorldPosition.xyz / HomogeneousWorldPosition.w;
|
|
float3 ToLight = LightPos.xyz - WorldPosition;
|
|
float DistanceSqr = dot(ToLight, ToLight);
|
|
float3 L = ToLight * rsqrt(DistanceSqr);
|
|
float NoL = 1;// dot(-L, WorldNormal);
|
|
|
|
//Shrink Pos in View Space.
|
|
float3 ViewNormal = mul(WorldNormal, (float3x3)View.TranslatedWorldToView);
|
|
|
|
//Shrink world position with neg worldNormal to get thickness
|
|
//These scalar const is based on adjust expericence.
|
|
float DepthScale = TransmissionParams.ExtinctionScale * 3.1f;
|
|
float NormalScale = TransmissionParams.NormalScale * 0.5;
|
|
|
|
float ShadowFilterRadius = ShadowBufferSize.w;
|
|
|
|
float4 ShrinkedPos = float4(ViewPos.xyz - (NormalScale)* ViewNormal.xyz, 1.0);
|
|
float4 ShrinkedScreenPos = mul(ShrinkedPos, ScreenToShadowMatrix);
|
|
|
|
|
|
ShrinkedScreenPos.xy = ShrinkedScreenPos.xy / ShrinkedScreenPos.w;
|
|
float DensityMulConstant = DepthScale * ProjectionDepthBiasParameters.w;
|
|
|
|
|
|
Settings.SceneDepth = (1.0f - ShrinkedScreenPos.z) + ProjectionDepthBiasParameters.x;
|
|
//Alpha is the thickness.
|
|
|
|
float ThicknessSum = 0.0;
|
|
uint PoissonMethod = 4;
|
|
float SampleNum = PoissonStartAndEnd[PoissonMethod].y - PoissonStartAndEnd[PoissonMethod].x;
|
|
|
|
for (int i = PoissonStartAndEnd[PoissonMethod].x; i < PoissonStartAndEnd[PoissonMethod].y; i++)
|
|
{
|
|
float2 DeltUv = PoissonDisk[i] * ShadowFilterRadius;
|
|
float2 ShadowUv = ShrinkedScreenPos.xy + DeltUv;
|
|
float ShadowmapDepth = Texture2DSampleLevel(Settings.ShadowDepthTexture, Settings.ShadowDepthTextureSampler, ShadowUv.xy, 0).r;
|
|
float Thickness = CalculateThickness(ShadowmapDepth, Settings.SceneDepth, DensityMulConstant, NormalScale, NoL);
|
|
ThicknessSum += (Thickness);
|
|
}
|
|
float Thickness = ThicknessSum / SampleNum;
|
|
|
|
return EncodeThickness(Thickness / SSSS_MAX_TRANSMISSION_PROFILE_DISTANCE);
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Computes thickness for a given world position from a cubemap shadowmap used on a point light. */
|
|
float CalcTransmissionThickness(float3 WorldNormal, FTransmissionProfileParams TransmissionParams, float3 WorldSampleToLightVec, float2 ProjParam, float LightInvRadius, float DepthBias)
|
|
{
|
|
float Thickness = 1;
|
|
|
|
float DepthScale = TransmissionParams.ExtinctionScale * 3.1f;
|
|
float NormalScale = TransmissionParams.NormalScale * 0.5;
|
|
|
|
//Shrink pos aligh -WorldNormal
|
|
float3 Normal = WorldNormal;
|
|
float3 LightRelativeBiasedPosition = -WorldSampleToLightVec - Normal * NormalScale;
|
|
float DistanceSqr = dot(WorldSampleToLightVec, WorldSampleToLightVec);
|
|
float3 L = WorldSampleToLightVec * rsqrt(DistanceSqr);
|
|
float NoL = pow(saturate(1 * dot(-L, Normal)), 1);
|
|
float Distance = length(WorldSampleToLightVec);
|
|
|
|
float ShadowFilterRadius = 1.0f;
|
|
|
|
BRANCH // Skip pixels outside of the light's influence
|
|
if (Distance * LightInvRadius < 1.0f)
|
|
{
|
|
float3 NormalizedLightVector = WorldSampleToLightVec / Distance, SideVector = 0, UpVector = 0;
|
|
int CubeFaceIndex = 0;
|
|
CalcCubemapVectorAndFaceIndex(NormalizedLightVector, ShadowFilterRadius, SideVector, UpVector, CubeFaceIndex);
|
|
// Transform the position into shadow space
|
|
float4 ShadowPosition = mul(float4(LightRelativeBiasedPosition, 1), ShadowViewProjectionMatrices[CubeFaceIndex]);
|
|
|
|
// Calculate the Z buffer value that would have been stored for this position in the shadow map
|
|
float CompareDistance = ShadowPosition.z / ShadowPosition.w;
|
|
float ShadowDepthBias = -DepthBias / ShadowPosition.w;
|
|
|
|
//Shadow Depth range Near to far, so no need mul far here.
|
|
float DensityMulConstant = TransmissionParams.ExtinctionScale * (10.0f / LightInvRadius);
|
|
float SceneDepth = CalcShadowLinearDepth(CompareDistance + ShadowDepthBias, ProjParam) * LightInvRadius;
|
|
|
|
Thickness = 0;
|
|
|
|
UNROLL for (int i = 0; i < 5; ++i)
|
|
{
|
|
float2 DeltUv = DiscSamples5[i] * ShadowFilterRadius;
|
|
float3 SamplePos = NormalizedLightVector + SideVector * DeltUv.x + UpVector * DeltUv.y;
|
|
#if USE_SEPARATE_SHADOW_DEPTH_CUBE_TEXTURE
|
|
float ShadowSample = TextureCubeSampleLevel(ShadowDepthCubeTexture2, ShadowDepthTextureSampler, SamplePos, 0.0).r;
|
|
#else
|
|
float ShadowSample = TextureCubeSampleDepthLevel(ShadowDepthCubeTexture, ShadowDepthTextureSampler, SamplePos, 0.0);
|
|
#endif
|
|
float ShadowDepth = CalcShadowLinearDepth(ShadowSample, ProjParam) * LightInvRadius;
|
|
Thickness += CalculateThickness(ShadowDepth, SceneDepth, DensityMulConstant, NormalScale, NoL);
|
|
}
|
|
Thickness /= 5;
|
|
|
|
}
|
|
return EncodeThickness(Thickness / SSSS_MAX_TRANSMISSION_PROFILE_DISTANCE);
|
|
}
|