226 lines
8.4 KiB
HLSL
226 lines
8.4 KiB
HLSL
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
/*=============================================================================
|
|
LightAccumulator.usf: FLightAccumulator "class" and it's methods, useful for screen space subsurface scattering
|
|
=============================================================================*/
|
|
|
|
#pragma once
|
|
|
|
#include "Common.ush"
|
|
|
|
// set by c++, not set for LPV
|
|
// 0 / 1
|
|
#ifndef VISUALIZE_LIGHT_CULLING
|
|
#define VISUALIZE_LIGHT_CULLING 0
|
|
#endif
|
|
|
|
// for ScreenSpaceSubsurfaceScattering
|
|
// 0 : fastest (speculars leak in SSSSS). Forces checkerboard rendering for subsurface profile
|
|
// 1 : luminance specular - works well (needs 64bit SceneColor, stores luminance of specular in alpha, can be optimized further in LightAccumulator_Add() )
|
|
// NOTE: Dependent on View.bCheckerboardSubsurfaceProfileRendering - if this is off, we fall back to mode 0
|
|
// 2 : colored specular works well (best, requires another RT for light accumulation)
|
|
#define SUBSURFACE_CHANNEL_MODE 1
|
|
|
|
struct FLightAccumulator
|
|
{
|
|
float3 TotalLight;
|
|
float TotalLightLuminance;
|
|
|
|
// only actually used SUBSURFACE_CHANNEL_MODE == 1
|
|
// assumed to be compiled out otherwise (not compiled out with #if so we can use if() instead of #if for better readability and compiler error checking)
|
|
// input for ScreenSpaceSubsurfaceScattering
|
|
float ScatterableLightLuma;
|
|
|
|
// only actually used SUBSURFACE_CHANNEL_MODE == 2
|
|
// assumed to be compiled out otherwise (not compiled out with #if so we can use if() instead of #if for better readability and compiler error checking)
|
|
// input for ScreenSpaceSubsurfaceScattering
|
|
float3 ScatterableLight;
|
|
|
|
// only used for development (not compiled out with #if so we can use if() instead of #if for better readability and compiler error checking)
|
|
// assumed to be compiled out otherwise
|
|
float EstimatedCost;
|
|
|
|
// only used for alpha, which needs to keep specular and alpha separate since specular needs to multiply by 1/opacity to compensate for alpha blending
|
|
// assumed to be compiled out otherwise
|
|
float3 TotalLightDiffuse;
|
|
float3 TotalLightSpecular;
|
|
|
|
};
|
|
|
|
struct FDeferredLightingSplit
|
|
{
|
|
float4 DiffuseLighting;
|
|
float4 SpecularLighting;
|
|
float LightingLuminance;
|
|
};
|
|
|
|
// accumulate light, can be called multiple times
|
|
void LightAccumulator_AddSplit(inout FLightAccumulator In, float3 DiffuseTotalLight, float3 SpecularTotalLight, float3 ScatterableLight, float3 CommonMultiplier, const bool bNeedsSeparateSubsurfaceLightAccumulation)
|
|
{
|
|
// 3 mad
|
|
In.TotalLight += (DiffuseTotalLight + SpecularTotalLight) * CommonMultiplier;
|
|
In.TotalLightLuminance += Luminance((DiffuseTotalLight + SpecularTotalLight) * CommonMultiplier);
|
|
|
|
// This should ideally be evaluated statically outside of this function to avoid the branch
|
|
if (bNeedsSeparateSubsurfaceLightAccumulation)
|
|
{
|
|
if (SUBSURFACE_CHANNEL_MODE == 1)
|
|
{
|
|
if (View.bCheckerboardSubsurfaceProfileRendering == 0)
|
|
{
|
|
In.ScatterableLightLuma += Luminance(ScatterableLight * CommonMultiplier);
|
|
}
|
|
}
|
|
else if (SUBSURFACE_CHANNEL_MODE == 2)
|
|
{
|
|
// 3 mad
|
|
In.ScatterableLight += ScatterableLight * CommonMultiplier;
|
|
}
|
|
}
|
|
|
|
In.TotalLightDiffuse += DiffuseTotalLight * CommonMultiplier;
|
|
In.TotalLightSpecular += SpecularTotalLight * CommonMultiplier;
|
|
}
|
|
|
|
void LightAccumulator_Add(inout FLightAccumulator In, float3 TotalLight, float3 ScatterableLight, float3 CommonMultiplier, const bool bNeedsSeparateSubsurfaceLightAccumulation)
|
|
{
|
|
LightAccumulator_AddSplit(In, TotalLight, 0.0f, ScatterableLight, CommonMultiplier, bNeedsSeparateSubsurfaceLightAccumulation);
|
|
}
|
|
|
|
float4 ConvertEstimatedCostToColor(float EstimatedCost)
|
|
{
|
|
return 0.1f * float4(1.0f, 0.25f, 0.075f, 0) * EstimatedCost;
|
|
}
|
|
|
|
//
|
|
// compute final value to store in the MRT0
|
|
// @retrun RGB:SceneColor Specular and Diffuse, A:Non Specular SceneColor Luminance
|
|
float4 LightAccumulator_GetResult(FLightAccumulator In)
|
|
{
|
|
float4 Ret;
|
|
|
|
if (VISUALIZE_LIGHT_CULLING == 1)
|
|
{
|
|
// a soft gradient from dark red to bright white, can be changed to be different
|
|
Ret = ConvertEstimatedCostToColor(In.EstimatedCost);
|
|
}
|
|
else
|
|
{
|
|
Ret = float4(In.TotalLight, 0);
|
|
|
|
if (SUBSURFACE_CHANNEL_MODE == 1 )
|
|
{
|
|
// bSubsurfacePostprocessEnabled bCheckerboardSubsurfaceProfileRendering OutputLuma
|
|
// 0 0 0
|
|
// 0 1 0
|
|
// 1 0 1
|
|
// 1 1 0
|
|
// The alpha channel will not be used in the post process subsurface pass if View.bSubsurfacePostprocessEnabled
|
|
// is not enabled. Add this gate (View.bSubsurfacePostprocessEnabled) so that when subsurface is disabled, scene
|
|
// captures will not pick up ScatterableLightLuma to pollute the alpha channel.
|
|
if (View.bCheckerboardSubsurfaceProfileRendering == 0 && View.bSubsurfacePostprocessEnabled)
|
|
{
|
|
// RGB accumulated RGB HDR color, A: specular luminance for screenspace subsurface scattering
|
|
Ret.a = In.ScatterableLightLuma;
|
|
}
|
|
}
|
|
else if (SUBSURFACE_CHANNEL_MODE == 2)
|
|
{
|
|
// RGB accumulated RGB HDR color, A: view independent (diffuse) luminance for screenspace subsurface scattering
|
|
// 3 add, 1 mul, 2 mad, can be optimized to use 2 less temporary during accumulation and remove the 3 add
|
|
Ret.a = Luminance(In.ScatterableLight);
|
|
// todo, need second MRT for SUBSURFACE_CHANNEL_MODE==2
|
|
}
|
|
}
|
|
|
|
return Ret;
|
|
}
|
|
|
|
|
|
FDeferredLightingSplit LightAccumulator_GetResultSplit(FLightAccumulator In)
|
|
{
|
|
float4 RetDiffuse;
|
|
float4 RetSpecular;
|
|
|
|
if (VISUALIZE_LIGHT_CULLING == 1)
|
|
{
|
|
// a soft gradient from dark red to bright white, can be changed to be different
|
|
RetDiffuse = ConvertEstimatedCostToColor(In.EstimatedCost);
|
|
RetSpecular = RetDiffuse;
|
|
}
|
|
else
|
|
{
|
|
RetDiffuse = float4(In.TotalLightDiffuse, 0);
|
|
RetSpecular = float4(In.TotalLightSpecular, 0);
|
|
|
|
if (SUBSURFACE_CHANNEL_MODE == 1 )
|
|
{
|
|
if (View.bCheckerboardSubsurfaceProfileRendering == 0 && View.bSubsurfacePostprocessEnabled)
|
|
{
|
|
// RGB accumulated RGB HDR color, A: specular luminance for screenspace subsurface scattering
|
|
RetDiffuse.a = In.ScatterableLightLuma;
|
|
}
|
|
}
|
|
else if (SUBSURFACE_CHANNEL_MODE == 2)
|
|
{
|
|
// RGB accumulated RGB HDR color, A: view independent (diffuse) luminance for screenspace subsurface scattering
|
|
// 3 add, 1 mul, 2 mad, can be optimized to use 2 less temporary during accumulation and remove the 3 add
|
|
RetDiffuse.a = Luminance(In.ScatterableLight);
|
|
// todo, need second MRT for SUBSURFACE_CHANNEL_MODE==2
|
|
}
|
|
}
|
|
|
|
FDeferredLightingSplit Ret;
|
|
Ret.DiffuseLighting = RetDiffuse;
|
|
Ret.SpecularLighting = RetSpecular;
|
|
Ret.LightingLuminance = In.TotalLightLuminance;
|
|
|
|
return Ret;
|
|
}
|
|
|
|
struct FSubstrateDeferredLighting
|
|
{
|
|
float4 SceneColor; // Untouched scene luminance, alpha channel may contain scatterable light luminance.
|
|
#if SUBSTRATE_OPAQUE_ROUGH_REFRACTION_ENABLED
|
|
float3 OpaqueRoughRefractionSceneColor; // Luminance that is going to be blurier by the top layer roughness
|
|
float3 SubSurfaceSceneColor; // Scene color that should go through the sub surface post process. There is not alpha: not needed because it is separated from specular already.
|
|
#endif
|
|
|
|
// only used for development (not compiled out with #if so we can use if() instead of #if for better readability and compiler error checking)
|
|
// assumed to be compiled out otherwise
|
|
float EstimatedCost;
|
|
|
|
float3 TotalDiffuseLighting;
|
|
float3 TotalSpecularLighting;
|
|
};
|
|
|
|
FSubstrateDeferredLighting GetInitialisedSubstrateDeferredLighting()
|
|
{
|
|
FSubstrateDeferredLighting Result = (FSubstrateDeferredLighting)0;
|
|
return Result;
|
|
}
|
|
|
|
void AccumulateSubstrateDeferredLighting(inout FSubstrateDeferredLighting SubstrateLighting, FLightAccumulator In, bool bDiffuseIsSubsurface, bool bIsToplayer)
|
|
{
|
|
FDeferredLightingSplit DiffSpec = LightAccumulator_GetResultSplit(In);
|
|
|
|
#if SUBSTRATE_OPAQUE_ROUGH_REFRACTION_ENABLED
|
|
if (bIsToplayer)
|
|
{
|
|
SubstrateLighting.SceneColor += DiffSpec.SpecularLighting + (bDiffuseIsSubsurface ? 0 : DiffSpec.DiffuseLighting);
|
|
SubstrateLighting.OpaqueRoughRefractionSceneColor += 0.0;
|
|
SubstrateLighting.SubSurfaceSceneColor +=(bDiffuseIsSubsurface ? DiffSpec.DiffuseLighting : 0).rgb;
|
|
}
|
|
else // !bIsToplayer
|
|
{
|
|
SubstrateLighting.SceneColor += 0.0;
|
|
SubstrateLighting.OpaqueRoughRefractionSceneColor +=(DiffSpec.SpecularLighting +(bDiffuseIsSubsurface ? 0 : DiffSpec.DiffuseLighting)).rgb;
|
|
SubstrateLighting.SubSurfaceSceneColor +=(bDiffuseIsSubsurface ? DiffSpec.DiffuseLighting : 0).rgb;
|
|
}
|
|
#else
|
|
SubstrateLighting.SceneColor += DiffSpec.DiffuseLighting + DiffSpec.SpecularLighting;
|
|
#endif
|
|
|
|
SubstrateLighting.EstimatedCost += In.EstimatedCost;
|
|
}
|