Lux URP Translucent Lighting.hlsl 8.2 KB

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