164 lines
6.7 KiB
HLSL
164 lines
6.7 KiB
HLSL
// 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
|