Files
UnrealEngineUWP/Engine/Shaders/Private/MobileShadingModels.ush
Dmitriy Dyomin 038d909044 Support shading models for mobile deferred path when static lighting is disabled
#rb wei.liu
#jira none
#preflight 627b5f586842238976719cc3

[CL 20134438 by Dmitriy Dyomin in ue5-main branch]
2022-05-11 03:17:42 -04:00

450 lines
17 KiB
Plaintext

// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "BRDF.ush"
#include "MobileGGX.ush"
#include "ShadingCommon.ush"
#include "ShadowFilteringCommon.ush"
#include "DeferredShadingCommon.ush"
#include "HairBsdf.ush"
#include "BurleyNormalizedSSSCommon.ush"
#ifndef MAX_MOBILE_SHADOWCASCADES
#define MAX_MOBILE_SHADOWCASCADES 0
#endif
#ifndef MOBILE_SHADOW_QUALITY
#define MOBILE_SHADOW_QUALITY 2
#endif
#ifndef MOBILE_HIGH_QUALITY_BRDF
#define MOBILE_HIGH_QUALITY_BRDF 0
#endif
//It's always 0 in mobile deferred lighting shaders
#ifndef FULLY_ROUGH
#define FULLY_ROUGH 0
#endif
//It's always 0 in mobile deferred lighting shaders
#ifndef NONMETAL
#define NONMETAL 0
#endif
//It's always 0 in mobile deferred lighting shaders
#ifndef MATERIAL_SHADINGMODEL_SINGLELAYERWATER
#define MATERIAL_SHADINGMODEL_SINGLELAYERWATER 0
#endif
//It's always 0 in mobile deferred lighting shaders
#ifndef FORWARDSHADING_USE_HQ_ENV_BRDF
#define FORWARDSHADING_USE_HQ_ENV_BRDF 0
#endif
//It's always 1 in mobile deferred lighting shaders
#ifndef MOBILE_DEFERRED_LIGHTING
#define MOBILE_DEFERRED_LIGHTING 0
#endif
#ifndef DEFERRED_SHADING_PATH
#define DEFERRED_SHADING_PATH 0
#endif
/*------------------------------------------------------------------------------
Mobile Shading Models
------------------------------------------------------------------------------*/
struct FMobileDirectLighting
{
half3 Diffuse;
half3 Specular;
};
struct FMobileShadingModelContext
{
half Opacity;
half3 DiffuseColor;
half3 SpecularColor;
half3 EnvBrdf;
half NoV;
float3 DiffuseDir;
#if MATERIAL_SHADINGMODEL_SINGLELAYERWATER
half BaseMaterialCoverageOverWater;
half WaterVisibility;
float3 WaterDiffuseIndirectLuminance;
#endif
#if MOBILE_SHADINGMODEL_SUPPORT
half ClearCoat;
half ClearCoatRoughness;
// If we don't use this shading model the color should be black (don't generate shader code for unused data, don't do indirectlighting cache lighting with this color).
half3 SubsurfaceColor;
uint SubsurfaceProfileInt;
half Curvature;
half Lobe0Roughness;
half Lobe1Roughness;
half LobeMix;
#endif
};
half3 GetEnvBRDF(half3 SpecularColor, half Roughness, half NoV)
{
#if FORWARDSHADING_USE_HQ_ENV_BRDF || MOBILE_DEFERRED_LIGHTING
return EnvBRDF(SpecularColor, Roughness, NoV);
#elif NONMETAL
// If nothing is hooked up to Metalic and Specular,
// then defaults are the same as a non-metal,
// so this define is safe.
return EnvBRDFApproxNonmetal(Roughness, NoV).xxx;
#else
return EnvBRDFApprox(SpecularColor, Roughness, NoV);
#endif
}
#if MOBILE_SHADINGMODEL_SUPPORT
half CalculateCurvature(half3 WorldNormal, float3 WorldPosition)
{
#if 0
half DeltaNormal = length(abs(DDX(WorldNormal)) + abs(DDY(WorldNormal)));
half DeltaPosition = length(abs(DDX(WorldPosition)) + abs(DDY(WorldPosition))) * BURLEY_CM_2_MM;
half CurvatureApprox = DeltaNormal / DeltaPosition;
#else
half3 dNdx = ddx((HALF3_TYPE)WorldNormal);
half3 dNdy = ddy((HALF3_TYPE)WorldNormal);
half x = dot(dNdx, dNdx);
half y = dot(dNdy, dNdy);
half CurvatureApprox = pow(max(x, y), ResolvedView.NormalCurvatureToRoughnessScaleBias.z);
#endif
return CurvatureApprox;
}
void GetProfileDualSpecular(uint SubsurfaceProfileInt, half Roughness, half Opacity, out half2 LobeRoughness, out half LobeMix)
{
half4 Data = GetSubsurfaceProfileTexture(SSSS_DUAL_SPECULAR_OFFSET, SubsurfaceProfileInt);
half2 MaterialRoughnessToLobeRoughness = Data.xy * SSSS_MAX_DUAL_SPECULAR_ROUGHNESS;
LobeMix = Data.z;
half RoughnessLerpFactor = saturate(Opacity * 10.0f);
MaterialRoughnessToLobeRoughness.x = lerp(1.0f, MaterialRoughnessToLobeRoughness.x, RoughnessLerpFactor);
MaterialRoughnessToLobeRoughness.y = lerp(1.0f, MaterialRoughnessToLobeRoughness.y, RoughnessLerpFactor);
LobeRoughness.x = max(saturate(Roughness * MaterialRoughnessToLobeRoughness.x), 0.02f);
LobeRoughness.y = saturate(Roughness * MaterialRoughnessToLobeRoughness.y);
}
half4 GetSSProfilePreIntegratedValue(uint SubsurfaceProfileInt, half NoL, half Curvature)
{
float3 UV = float3((NoL * .5 + .5), Curvature, SubsurfaceProfileInt);
return Texture2DArraySampleLevel(View.SSProfilesPreIntegratedTexture, View.SSProfilesPreIntegratedSampler, UV, 0);
}
#endif
void InitShadingModelContext(inout FMobileShadingModelContext ShadingModelContext, inout FGBufferData GBuffer, half3 CameraVector)
{
half NoV = max(dot(GBuffer.WorldNormal, CameraVector), 0.0f);
// This is to prevent Vis to get inf when both NoL and NoV are 0.
ShadingModelContext.NoV = saturate(abs(NoV) + 1e-5);
ShadingModelContext.DiffuseDir = GBuffer.WorldNormal;
#if NONMETAL
GBuffer.DiffuseColor = GBuffer.BaseColor;
GBuffer.SpecularColor = 0.04;
#else
GBuffer.DiffuseColor = GBuffer.BaseColor - GBuffer.BaseColor * GBuffer.Metallic; // 1 mad
half DielectricSpecular = 0.08 * GBuffer.Specular;
GBuffer.SpecularColor = (DielectricSpecular - DielectricSpecular * GBuffer.Metallic) + GBuffer.BaseColor * GBuffer.Metallic; // 2 mad
#endif
#if MOBILE_EMULATION && !MOBILE_DEFERRED_LIGHTING
{
// this feature is only needed for development/editor - we can compile it out for a shipping build (see r.CompileShadersForDevelopment cvar help)
GBuffer.DiffuseColor = GBuffer.DiffuseColor * ResolvedView.DiffuseOverrideParameter.w + ResolvedView.DiffuseOverrideParameter.xyz;
GBuffer.SpecularColor = GBuffer.SpecularColor * ResolvedView.SpecularOverrideParameter.w + ResolvedView.SpecularOverrideParameter.xyz;
}
#endif
ShadingModelContext.DiffuseColor = GBuffer.DiffuseColor;
ShadingModelContext.SpecularColor = GBuffer.SpecularColor;
#if MATERIAL_SHADINGMODEL_SINGLELAYERWATER
ShadingModelContext.BaseMaterialCoverageOverWater = ShadingModelContext.Opacity;
ShadingModelContext.WaterVisibility = 1.0 - ShadingModelContext.BaseMaterialCoverageOverWater;
// Fade out diffuse as this will be handled by the single scattering lighting. when over the water surface.
// We keep the SpecularColor for sun/water interactions
ShadingModelContext.WaterDiffuseIndirectLuminance = 0;
ShadingModelContext.DiffuseColor *= ShadingModelContext.BaseMaterialCoverageOverWater;
#endif
#if MOBILE_SHADINGMODEL_SUPPORT
if (GBuffer.ShadingModelID == SHADINGMODELID_CLEAR_COAT)
{
ShadingModelContext.ClearCoat = GBuffer.CustomData.x;
ShadingModelContext.ClearCoatRoughness = clamp(GBuffer.CustomData.y, 0.015625, 1.0);
// Approximation of refraction's effect on EnvBRDF
half RefractionScale = ((NoV * 0.5 + 0.5) * NoV - 1) * saturate(1.25 - 1.25 * GBuffer.Roughness) + 1;
half Specular = lerp(GBuffer.Specular, RefractionScale, ShadingModelContext.ClearCoat);
half DielectricSpecular = 0.08 * Specular;
ShadingModelContext.SpecularColor = (DielectricSpecular - DielectricSpecular * GBuffer.Metallic) + GBuffer.BaseColor * GBuffer.Metallic; // 2 mad
}
else if (GBuffer.ShadingModelID == SHADINGMODELID_SUBSURFACE || GBuffer.ShadingModelID == SHADINGMODELID_PREINTEGRATED_SKIN)
{
ShadingModelContext.SubsurfaceColor = ExtractSubsurfaceColor(GBuffer);
ShadingModelContext.Opacity = GBuffer.CustomData.a;
}
else if (GBuffer.ShadingModelID == SHADINGMODELID_SUBSURFACE_PROFILE)
{
ShadingModelContext.SubsurfaceProfileInt = ExtractSubsurfaceProfileInt(GBuffer);
ShadingModelContext.Curvature = GBuffer.CustomData.g;
ShadingModelContext.Opacity = GBuffer.CustomData.a;
half2 LobeRoughness;
GetProfileDualSpecular(ShadingModelContext.SubsurfaceProfileInt, GBuffer.Roughness,
ShadingModelContext.Opacity, LobeRoughness, ShadingModelContext.LobeMix);
ShadingModelContext.Lobe0Roughness = LobeRoughness.x;
ShadingModelContext.Lobe1Roughness = LobeRoughness.y;
}
else if (GBuffer.ShadingModelID == SHADINGMODELID_HAIR)
{
FHairTransmittanceData TransmittanceData = InitHairTransmittanceData(true);
float3 N = GBuffer.WorldNormal;
float3 V = CameraVector;
float3 L = normalize(V - N * dot(V, N));
ShadingModelContext.DiffuseDir = L;
ShadingModelContext.DiffuseColor = 2 * PI * HairShading(GBuffer, L, V, N, 1, TransmittanceData, 0, 0.2, uint2(0, 0));
}
#endif
#if FULLY_ROUGH
EnvBRDFApproxFullyRough(ShadingModelContext.DiffuseColor, ShadingModelContext.SpecularColor);
ShadingModelContext.EnvBrdf = 0.0f;
#else
ShadingModelContext.EnvBrdf = GetEnvBRDF(ShadingModelContext.SpecularColor, GBuffer.Roughness, NoV);
#endif
}
half Vis_SmithJointApprox_Mobile(half a, half NoV, half NoL)
{
float Vis_SmithV = NoL * (NoV * (1 - a) + a);
float Vis_SmithL = NoV * (NoL * (1 - a) + a);
// clamp the visibility term to 0~1
return saturate(0.5f * rcp(max(Vis_SmithV + Vis_SmithL, 0.0001f)));
}
// [Schlick 1994, "An Inexpensive BRDF Model for Physically-Based Rendering"]
half3 F_Schlick_Mobile(half3 SpecularColor, half VoH)
{
half OneMinusVoH = 1 - VoH;
half Fc = OneMinusVoH * OneMinusVoH;
Fc = Fc * Fc;
Fc = Fc * OneMinusVoH;
//return Fc + (1 - Fc) * SpecularColor; // 1 add, 3 mad
// Anything less than 2% is physically impossible and is instead considered to be shadowing
return saturate(50.0 * SpecularColor.g) * Fc + (1 - Fc) * SpecularColor;
}
half3 MobileSpecularGGXInner(half D, half3 EnvBrdf, half3 SpecularColor, half Roughness, half NoV, half NoL, half VoH)
{
#if MOBILE_HIGH_QUALITY_BRDF
half Vis = Vis_SmithJointApprox_Mobile(Roughness * Roughness, NoV, NoL);
half3 F = F_Schlick_Mobile(SpecularColor, VoH);
#else
half Vis = (Roughness * 0.25 + 0.25);
half3 F = EnvBrdf;
#endif
return (D * Vis) * F;
}
half3 MobileSpecularGGX(half3 EnvBrdf, half3 SpecularColor, half Roughness, half NoV, half NoH, half NoL, half VoH)
{
half D = GGX_Mobile(Roughness, NoH);
return MobileSpecularGGXInner(D, EnvBrdf, SpecularColor, Roughness, NoV, NoL, VoH);
}
half3 MobileDualSpecularGGX(half AverageRoughness, half Lobe0Roughness, half Lobe1Roughness, half LobeMix, half3 EnvBrdf, half3 SpecularColor, half NoV, half NoH, half NoL, half VoH)
{
half D = lerp(GGX_Mobile(Lobe0Roughness, NoH), GGX_Mobile(Lobe1Roughness, NoH), LobeMix);
return MobileSpecularGGXInner(D, EnvBrdf, SpecularColor, AverageRoughness, NoV, NoL, VoH);
}
FMobileDirectLighting MobileDefaultLitBxDF(FMobileShadingModelContext ShadingModelContext, FGBufferData GBuffer, half NoL, half NoH, half VoH, half3 V, half3 L, half Shadow)
{
FMobileDirectLighting Lighting;
Lighting.Specular = Shadow * NoL * MobileSpecularGGX(ShadingModelContext.EnvBrdf, ShadingModelContext.SpecularColor, GBuffer.Roughness, ShadingModelContext.NoV, NoH, NoL, VoH);
Lighting.Diffuse = Shadow * NoL * ShadingModelContext.DiffuseColor;
return Lighting;
}
#if MOBILE_SHADINGMODEL_SUPPORT
FMobileDirectLighting MobileClearCoatBxDF(FMobileShadingModelContext ShadingModelContext, FGBufferData GBuffer, half NoL, half NoH, half VoH, half3 V, half3 L, half Shadow)
{
FMobileDirectLighting Lighting;
half ClearCoatRoughness = ShadingModelContext.ClearCoatRoughness;
half F0 = 0.04;
half Fc = Pow5(1 - VoH);
half F = Fc + (1 - Fc) * F0;
half LayerAttenuation = 1 - F;
LayerAttenuation *= LayerAttenuation;
// Vis_SmithJointApprox
half a = ClearCoatRoughness * ClearCoatRoughness;
float Vis = Vis_SmithJointApprox_Mobile(a, NoL, ShadingModelContext.NoV);
Lighting.Specular = NoL * ShadingModelContext.ClearCoat * F * Vis * GGX_Mobile(ClearCoatRoughness, NoH);
half Eta = 0.66666667f;
half RefractionBlendFactor = (0.63 - 0.22 * VoH) * VoH - 0.745;
half RefractionProjectionTerm = RefractionBlendFactor * NoH;
half BottomNoV = saturate(Eta * ShadingModelContext.NoV - RefractionProjectionTerm);
half BottomNoL = saturate(Eta * NoL - RefractionProjectionTerm);
half3 Transmission = 0.0;
if (BottomNoL > 0.0 && BottomNoV > 0.0)
{
// Normalized layer thickness documented for clarity
half ThinDistance = (rcp(BottomNoV) + rcp(BottomNoL));
half AbsorptionMix = GBuffer.Metallic;
Transmission = 1.0;
if (AbsorptionMix > 0.0)
{
// Base color represents reflected color viewed at 0 incidence angle, after being absorbed through the substrate.
// Because of this, extinction is normalized by traveling through layer thickness twice
half3 TransmissionColor = GBuffer.BaseColor;
half3 ExtinctionCoefficient = -log(TransmissionColor) * 0.5f;
half3 OpticalDepth = ExtinctionCoefficient * max(ThinDistance - 2.0, 0.0);
Transmission = saturate(exp(-OpticalDepth));
Transmission = lerp(1.0, Transmission, AbsorptionMix);
}
}
half3 CommonDiffuse = ShadingModelContext.DiffuseColor;
half3 DefaultDiffuse = NoL;
half3 RefractedDiffuse = (LayerAttenuation * BottomNoL) * Transmission;
Lighting.Diffuse = Shadow * CommonDiffuse * lerp(DefaultDiffuse, RefractedDiffuse, ShadingModelContext.ClearCoat);
half3 CommonSpecular = MobileSpecularGGX(ShadingModelContext.EnvBrdf, ShadingModelContext.SpecularColor, GBuffer.Roughness, ShadingModelContext.NoV, NoH, NoL, VoH);
half3 DefaultSpecular = NoL;
half3 RefractedSpecular = LayerAttenuation * Transmission * BottomNoL;
Lighting.Specular += CommonSpecular * lerp(DefaultSpecular, RefractedSpecular, ShadingModelContext.ClearCoat);
Lighting.Specular *= Shadow;
return Lighting;
}
FMobileDirectLighting MobileHairBxDF(FMobileShadingModelContext ShadingModelContext, FGBufferData GBuffer, half NoL, half NoH, half VoH, half3 V, half3 L, half Shadow)
{
FHairTransmittanceData HairTransmittance = InitHairTransmittanceData();
const float3 BsdfValue = HairShading(GBuffer, L, V, GBuffer.WorldNormal, Shadow, HairTransmittance, 1, 0, uint2(0, 0));
half3 Transmission = BsdfValue * PI; // multiply PI since the LightColor is divided by PI in C++;
FMobileDirectLighting Lighting;
Lighting.Diffuse = Transmission * Shadow;
Lighting.Specular = 0;
return Lighting;
}
FMobileDirectLighting MobileSubsurfaceBxDF(FMobileShadingModelContext ShadingModelContext, FGBufferData GBuffer, half NoL, half NoH, half VoH, half3 V, half3 L, half Shadow)
{
FMobileDirectLighting Lighting;
Lighting = MobileDefaultLitBxDF(ShadingModelContext, GBuffer, NoL, NoH, VoH, V, L, Shadow);
half InScatter = pow(saturate(dot(L, -V)), 12) * lerp(3, .1f, ShadingModelContext.Opacity);
half NormalContribution = saturate(NoH * ShadingModelContext.Opacity + 1 - ShadingModelContext.Opacity);
half BackScatter = GBuffer.GBufferAO * NormalContribution / (PI * 2);
// lerp to never exceed 1 (energy conserving)
half3 Transmission = lerp(BackScatter, 1, InScatter) * ShadingModelContext.SubsurfaceColor * PI; // multiply PI since the LightColor is divided by PI in C++
Lighting.Diffuse += Transmission * Shadow;
return Lighting;
}
FMobileDirectLighting MobilePreintegratedSkinBxDF(FMobileShadingModelContext ShadingModelContext, FGBufferData GBuffer, half NoL, half NoH, half VoH, half3 V, half3 L, half Shadow)
{
FMobileDirectLighting Lighting;
Lighting = MobileDefaultLitBxDF(ShadingModelContext, GBuffer, NoL, NoH, VoH, V, L, Shadow);
half3 PreintegratedBRDF = Texture2DSampleLevel(View.PreIntegratedBRDF, View.PreIntegratedBRDFSampler, float2(saturate(NoL * .5 + .5), 1 - ShadingModelContext.Opacity), 0).rgb;
half3 Transmission = PreintegratedBRDF * ShadingModelContext.SubsurfaceColor * PI; // multiply PI since the LightColor is divided by PI in C++
Lighting.Diffuse += Transmission * Shadow;
return Lighting;
}
FMobileDirectLighting MobileSubsurfaceProfileBxDF(FMobileShadingModelContext ShadingModelContext, FGBufferData GBuffer, half NoL, half NoH, half VoH, half3 V, half3 L, half Shadow)
{
FMobileDirectLighting Lighting;
half UnClampedNoL = dot(GBuffer.WorldNormal, L);
half ShadowFactor = 1 - sqrt(Shadow);
// Rotate the world normal based on the shadow value, it's just a experimental value
half UnClampedRotatedNoL = max(UnClampedNoL - max(2.0f * UnClampedNoL, 0.4f) * ShadowFactor, -1.0f);
half4 BurleyDiffuse = GetSSProfilePreIntegratedValue(ShadingModelContext.SubsurfaceProfileInt, UnClampedRotatedNoL, ShadingModelContext.Curvature);
Lighting.Diffuse = BurleyDiffuse.rgb * ShadingModelContext.DiffuseColor;
half AverageRoughness = lerp(ShadingModelContext.Lobe0Roughness, ShadingModelContext.Lobe1Roughness, ShadingModelContext.LobeMix);
Lighting.Specular = Shadow * NoL * MobileDualSpecularGGX(AverageRoughness, ShadingModelContext.Lobe0Roughness, ShadingModelContext.Lobe1Roughness, ShadingModelContext.LobeMix, ShadingModelContext.EnvBrdf, ShadingModelContext.SpecularColor, ShadingModelContext.NoV, NoH, NoL, VoH);
return Lighting;
}
#endif
FMobileDirectLighting MobileIntegrateBxDF(FMobileShadingModelContext ShadingModelContext, FGBufferData GBuffer, half NoL, half NoH, half VoH, half3 V, half3 L, half Shadow = 1.0f)
{
#if MOBILE_SHADINGMODEL_SUPPORT
if (GBuffer.ShadingModelID == SHADINGMODELID_CLEAR_COAT)
{
return MobileClearCoatBxDF(ShadingModelContext, GBuffer, NoL, NoH, VoH, V, L, Shadow);
}
else if (GBuffer.ShadingModelID == SHADINGMODELID_HAIR)
{
return MobileHairBxDF(ShadingModelContext, GBuffer, NoL, NoH, VoH, V, L, Shadow);
}
else if (GBuffer.ShadingModelID == SHADINGMODELID_SUBSURFACE)
{
return MobileSubsurfaceBxDF(ShadingModelContext, GBuffer, NoL, NoH, VoH, V, L, Shadow);
}
else if (GBuffer.ShadingModelID == SHADINGMODELID_PREINTEGRATED_SKIN)
{
return MobilePreintegratedSkinBxDF(ShadingModelContext, GBuffer, NoL, NoH, VoH, V, L, Shadow);
}
else if (GBuffer.ShadingModelID == SHADINGMODELID_SUBSURFACE_PROFILE)
{
return MobileSubsurfaceProfileBxDF(ShadingModelContext, GBuffer, NoL, NoH, VoH, V, L, Shadow);
}
#endif
return MobileDefaultLitBxDF(ShadingModelContext, GBuffer, NoL, NoH, VoH, V, L, Shadow);
}