HairLightingEquations.azsli 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244
  1. /*
  2. * Modifications Copyright (c) Contributors to the Open 3D Engine Project.
  3. * For complete copyright and license terms please see the LICENSE at the root of this distribution.
  4. *
  5. * SPDX-License-Identifier: Apache-2.0 OR MIT
  6. *
  7. */
  8. #pragma once
  9. // --- Overview ---
  10. //
  11. // This is a modified Marschner Lighting Model for hair based on the following papers:
  12. //
  13. // The original Marschner Siggraph paper defining the fundementsal hair lighting model.
  14. // http://www.graphics.stanford.edu/papers/hair/hair-sg03final.pdf
  15. //
  16. // An Energy-Conserving Hair Reflectance Model
  17. // http://www.eugenedeon.com/project/an-energy-conserving-hair-reflectance-model/
  18. // http://www.eugenedeon.com/wp-content/uploads/2014/04/egsrhair.pdf
  19. //
  20. // Physically Based Hair Shading in Unreal - specifically adapted in our shader
  21. // https://blog.selfshadow.com/publications/s2016-shading-course/karis/s2016_pbs_epic_hair.pptx
  22. //
  23. // Strand-based Hair Rendering in Frostbite for reference
  24. // https://advances.realtimerendering.com/s2019/hair_presentation_final.pdf
  25. //
  26. //
  27. // Path Notations
  28. // --------------
  29. // The Marschner model divides hair rendering into three light paths: R, TT and TRT
  30. // R - the light bounces straight off the hair fiber (Reflection)
  31. // TT - the light penetrates the hair fiber and exits at the other side (Transmitance - Transmitance)
  32. // TRT - the light penetrates the hair fiber, bounces inside and exists (Transmitance - Reflection - Transmitance)
  33. //
  34. // The calculations for each path are devided into longitude (M) and azimuth (N) terms:
  35. // Longtitude: M_R, M_TT, M_TRT
  36. // Azimuth: N_R, N_TT, N_TRT
  37. //
  38. // Other notations
  39. // ---------------
  40. // Wi - incoming light vector
  41. // Wr - reflected light vector
  42. // L - angles with respect to the Longitude
  43. // O - vectors with respect to the azimuth
  44. // Li and Lr are the longitudinal angles with respect to incoming/reflected light, i.e. the angle between
  45. // those vector and the normal plane (plane perpendicular to the hair)
  46. // Oi and Or are the azimuthal angles, i.e. the angles contained by the normal plane
  47. // Lh and Oh are the averages, i.e. Lh = (Lr + Li) / 2 and Oh = (Or + Oi) / 2
  48. // Ld is the difference angle, i.e. Ld = (Lr - Li) / 2
  49. // O denotes the relative azimuth, simply O = (Or - Oi)
  50. #include <Atom/RPI/Math.azsli>
  51. //------------------------------------------------------------------------------
  52. option bool o_enableMarschner_R = true;
  53. option bool o_enableMarschner_TRT = true;
  54. option bool o_enableMarschner_TT = true;
  55. option bool o_enableLongtitudeCoeff = true;
  56. option bool o_enableAzimuthCoeff = true;
  57. //------------------------------------------------------------------------------
  58. // Longitudinal functions (M_R, M_TT, M_RTR)
  59. // Notice that tilt and roughness multipliers are a-priori per Epic artistic taste
  60. float M_R(Surface surface, float Lh, float sinLiPlusSinLr)
  61. {
  62. float a = 1.0f * surface.cuticleTilt; // Tilt is translate as the mean offset
  63. float b = 0.5f * surface.roughnessA2; // Roughness is used as the standard deviation
  64. // return GaussianNormalized(sinLiPlusSinLr, a, b); // reference
  65. return GaussianNormalized(Lh, a, b);
  66. }
  67. float M_TT(Surface surface, float Lh, float sinLiPlusSinLr)
  68. {
  69. float a = 1.0f * surface.cuticleTilt;
  70. float b = 0.5f * surface.roughnessA2;
  71. // return GaussianNormalized(sinLiPlusSinLr, a, b); // reference
  72. return GaussianNormalized(Lh, a, b);
  73. }
  74. float M_TRT(Surface surface, float Lh, float sinLiPlusSinLr)
  75. {
  76. float a = 1.5f * surface.cuticleTilt;
  77. float b = 1.0f * surface.roughnessA2;
  78. // return GaussianNormalized(sinLiPlusSinLr, a, b); // reference
  79. return GaussianNormalized(Lh, a, b);
  80. }
  81. // Azimuth functions (N_R, N_TT, N_RTR)
  82. float N_R(Surface surface, float cos_O2, float3 Wi, float3 Wr, float f0)
  83. {
  84. // Fresnel part of the attentuation term (A in the papers)
  85. float fresnel = FresnelSchlick( sqrt(0.5 * dot(Wi, Wr) + 0.5) , f0);
  86. // Distribution term
  87. float distribution = 0.25 * cos_O2;
  88. // No absorption term since this is the reflected light path
  89. return fresnel * distribution;
  90. }
  91. // Light passes through the hair and exits at the back - most dominant effect
  92. // will be of lights at the back of the hair when the hair is thin and not concealed
  93. float3 N_TT(Surface surface, float n2, float cos_O, float cos_O2, float3 cos_Ld, float f0)
  94. {
  95. // Helper variables (see papers)
  96. float a = rcp(n2);
  97. float h = (1 + a * (0.6 - (0.8 * cos_O))) * cos_O2;
  98. // Fresnel part of the attentuation term (A in the papers)
  99. float fresnel = FresnelSchlick(cos_Ld * sqrt( 1 - (h*h) ), f0);
  100. fresnel = Pow2(1 - fresnel);
  101. // The absorption part of the attenuation term (A in the papers).
  102. float3 absorption = pow(surface.albedo, sqrt( 1 - (h*h*a*a) ) / (2 * cos_Ld) );
  103. // Distribution term
  104. float distribution = exp(-3.65 * cos_O - 3.98);
  105. return absorption * (fresnel * distribution);
  106. }
  107. float3 N_TRT(Surface surface, float cos_O, float3 cos_Ld, float f0)
  108. {
  109. // Helper variables (see papers)
  110. float h = sqrt(3.0f) * 0.5f;
  111. // Fresnel part of the attentuation term (A in the papers)
  112. float fresnel = FresnelSchlick(cos_Ld * sqrt( 1 - (h*h) ), f0);
  113. fresnel = Pow2(1 - fresnel) * fresnel;
  114. // How much light the hair will absorb. Part of the attenuation term (A in the papers)
  115. float3 absorption = pow(surface.albedo, 0.8f / max(cos_Ld, 0.001) );
  116. // Distribution term
  117. float distribution = exp(17.0f * cos_O - 16.78f);
  118. return absorption * (fresnel * distribution);
  119. }
  120. //------------------------------------------------------------------------------
  121. // The BSDF lighting function used by Hair
  122. //------------------------------------------------------------------------------
  123. float3 HairMarschnerBSDF(Surface surface, LightingData lightingData, const float3 dirToLight)
  124. {
  125. //-------------- Lighting Parameters Calculations ---------------
  126. // Incoming and outgoing light directions
  127. float3 Wi = normalize(dirToLight); // Incident light direction
  128. float3 Wr = normalize(lightingData.dirToCamera); // Reflected light measurement direction AKA reflection
  129. float3 T = normalize(surface.tangent); // Hair tangent
  130. // Incident light and reflection direction projected along the tangent
  131. float Ti = T * dot(T, Wi);
  132. float Tr = T * dot(T, Wr);
  133. // The light and reflection vectors projected along the normal plane to the hair.
  134. // The plane is spliting the Azimuth and Longtitude angles and vectors contributions.
  135. float3 NPi = normalize(Wi - Ti);
  136. float3 NPr = normalize(Wr - Tr);
  137. // Azimuthal angle between the incident light vector and the reflection
  138. // direction (the direction at which we measure the light scaterring)
  139. // float O = acos(dot(NPi, NPr)) <- Unused, for reference only
  140. float cos_O = dot(NPi, NPr);
  141. // cosine(O / 2)
  142. float cos_O2 = sqrt(0.5 * cos_O + 0.5); // <- trigonometric formula for calculating cos(x/2) given cos(x)
  143. // Longitude angles
  144. float Li = acos(clamp(dot(Wi, NPi),-1.0f, 1.0f));
  145. float Lr = acos(clamp(dot(Wr, NPr),-1.0f, 1.0f));
  146. float Lh = (Lr + Li) * 0.5f;
  147. float Ld = (Lr - Li) * 0.5f;
  148. // The folowing is according to the original article - reference only
  149. // float sinLiPlusSinLr = dot(Wi, NPi) * dot(Wr, NPr);// sin(Li) + sin(Lr);
  150. float sinLiPlusSinLr = sin(Li) + sin(Lr);
  151. // Refraction index
  152. const float n = 1.55f;
  153. float cos_Ld = cos(Ld);
  154. float n2 = (1.19f / cos_Ld) + 0.36f * cos_Ld;
  155. // Fresnel F0
  156. float f0 = Pow2( (1.0f - n) / (1.0f + n) );
  157. //--------------- Lighting accumulation per lobe ------------------
  158. float3 lighting = float3(0.0f, 0.0f, 0.0f);
  159. // R Path - single reflection from the hair towards the eye.
  160. if (o_enableMarschner_R)
  161. {
  162. float lighting_R = o_enableLongtitudeCoeff ? M_R(surface, Lh, sinLiPlusSinLr) : 1.0f;
  163. if (o_enableAzimuthCoeff)
  164. lighting_R *= N_R(surface, cos_O2, Wi, Wr, f0);
  165. // The following lines are a cheap method to get occluded reflection by accoounting
  166. // for the thickness if the reflection of light is going through the hair.
  167. // A reminder for this approximation - this is the R and not the TT lobe.
  168. float lightToEye = saturate(-dot(Wi, Wr));
  169. float selfOcclude = lightToEye * surface.thickness;
  170. float lightTransferRatio = 1.0f - selfOcclude;
  171. lightTransferRatio *= lightTransferRatio;
  172. lighting_R *= lightTransferRatio;
  173. lighting += float3(lighting_R, lighting_R, lighting_R);
  174. }
  175. // TT Path - ray passes through the hair.
  176. // The ray from the eye is refracted into the hair, then refracted again through the
  177. // back of the hair. The main contribution here would for thin hair areas from lights
  178. // behind the hair that are not concealed. For thicker hair this contribution should
  179. // be blocked by the head consealing the back light and by the thickness of the hair
  180. // that absorbs this energy over thick area hence reducing the energy pass based
  181. // on the average thickness.
  182. if (o_enableMarschner_TT)
  183. {
  184. float3 lighting_TT = o_enableLongtitudeCoeff ? M_TT(surface, Lh, sinLiPlusSinLr) : float3(1.0f, 1.0f, 1.0f);
  185. if (o_enableAzimuthCoeff)
  186. lighting_TT *= N_TT(surface, n2, cos_O, cos_O2, cos_Ld, f0);
  187. // Reduce back transmittance based on the thickness of the hair
  188. lighting_TT *= (1.0f - surface.thickness);
  189. lighting += lighting_TT;
  190. }
  191. // TRT Path - ray refracted into the hair, reflected back inside and exits (refracted)
  192. // the hair towards the eye.
  193. if (o_enableMarschner_TRT)
  194. {
  195. float3 lighting_TRT = o_enableLongtitudeCoeff ? M_TRT(surface, Lh, sinLiPlusSinLr) : float3(1.0f, 1.0f, 1.0f);
  196. if (o_enableAzimuthCoeff)
  197. lighting_TRT *= N_TRT(surface, cos_O, cos_Ld, f0);
  198. lighting += lighting_TRT;
  199. }
  200. return lighting;
  201. }