Lux URP Hair Lighting.hlsl 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303
  1. // NOTE: Based on URP Lighting.hlsl which replaced some half3 with floats to avoid lighting artifacts on mobile
  2. // Hair lighting functions renamed to solves problems with LWRP 6.x
  3. #ifndef LIGHTWEIGHT_HAIRLIGHTING_INCLUDED
  4. #define LIGHTWEIGHT_HAIRLIGHTING_INCLUDED
  5. #include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Common.hlsl"
  6. #include "Packages/com.unity.render-pipelines.core/ShaderLibrary/EntityLighting.hlsl"
  7. #include "Packages/com.unity.render-pipelines.core/ShaderLibrary/ImageBasedLighting.hlsl"
  8. #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
  9. #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Shadows.hlsl"
  10. // From HDRP -----------------------------------------
  11. float RoughnessToBlinnPhongSpecularExponent_Lux(float roughness)
  12. {
  13. return clamp(2 * rcp(roughness * roughness) - 2, FLT_EPS, rcp(FLT_EPS));
  14. }
  15. //http://web.engr.oregonstate.edu/~mjb/cs519/Projects/Papers/HairRendering.pdf
  16. float3 ShiftTangent_Lux(float3 T, float3 N, float shift)
  17. {
  18. return normalize(T + N * shift);
  19. }
  20. // Note: this is Blinn-Phong, the original paper uses Phong.
  21. float3 D_KajiyaKay_Lux(float3 T, float3 H, float specularExponent)
  22. {
  23. float TdotH = dot(T, H);
  24. float sinTHSq = saturate(1.0 - TdotH * TdotH);
  25. float dirAttn = saturate(TdotH + 1.0); // Evgenii: this seems like a hack? Do we really need this?
  26. // Note: Kajiya-Kay is not energy conserving.
  27. // We attempt at least some energy conservation by approximately normalizing Blinn-Phong NDF.
  28. // We use the formulation with the NdotL.
  29. // See http://www.thetenthplanet.de/archives/255.
  30. float n = specularExponent;
  31. float norm = (n + 2) * rcp(2 * PI);
  32. return dirAttn * norm * PositivePow(sinTHSq, 0.5 * n);
  33. }
  34. /*
  35. #if (_USE_LIGHT_FACING_NORMAL)
  36. // The Kajiya-Kay model has a "built-in" transmission, and the 'NdotL' is always positive.
  37. float cosTL = dot(bsdfData.hairStrandDirectionWS, L);
  38. float sinTL = sqrt(saturate(1 - cosTL * cosTL));
  39. float NdotL = sinTL; // Corresponds to the cosine w.r.t. the light-facing normal
  40. #else
  41. // Double-sided Lambert.
  42. float NdotL = dot(bsdfData.normalWS, L);
  43. #endif
  44. */
  45. // From HDRP END -----------------------------------------
  46. /*
  47. This is input data:
  48. struct InputData
  49. {
  50. float3 positionWS;
  51. half3 normalWS;
  52. half3 viewDirectionWS;
  53. float4 shadowCoord;
  54. half fogCoord;
  55. half3 vertexLighting;
  56. half3 bakedGI;
  57. };
  58. */
  59. // Ref: Donald Revie - Implementing Fur Using Deferred Shading (GPU Pro 2)
  60. // The grain direction (e.g. hair or brush direction) is assumed to be orthogonal to the normal.
  61. // The returned normal is NOT normalized.
  62. half3 ComputeGrainNormal_Lux(half3 grainDir, half3 V)
  63. {
  64. half3 B = cross(-V, grainDir);
  65. return cross(B, grainDir);
  66. }
  67. // Fake anisotropic by distorting the normal.
  68. // The grain direction (e.g. hair or brush direction) is assumed to be orthogonal to N.
  69. // Anisotropic ratio (0->no isotropic; 1->full anisotropy in tangent direction)
  70. half3 GetAnisotropicModifiedNormal_Lux(half3 grainDir, half3 N, half3 V, half anisotropy)
  71. {
  72. half3 grainNormal = ComputeGrainNormal_Lux(grainDir, V);
  73. return lerp(N, grainNormal, anisotropy);
  74. }
  75. half3 GlobalIlluminationHair_Lux(
  76. //BRDFData brdfData,
  77. half3 albedo,
  78. half3 specular,
  79. half roughness,
  80. half perceptualRoughness,
  81. half occlusion,
  82. half3 bakedGI,
  83. half3 normalWS,
  84. half3 viewDirectionWS,
  85. half3 bitangentWS,
  86. half ambientReflection
  87. )
  88. {
  89. // We do not handle backfaces properly yet.
  90. half NdotV = dot(normalWS, viewDirectionWS);
  91. half s = sign(NdotV);
  92. // Lets fix this for reflections?
  93. //NdotV = s * NdotV;
  94. // Strengthen occlusion on backfaces
  95. //occlusion = lerp(occlusion * 0.5, occlusion, saturate(1 + s));
  96. // We do not "fix" the reflection vector. This gives us some scattering like reflections
  97. //half3 reflectNormalWS = GetAnisotropicModifiedNormal_Lux(s * bitangentWS, s * normalWS, viewDirectionWS, 0.6h);
  98. half3 reflectNormalWS = GetAnisotropicModifiedNormal_Lux(bitangentWS, normalWS, viewDirectionWS, 0.6h);
  99. half3 reflectVector = reflect(-viewDirectionWS, reflectNormalWS);
  100. half fresnelTerm = Pow4(1.0 - saturate(NdotV) );
  101. // ??? perceptualRoughness *= saturate(1.2 - 0.8); //abs(bsdfData.anisotropy));
  102. half3 indirectDiffuse = bakedGI * occlusion;
  103. half3 indirectSpecular = GlossyEnvironmentReflection(reflectVector, perceptualRoughness, occlusion) * ambientReflection;
  104. // EnvironmentBRDFHair
  105. half3 c = indirectDiffuse * albedo;
  106. float surfaceReduction = 1.0 / (roughness * roughness + 1.0);
  107. half reflectivity = ReflectivitySpecular(specular);
  108. half grazingTerm = saturate( (1.0h - roughness) + reflectivity);
  109. c += surfaceReduction * indirectSpecular * lerp(specular, grazingTerm, fresnelTerm);
  110. return c;
  111. }
  112. half3 LightingHair_Lux(
  113. half3 albedo,
  114. half3 specular,
  115. Light light,
  116. half3 normalWS,
  117. half geomNdotV,
  118. half3 viewDirectionWS,
  119. half roughness1,
  120. half roughness2,
  121. half3 t1,
  122. half3 t2,
  123. half3 specularTint,
  124. half3 secondarySpecularTint,
  125. half rimTransmissionIntensity
  126. )
  127. {
  128. half NdotL = dot(normalWS, light.direction);
  129. half LdotV = dot(light.direction, viewDirectionWS);
  130. float invLenLV = rsqrt(max(2.0 * LdotV + 2.0, FLT_EPS));
  131. half3 halfDir = (light.direction + viewDirectionWS) * invLenLV;
  132. half3 hairSpec1 = specularTint * D_KajiyaKay_Lux(t1, halfDir, roughness1);
  133. #if defined(_SECONDARYLOBE)
  134. half3 hairSpec2 = secondarySpecularTint * D_KajiyaKay_Lux(t2, halfDir, roughness2);
  135. #endif
  136. float NdotH = saturate(dot(normalWS, halfDir));
  137. half LdotH = saturate(dot(light.direction, halfDir));
  138. half3 F = F_Schlick(specular, LdotH);
  139. // Reflection
  140. half3 specR = 0.25h * F * (hairSpec1
  141. #if defined(_SECONDARYLOBE)
  142. + hairSpec2
  143. #endif
  144. ) * saturate(NdotL) * saturate(geomNdotV * HALF_MAX);
  145. // Transmission // Yibing's and Morten's hybrid scatter model hack.
  146. half scatterFresnel1 = pow(saturate(-LdotV), 9.0h) * pow(saturate(1.0h - geomNdotV * geomNdotV), 12.0h);
  147. // This looks shitty (using 20)
  148. //half scatterFresnel2 = saturate(PositivePow((1.0h - geomNdotV), 20.0h));
  149. half scatterFresnel2 = saturate(Pow4(1.0h - geomNdotV));
  150. half transmission = scatterFresnel1 + rimTransmissionIntensity * scatterFresnel2;
  151. half3 specT = albedo * transmission;
  152. half3 diffuse = albedo * saturate(NdotL);
  153. // combine
  154. half3 result = (diffuse + specR + specT) * light.color * light.distanceAttenuation * light.shadowAttenuation;
  155. return result;
  156. }
  157. half4 LuxURPHairFragment(
  158. InputData inputData,
  159. half3 tangentWS,
  160. half3 albedo,
  161. half3 specular,
  162. half occlusion,
  163. half3 emission,
  164. half3 noise,
  165. half specularShift,
  166. half3 specularTint,
  167. half perceptualRoughness,
  168. half secondarySpecularShift,
  169. half3 secondarySpecularTint,
  170. half secondaryPerceptualRoughness,
  171. half rimTransmissionIntensity,
  172. half ambientReflection
  173. )
  174. {
  175. // ShadowMask: To ensure backward compatibility we have to avoid using shadowMask input, as it is not present in older shaders
  176. #if defined(SHADOWS_SHADOWMASK) && defined(LIGHTMAP_ON)
  177. half4 shadowMask = inputData.shadowMask;
  178. #elif !defined (LIGHTMAP_ON)
  179. half4 shadowMask = unity_ProbesOcclusion;
  180. #else
  181. half4 shadowMask = half4(1, 1, 1, 1);
  182. #endif
  183. // TODO: Simplify this...
  184. perceptualRoughness = PerceptualSmoothnessToPerceptualRoughness(perceptualRoughness); // * saturate(noise.r * 2) );
  185. half roughness1 = PerceptualRoughnessToRoughness(perceptualRoughness);
  186. half pbRoughness1 = RoughnessToBlinnPhongSpecularExponent_Lux(roughness1);
  187. #if defined(_SECONDARYLOBE)
  188. secondaryPerceptualRoughness = PerceptualSmoothnessToPerceptualRoughness(secondaryPerceptualRoughness); // * saturate(noise.r * 2) );
  189. half roughness2 = PerceptualRoughnessToRoughness(secondaryPerceptualRoughness);
  190. half pbRoughness2 = RoughnessToBlinnPhongSpecularExponent_Lux(roughness2);
  191. #else
  192. secondaryPerceptualRoughness = 0;
  193. half roughness2 = 0;
  194. half pbRoughness2 = 0;
  195. #endif
  196. half geomNdotV = dot(inputData.normalWS, inputData.viewDirectionWS);
  197. // Adjust tangentWS in case normal mapping is enabled
  198. #if defined(_NORMALMAP)
  199. tangentWS = Orthonormalize(tangentWS, inputData.normalWS);
  200. #endif
  201. // Always calculate bitangent WS
  202. half3 bitangentWS = cross(inputData.normalWS, tangentWS);
  203. #if defined(_STRANDDIR_BITANGENT)
  204. half3 strandDirWS = bitangentWS;
  205. #else
  206. half3 strandDirWS = tangentWS;
  207. #endif
  208. half3 t1 = ShiftTangent_Lux(strandDirWS, inputData.normalWS, specularShift);
  209. #if defined(_SECONDARYLOBE)
  210. half3 t2 = ShiftTangent_Lux(strandDirWS, inputData.normalWS, secondarySpecularShift);
  211. #else
  212. half3 t2 = 0;
  213. #endif
  214. // Start Lighting
  215. // (From HDRP) Note: For Kajiya hair we currently rely on a single cubemap sample instead of two, as in practice smoothness of both lobe aren't too far from each other.
  216. // and we take smoothness of the secondary lobe as it is often more rough (it is the colored one).
  217. // NOPE: We use primary!!!!!
  218. // Main Light
  219. Light light = GetMainLight(inputData.shadowCoord);
  220. // SSAO
  221. #if defined(_SCREEN_SPACE_OCCLUSION)
  222. AmbientOcclusionFactor aoFactor = GetScreenSpaceAmbientOcclusion(inputData.normalizedScreenSpaceUV);
  223. // Does not play nicely with blend?!
  224. //light.color *= aoFactor.directAmbientOcclusion;
  225. occlusion = min(occlusion, aoFactor.indirectAmbientOcclusion);
  226. #endif
  227. half3 color = GlobalIlluminationHair_Lux(albedo, specular, roughness1, perceptualRoughness, occlusion, inputData.bakedGI, inputData.normalWS, inputData.viewDirectionWS, bitangentWS, ambientReflection);
  228. MixRealtimeAndBakedGI(light, inputData.normalWS, inputData.bakedGI, half4(0, 0, 0, 0));
  229. color += LightingHair_Lux(albedo, specular, light, inputData.normalWS, geomNdotV, inputData.viewDirectionWS, pbRoughness1, pbRoughness2, t1, t2, specularTint, secondarySpecularTint, rimTransmissionIntensity);
  230. // Additional Lights
  231. #ifdef _ADDITIONAL_LIGHTS
  232. uint pixelLightCount = GetAdditionalLightsCount();
  233. for (uint i = 0u; i < pixelLightCount; ++i) {
  234. // Light light = GetAdditionalPerObjectLight(index, positionWS); // here; shadowAttenuation = 1.0;
  235. // URP 10: We have to use the new GetAdditionalLight function
  236. Light light = GetAdditionalLight(i, inputData.positionWS, shadowMask);
  237. // Does not play nicely with blend?!
  238. #if defined(_SCREEN_SPACE_OCCLUSION)
  239. //light.color *= aoFactor.directAmbientOcclusion;
  240. #endif
  241. color += LightingHair_Lux(albedo, specular, light, inputData.normalWS, geomNdotV, inputData.viewDirectionWS, pbRoughness1, pbRoughness2, t1, t2, specularTint, secondarySpecularTint, rimTransmissionIntensity);
  242. }
  243. #endif
  244. #ifdef _ADDITIONAL_LIGHTS_VERTEX
  245. color += inputData.vertexLighting * albedo;
  246. #endif
  247. color += emission;
  248. return half4(color, 1);
  249. }
  250. #endif