Lux URP Skin Lighting.hlsl 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172
  1. // NOTE: Based on URP Lighting.hlsl which replaced some half3 with floats to avoid lighting artifacts on mobile
  2. #ifndef LIGHTWEIGHT_SKINLIGHTING_INCLUDED
  3. #define LIGHTWEIGHT_SKINLIGHTING_INCLUDED
  4. TEXTURE2D(_SkinLUT); SAMPLER(sampler_SkinLUT); float4 _SkinLUT_TexelSize;
  5. // Based on Minimalist CookTorrance BRDF
  6. // Implementation is slightly different from original derivation: http://www.thetenthplanet.de/archives/255
  7. //
  8. // * NDF [Modified] GGX
  9. // * Modified Kelemen and Szirmay-​Kalos for Visibility term
  10. // * Fresnel approximated with 1/LdotH
  11. half3 DirectBDRF_Lux(BRDFData brdfData, half3 normalWS, half3 lightDirectionWS, half3 viewDirectionWS)
  12. {
  13. #ifndef _SPECULARHIGHLIGHTS_OFF
  14. float3 halfDir = SafeNormalize(lightDirectionWS + viewDirectionWS);
  15. float NoH = saturate(dot(normalWS, halfDir));
  16. half LoH = saturate(dot(lightDirectionWS, halfDir));
  17. // GGX Distribution multiplied by combined approximation of Visibility and Fresnel
  18. // BRDFspec = (D * V * F) / 4.0
  19. // D = roughness² / ( NoH² * (roughness² - 1) + 1 )²
  20. // V * F = 1.0 / ( LoH² * (roughness + 0.5) )
  21. // See "Optimizing PBR for Mobile" from Siggraph 2015 moving mobile graphics course
  22. // https://community.arm.com/events/1155
  23. // Final BRDFspec = roughness² / ( NoH² * (roughness² - 1) + 1 )² * (LoH² * (roughness + 0.5) * 4.0)
  24. // We further optimize a few light invariant terms
  25. // brdfData.normalizationTerm = (roughness + 0.5) * 4.0 rewritten as roughness * 4.0 + 2.0 to a fit a MAD.
  26. float d = NoH * NoH * brdfData.roughness2MinusOne + 1.00001f;
  27. half LoH2 = LoH * LoH;
  28. half specularTerm = brdfData.roughness2 / ((d * d) * max(0.1h, LoH2) * brdfData.normalizationTerm);
  29. // On platforms where half actually means something, the denominator has a risk of overflow
  30. // clamp below was added specifically to "fix" that, but dx compiler (we convert bytecode to metal/gles)
  31. // sees that specularTerm have only non-negative terms, so it skips max(0,..) in clamp (leaving only min(100,...))
  32. #if defined (SHADER_API_MOBILE) || defined (SHADER_API_SWITCH)
  33. specularTerm = specularTerm - HALF_MIN;
  34. specularTerm = clamp(specularTerm, 0.0, 100.0); // Prevent FP16 overflow on mobiles
  35. #endif
  36. half3 color = specularTerm * brdfData.specular; // + brdfData.diffuse;
  37. return color;
  38. #else
  39. return 0; //brdfData.diffuse;
  40. #endif
  41. }
  42. half3 GlobalIllumination_Lux(BRDFData brdfData, half3 bakedGI, half occlusion, half3 normalWS, half3 viewDirectionWS,
  43. half specOccluison)
  44. {
  45. half3 reflectVector = reflect(-viewDirectionWS, normalWS);
  46. half fresnelTerm = Pow4(1.0 - saturate(dot(normalWS, viewDirectionWS)));
  47. half3 indirectDiffuse = bakedGI * occlusion;
  48. half3 indirectSpecular = GlossyEnvironmentReflection(reflectVector, brdfData.perceptualRoughness, occlusion) * specOccluison;
  49. return EnvironmentBRDF(brdfData, indirectDiffuse, indirectSpecular, fresnelTerm);
  50. }
  51. half3 LightingPhysicallyBasedSkin(BRDFData brdfData, half3 lightColor, half3 lightDirectionWS, half lightAttenuation, half3 normalWS, half3 viewDirectionWS, half NdotL, half NdotLUnclamped, half curvature, half skinMask)
  52. {
  53. //half3 radiance = lightColor * NdotL;
  54. half3 diffuseLighting = brdfData.diffuse * SAMPLE_TEXTURE2D_LOD(_SkinLUT, sampler_SkinLUT, float2( (NdotLUnclamped * 0.5 + 0.5), curvature), 0).rgb;
  55. diffuseLighting = lerp(brdfData.diffuse * NdotL, diffuseLighting, skinMask);
  56. return ( DirectBDRF_Lux(brdfData, normalWS, lightDirectionWS, viewDirectionWS) * NdotL + diffuseLighting ) * lightColor * lightAttenuation;
  57. }
  58. half3 LightingPhysicallyBasedSkin(BRDFData brdfData, Light light, half3 normalWS, half3 viewDirectionWS, half NdotL, half NdotLUnclamped, half curvature, half skinMask)
  59. {
  60. return LightingPhysicallyBasedSkin(brdfData, light.color, light.direction, light.distanceAttenuation * light.shadowAttenuation, normalWS, viewDirectionWS, NdotL, NdotLUnclamped, curvature, skinMask);
  61. }
  62. half4 LuxLWRPSkinFragmentPBR(InputData inputData, half3 albedo, half metallic, half3 specular,
  63. half smoothness, half occlusion, half3 emission, half alpha, half4 translucency, half AmbientReflection, half3 diffuseNormalWS, half3 subsurfaceColor, half curvature, half skinMask, half maskbyshadowstrength, half backScatter)
  64. {
  65. // #if defined(SHADOWS_SHADOWMASK) && defined(LIGHTMAP_ON)
  66. // half4 shadowMask = inputData.shadowMask;
  67. // #elif !defined (LIGHTMAP_ON)
  68. // half4 shadowMask = unity_ProbesOcclusion;
  69. // #else
  70. // half4 shadowMask = half4(1, 1, 1, 1);
  71. // #endif
  72. half4 shadowMask = half4(1, 1, 1, 1);
  73. BRDFData brdfData;
  74. InitializeBRDFData(albedo, metallic, specular, smoothness, alpha, brdfData);
  75. Light mainLight = GetMainLight(inputData.shadowCoord);
  76. half3 mainLightColor = mainLight.color;
  77. // SSAO
  78. #if defined(_SCREEN_SPACE_OCCLUSION)
  79. AmbientOcclusionFactor aoFactor = GetScreenSpaceAmbientOcclusion(inputData.normalizedScreenSpaceUV);
  80. mainLight.color *= aoFactor.directAmbientOcclusion;
  81. occlusion = min(occlusion, aoFactor.indirectAmbientOcclusion);
  82. #endif
  83. MixRealtimeAndBakedGI(mainLight, inputData.normalWS, inputData.bakedGI, half4(0, 0, 0, 0));
  84. half3 color = GlobalIllumination_Lux(brdfData, inputData.bakedGI, occlusion, inputData.normalWS, inputData.viewDirectionWS, AmbientReflection);
  85. // Backscattering
  86. #if defined(_BACKSCATTER)
  87. color += backScatter * SampleSH(-diffuseNormalWS) * albedo * occlusion * translucency.x * subsurfaceColor * skinMask;
  88. #endif
  89. half NdotLUnclamped = dot(diffuseNormalWS, mainLight.direction);
  90. half NdotL = saturate( dot(inputData.normalWS, mainLight.direction) );
  91. color += LightingPhysicallyBasedSkin(brdfData, mainLight, inputData.normalWS, inputData.viewDirectionWS, NdotL, NdotLUnclamped, curvature, skinMask);
  92. // Subsurface Scattering
  93. half transPower = translucency.y;
  94. half3 transLightDir = mainLight.direction + inputData.normalWS * translucency.w;
  95. half transDot = dot( transLightDir, -inputData.viewDirectionWS );
  96. transDot = exp2(saturate(transDot) * transPower - transPower);
  97. color += skinMask * subsurfaceColor * transDot * (1.0h - saturate(NdotLUnclamped)) * mainLightColor * lerp(1.0h, mainLight.shadowAttenuation, translucency.z) * translucency.x;
  98. #ifdef _ADDITIONAL_LIGHTS
  99. uint pixelLightCount = GetAdditionalLightsCount();
  100. for (uint i = 0u; i < pixelLightCount; ++i)
  101. {
  102. //Light light = GetAdditionalLight(i, inputData.positionWS);
  103. // Get index upfront as we need it for GetAdditionalLightShadowParams();
  104. int index = GetPerObjectLightIndex(i);
  105. // Light light = GetAdditionalPerObjectLight(index, inputData.positionWS); // here; shadowAttenuation = 1.0;
  106. // URP 10: We have to use the new GetAdditionalLight function or reconstruct it:
  107. Light light = GetAdditionalPerObjectLight(index, inputData.positionWS);
  108. half3 lightColor = light.color;
  109. #if defined(_SCREEN_SPACE_OCCLUSION)
  110. light.color *= aoFactor.directAmbientOcclusion;
  111. #endif
  112. #if USE_STRUCTURED_BUFFER_FOR_LIGHT_DATA
  113. half4 occlusionProbeChannels = _AdditionalLightsBuffer[index].occlusionProbeChannels;
  114. #else
  115. half4 occlusionProbeChannels = _AdditionalLightsOcclusionProbes[index];
  116. #endif
  117. light.shadowAttenuation = AdditionalLightShadow(index, inputData.positionWS, shadowMask, occlusionProbeChannels);
  118. half NdotLUnclamped = dot(diffuseNormalWS, light.direction);
  119. NdotL = saturate( dot(inputData.normalWS, light.direction) );
  120. color += LightingPhysicallyBasedSkin(brdfData, light, inputData.normalWS, inputData.viewDirectionWS, NdotL, NdotLUnclamped, curvature, skinMask);
  121. // Subsurface Scattering
  122. half4 shadowParams = GetAdditionalLightShadowParams(index);
  123. lightColor *= lerp(1, shadowParams.x, maskbyshadowstrength); // shadowParams.x == shadow strength, which is 0 for point lights
  124. transLightDir = light.direction + inputData.normalWS * translucency.w;
  125. transDot = dot( transLightDir, -inputData.viewDirectionWS );
  126. transDot = exp2(saturate(transDot) * transPower - transPower);
  127. color += skinMask * subsurfaceColor * transDot * (1.0h - saturate(NdotLUnclamped)) * lightColor * lerp(1.0h, light.shadowAttenuation, translucency.z) * light.distanceAttenuation * translucency.x;
  128. }
  129. #endif
  130. #ifdef _ADDITIONAL_LIGHTS_VERTEX
  131. color += inputData.vertexLighting * brdfData.diffuse;
  132. #endif
  133. color += emission;
  134. return half4(color, alpha);
  135. }
  136. #endif