Files
beyond/Assets/ThirdParty/Lux URP Essentials/Shaders/Includes/Lux URP Translucent Lighting.hlsl
2024-11-20 15:21:28 +01:00

164 lines
6.7 KiB
HLSL
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// NOTE: Based on URP Lighting.hlsl which rplaced some half3 with floats to avoid lighting artifacts on mobile
#ifndef LIGHTWEIGHT_TRANSLUCENTLIGHTING_INCLUDED
#define LIGHTWEIGHT_TRANSLUCENTLIGHTING_INCLUDED
// Based on Minimalist CookTorrance BRDF
// Implementation is slightly different from original derivation: http://www.thetenthplanet.de/archives/255
//
// * NDF [Modified] GGX
// * Modified Kelemen and Szirmay-Kalos for Visibility term
// * Fresnel approximated with 1/LdotH
half3 DirectBDRF_Lux(BRDFData brdfData, half3 normalWS, half3 lightDirectionWS, half3 viewDirectionWS)
{
#ifndef _SPECULARHIGHLIGHTS_OFF
float3 halfDir = SafeNormalize(lightDirectionWS + viewDirectionWS);
float NoH = saturate(dot(normalWS, halfDir));
half LoH = saturate(dot(lightDirectionWS, halfDir));
// GGX Distribution multiplied by combined approximation of Visibility and Fresnel
// BRDFspec = (D * V * F) / 4.0
// D = roughness² / ( NoH² * (roughness² - 1) + 1 )²
// V * F = 1.0 / ( LoH² * (roughness + 0.5) )
// See "Optimizing PBR for Mobile" from Siggraph 2015 moving mobile graphics course
// https://community.arm.com/events/1155
// Final BRDFspec = roughness² / ( NoH² * (roughness² - 1) + 1 )² * (LoH² * (roughness + 0.5) * 4.0)
// We further optimize a few light invariant terms
// brdfData.normalizationTerm = (roughness + 0.5) * 4.0 rewritten as roughness * 4.0 + 2.0 to a fit a MAD.
float d = NoH * NoH * brdfData.roughness2MinusOne + 1.00001f;
half LoH2 = LoH * LoH;
half specularTerm = brdfData.roughness2 / ((d * d) * max(0.1h, LoH2) * brdfData.normalizationTerm);
// On platforms where half actually means something, the denominator has a risk of overflow
// clamp below was added specifically to "fix" that, but dx compiler (we convert bytecode to metal/gles)
// sees that specularTerm have only non-negative terms, so it skips max(0,..) in clamp (leaving only min(100,...))
#if defined (SHADER_API_MOBILE) || defined (SHADER_API_SWITCH)
specularTerm = specularTerm - HALF_MIN;
specularTerm = clamp(specularTerm, 0.0, 100.0); // Prevent FP16 overflow on mobiles
#endif
half3 color = specularTerm * brdfData.specular + brdfData.diffuse;
return color;
#else
return brdfData.diffuse;
#endif
}
half3 GlobalIllumination_Lux(BRDFData brdfData, half3 bakedGI, half occlusion, half3 normalWS, half3 viewDirectionWS,
half specOccluison)
{
half3 reflectVector = reflect(-viewDirectionWS, normalWS);
half fresnelTerm = Pow4(1.0 - saturate(dot(normalWS, viewDirectionWS)));
half3 indirectDiffuse = bakedGI * occlusion;
half3 indirectSpecular = GlossyEnvironmentReflection(reflectVector, brdfData.perceptualRoughness, occlusion) * specOccluison;
return EnvironmentBRDF(brdfData, indirectDiffuse, indirectSpecular, fresnelTerm);
}
half3 LightingPhysicallyBasedWrapped(BRDFData brdfData, half3 lightColor, half3 lightDirectionWS, half lightAttenuation, half3 normalWS, half3 viewDirectionWS, half NdotL)
{
half3 radiance = lightColor * (lightAttenuation * NdotL);
return DirectBDRF_Lux(brdfData, normalWS, lightDirectionWS, viewDirectionWS) * radiance;
}
half3 LightingPhysicallyBasedWrapped(BRDFData brdfData, Light light, half3 normalWS, half3 viewDirectionWS, half NdotL)
{
return LightingPhysicallyBasedWrapped(brdfData, light.color, light.direction, light.distanceAttenuation * light.shadowAttenuation, normalWS, viewDirectionWS, NdotL);
}
half4 LuxURPTranslucentFragmentPBR(InputData inputData, half3 albedo, half metallic, half3 specular,
half smoothness, half occlusion, half3 emission, half alpha, half4 translucency, half AmbientReflection
#if defined(_CUSTOMWRAP)
, half wrap
#endif
#if defined(_STANDARDLIGHTING)
, half mask
#endif
, half maskbyshadowstrength
)
{
BRDFData brdfData;
InitializeBRDFData(albedo, metallic, specular, smoothness, alpha, brdfData);
Light mainLight = GetMainLight(inputData.shadowCoord);
MixRealtimeAndBakedGI(mainLight, inputData.normalWS, inputData.bakedGI, half4(0, 0, 0, 0));
half3 color = GlobalIllumination_Lux(brdfData, inputData.bakedGI, occlusion, inputData.normalWS, inputData.viewDirectionWS, AmbientReflection);
// Wrapped Diffuse
#if defined(_CUSTOMWRAP)
half w = wrap;
#if defined(_STANDARDLIGHTING)
w *= mask;
#endif
#else
half w = 0.4;
#endif
half NdotL = saturate((dot(inputData.normalWS, mainLight.direction) + w) / ((1 + w) * (1 + w)));
// NdotL = saturate( dot(inputData.normalWS, mainLight.direction) );
color += LightingPhysicallyBasedWrapped(brdfData, mainLight, inputData.normalWS, inputData.viewDirectionWS, NdotL);
// translucency
half transPower = translucency.y;
half3 transLightDir = mainLight.direction + inputData.normalWS * translucency.w;
half transDot = dot( transLightDir, -inputData.viewDirectionWS );
transDot = exp2(saturate(transDot) * transPower - transPower);
color += brdfData.diffuse * transDot * (1.0 - NdotL) * mainLight.color * lerp(1.0h, mainLight.shadowAttenuation, translucency.z) * translucency.x * 4
#if defined(_STANDARDLIGHTING)
* mask
#endif
;
#ifdef _ADDITIONAL_LIGHTS
uint pixelLightCount = GetAdditionalLightsCount();
for (uint i = 0u; i < pixelLightCount; ++i)
{
//Light light = GetAdditionalLight(i, inputData.positionWS);
// Get index upfront as we need it for GetAdditionalLightShadowParams();
int index = GetPerObjectLightIndex(i);
Light light = GetAdditionalPerObjectLight(index, inputData.positionWS);
// Wrapped Diffuse
NdotL = saturate((dot(inputData.normalWS, light.direction) + w) / ((1 + w) * (1 + w)));
color += LightingPhysicallyBasedWrapped(brdfData, light, inputData.normalWS, inputData.viewDirectionWS, NdotL);
half4 shadowParams = GetAdditionalLightShadowParams(index);
light.color *= lerp(1, shadowParams.x, maskbyshadowstrength); // shadowParams.x == shadow strength, which is 0 for point lights
// Translucency
transLightDir = light.direction + inputData.normalWS * translucency.w;
transDot = dot( transLightDir, -inputData.viewDirectionWS );
transDot = exp2(saturate(transDot) * transPower - transPower);
color += brdfData.diffuse * transDot * (1.0 - NdotL) * light.color * lerp(1.0h, light.shadowAttenuation, translucency.z) * light.distanceAttenuation * translucency.x * 4
#if defined(_STANDARDLIGHTING)
* mask
#endif
;
}
#endif
#ifdef _ADDITIONAL_LIGHTS_VERTEX
color += inputData.vertexLighting * brdfData.diffuse;
#endif
color += emission;
return half4(color, alpha);
}
#endif