HairLighting.azsli 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285
  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. //------------------------------------------------------------------------------
  9. // Shader code related to lighting and shadowing for TressFX
  10. //------------------------------------------------------------------------------
  11. //
  12. // Copyright (c) 2019 Advanced Micro Devices, Inc. All rights reserved.
  13. //
  14. // Permission is hereby granted, free of charge, to any person obtaining a copy
  15. // of this software and associated documentation files (the "Software"), to deal
  16. // in the Software without restriction, including without limitation the rights
  17. // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  18. // copies of the Software, and to permit persons to whom the Software is
  19. // furnished to do so, subject to the following conditions:
  20. //
  21. // The above copyright notice and this permission notice shall be included in
  22. // all copies or substantial portions of the Software.
  23. //
  24. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  25. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  26. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  27. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  28. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  29. // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  30. // THE SOFTWARE.
  31. //
  32. #pragma once
  33. #include <HairUtilities.azsli>
  34. //------------------------------------------------------------------------------
  35. struct HairShadeParams
  36. {
  37. float3 m_color;
  38. float m_hairShadowAlpha;
  39. float m_fiberRadius;
  40. float m_fiberSpacing;
  41. // Original TressFX Kajiya lighting model parameters
  42. float m_Ka;
  43. float m_Kd;
  44. float m_Ks1;
  45. float m_Ex1;
  46. float m_Ks2;
  47. float m_Ex2;
  48. // Marschner lighting model parameters
  49. float m_cuticleTilt;
  50. float m_roughness;
  51. };
  52. //! Original TressFX enhanced Kajiya-Kay lighting model code
  53. //!
  54. //! Returns a float3 which is the scale for diffuse, spec term, and colored spec term.
  55. //!
  56. //! The diffuse term is from Kajiya.
  57. //!
  58. //! The spec term is what Marschner refers to as "R", reflecting directly off the surface
  59. //! of the hair, taking the color of the light like a dielectric specular term. This
  60. //! highlight is shifted towards the root of the hair.
  61. //!
  62. //! The colored spec term is caused by light passing through the hair, bouncing off the
  63. //! back, and coming back out. It therefore picks up the color of the light.
  64. //! Marschner refers to this term as the "TRT" term. This highlight is shifted towards the
  65. //! tip of the hair.
  66. //!
  67. //! vEyeDir, vLightDir and vTangentDir are all pointing out.
  68. //! coneAngleRadians explained below.
  69. //!
  70. //! hair has a tiled-conical shape along its lenght. Sort of like the following.
  71. //!
  72. //! \ /
  73. //! \ /
  74. //! \ /
  75. //! \ /
  76. //!
  77. //! The angle of the cone is the last argument, in radians.
  78. //! It's typically in the range of 5 to 10 degrees
  79. float3 ComputeDiffuseSpecFactors(
  80. float3 vEyeDir, float3 vLightDir, float3 vTangentDir, HairShadeParams params,
  81. float coneAngleRadians = 10 * AMD_PI / 180)
  82. {
  83. // In Kajiya's model: diffuse component: sin(t, l)
  84. float cosTL = (dot(vTangentDir, vLightDir));
  85. float sinTL = sqrt(1 - cosTL * cosTL);
  86. float diffuse = sinTL;
  87. float cosTRL = -cosTL;
  88. float sinTRL = sinTL;
  89. float cosTE = (dot(vTangentDir, vEyeDir));
  90. float sinTE = sqrt(1 - cosTE * cosTE);
  91. // Primary highlight: reflected direction shift towards root (2 * coneAngleRadians)
  92. float cosTRL_root = cosTRL * cos(2 * coneAngleRadians) - sinTRL * sin(2 * coneAngleRadians);
  93. float sinTRL_root = sqrt(1 - cosTRL_root * cosTRL_root);
  94. float specular_root = max(0, cosTRL_root * cosTE + sinTRL_root * sinTE);
  95. // Secondary highlight: reflected direction shifted toward tip (3*coneAngleRadians)
  96. float cosTRL_tip = cosTRL * cos(-3 * coneAngleRadians) - sinTRL * sin(-3 * coneAngleRadians);
  97. float sinTRL_tip = sqrt(1 - cosTRL_tip * cosTRL_tip);
  98. float specular_tip = max(0, cosTRL_tip * cosTE + sinTRL_tip * sinTE);
  99. return float3(
  100. params.m_Kd * diffuse,
  101. params.m_Ks1 * pow(specular_root, params.m_Ex1),
  102. params.m_Ks2 * pow(specular_tip, params.m_Ex2));
  103. }
  104. float LinearizeDepth(float depthNDC, float fNear, float fFar)
  105. {
  106. return fNear * fFar / (fFar - depthNDC * (fFar - fNear));
  107. }
  108. //! The following code is for reference only and should be removed once the
  109. //! Kajiya-Kay original TressFX lighting model is connected to Atom as was
  110. //! done for the Marshcner lighting model.
  111. #define DEMO_NUMBER_OF_LIGHTS 3
  112. #define DEMO_NUMBER_OF_LIGHTS 3
  113. float3 SimplifiedHairLighting(float3 vTangent, float3 vPositionWS, float3 vViewDirWS, in HairShadeParams params, float3 vNDC)
  114. {
  115. // Initialize information needed for all lights
  116. float3 V = normalize(vViewDirWS);
  117. float3 T = normalize(vTangent);
  118. float3 accumulatedHairColor = float3(0.0, 0.0, 0.0);
  119. float4 lightPosWSVec[DEMO_NUMBER_OF_LIGHTS] = {
  120. float4(3, 0, 3, 1.5f), // Sun
  121. float4(-.5, 0, 0.5, .5f),
  122. float4(.5, 0, 0.5, .5f),
  123. };
  124. float3 lightColorVec[DEMO_NUMBER_OF_LIGHTS] = {
  125. float3(1,1,.95f), // Sun
  126. float3(1,1,1),
  127. float3(1,1,1)
  128. };
  129. // Static lights loop for reference - not connected to Atom
  130. // [To Do] - connect to Atom lighting ala HairLightTypes loop
  131. for (int l = 0; l < DEMO_NUMBER_OF_LIGHTS ; l++)
  132. {
  133. float3 lightPosWS = lightPosWSVec[l].xyz;
  134. float3 LightVector = normalize( vPositionWS - lightPosWS );
  135. float lightIntensity = lightPosWSVec[l].w;
  136. float3 LightColor = lightColorVec[l];
  137. float3 L = LightVector;
  138. // Reference usage of shadow
  139. // float shadowTerm = ComputeLightShadow(l, vPositionWS, params);
  140. // if (shadowTerm <= 0.f)
  141. // continue;
  142. float3 lightSurfaceCoeffs = ComputeDiffuseSpecFactors(V, L, T, params);
  143. // The diffuse coefficient here is a rough approximation as per the Kajiya model
  144. float3 diffuseCompoenent = lightSurfaceCoeffs.x * params.m_color;
  145. // This is the approximation to Marschner R but azimuthal only
  146. float3 specularAtPos = lightSurfaceCoeffs.y;
  147. // This is the approximation to Marschner TRT but azimuthal only
  148. // Notice the base color gather due to the trsmittance within the hair
  149. float3 specularAtBase = lightSurfaceCoeffs.z * params.m_color;
  150. // Final result
  151. float3 lightContribution = (diffuseCompoenent + specularAtPos + specularAtBase) * lightIntensity * LightColor; // * shadowTerm;
  152. accumulatedHairColor += max(float3(0, 0, 0), lightContribution );
  153. }
  154. return accumulatedHairColor;
  155. }
  156. //==============================================================================
  157. // Atom Lighting
  158. //==============================================================================
  159. #include <Atom/Features/Shadow/DirectionalLightShadow.azsli>
  160. #include <HairLightTypes.azsli>
  161. //------------------------------------------------------------------------------
  162. float3 CalculateLighting(
  163. float4 screenCoords, // XY - screen coords 0..max pix res, Z - depth 0..1
  164. float3 vPositionWS, float3 vViewDirWS, float3 vTangent,
  165. float thickness, in HairShadeParams material )
  166. {
  167. //-------- Surface init --------
  168. Surface surface;
  169. const float specularF0Factor = 0.04f; // set this to 0.04?!
  170. surface.position = vPositionWS;
  171. surface.tangent = vTangent; // Redundant - will be calculated per light
  172. surface.normal = float3(0, 0, 0); // Will fail lights that did not initialize properly.
  173. surface.vertexNormal = float3(0,0,0); // [To Do] - vertex normals are not handled yet in the hair shader.
  174. surface.roughnessLinear = material.m_roughness;
  175. surface.cuticleTilt = material.m_cuticleTilt;
  176. surface.thickness = thickness;
  177. surface.CalculateRoughnessA();
  178. surface.SetAlbedoAndSpecularF0( material.m_color, specularF0Factor);
  179. // The trasmission / back lighting does not seem to work!
  180. surface.transmission.InitializeToZero(); // Assuming thin layer
  181. surface.transmission.tint = material.m_color;
  182. surface.transmission.thickness = 0.001; // 1 mm settings
  183. surface.transmission.transmissionParams = float4(1.0, 1.0, 1.0, 32.0); // for thin surface XYZ are partials * W that is the exponent mult
  184. //------- LightingData init -------
  185. LightingData lightingData;
  186. float4 screenPositionForLighting = mul(ViewSrg::m_viewProjectionMatrix, float4(vPositionWS, 1.0));
  187. uint2 dimensions;
  188. PassSrg::m_linearDepth.GetDimensions(dimensions.x, dimensions.y);
  189. screenPositionForLighting.y = 1.0 - screenPositionForLighting.y;
  190. screenPositionForLighting.xy = (screenPositionForLighting.xy * 0.5 + 0.5) * dimensions;
  191. // Light iterator - required for the init but the culling is not used yet.
  192. lightingData.tileIterator.Init(screenCoords, PassSrg::m_lightListRemapped, PassSrg::m_tileLightData);
  193. // The normal assignment will be overriden afterwards per light
  194. lightingData.Init(surface.position, surface.GetSpecularNormal(), surface.roughnessLinear, ViewSrg::m_worldPosition.xyz);
  195. ApplyLighting(surface, lightingData, screenCoords);
  196. return lightingData.diffuseLighting + lightingData.specularLighting;
  197. }
  198. float3 TressFXShading(float2 pixelCoord, float depth, float3 tangent, float3 baseColor, float thickness, int shaderParamIndex)
  199. {
  200. float3 vNDC; // normalized device / screen coordinates: [-1..1, -1..1, 0..1]
  201. float3 vPositionWS = ScreenPosToWorldPos(PassSrg::m_linearDepth, pixelCoord, depth, vNDC);
  202. // [To Do] the follwing two lines are a hack to make the tile lighting work for now
  203. #define _BIG_HACK_FOR_TESTING_ 1//32
  204. float4 screenCoords = float4( _BIG_HACK_FOR_TESTING_ * pixelCoord, depth, depth); // screen space position - XY in pixels - ZW are depth 0..1
  205. float3 vViewDirWS = g_vEye - vPositionWS;
  206. //---- TressFX original lighting params setting ----
  207. HairShadeParams params;
  208. params.m_color = baseColor;
  209. params.m_hairShadowAlpha = HairParams[shaderParamIndex].m_shadowAlpha;
  210. params.m_fiberRadius = HairParams[shaderParamIndex].m_fiberRadius;
  211. params.m_fiberSpacing = HairParams[shaderParamIndex].m_fiberSpacing;
  212. params.m_Ka = HairParams[shaderParamIndex].m_matKValue.x;
  213. params.m_Kd = HairParams[shaderParamIndex].m_matKValue.y;
  214. params.m_Ks1 = HairParams[shaderParamIndex].m_matKValue.z;
  215. params.m_Ex1 = HairParams[shaderParamIndex].m_matKValue.w;
  216. params.m_Ks2 = HairParams[shaderParamIndex].m_hairKs2;
  217. params.m_Ex2 = HairParams[shaderParamIndex].m_hairEx2;
  218. params.m_cuticleTilt = HairParams[shaderParamIndex].m_cuticleTilt;
  219. params.m_roughness = HairParams[shaderParamIndex].m_roughness;
  220. //---------------------------------------------------
  221. float3 accumulatedLight = float3(0, 0, 1);
  222. if (o_hairLightingModel == HairLightingModel::Kajiya)
  223. { // This option should be removed and the Kajiya-Kay model should be operated from within
  224. // the Atom lighting loop.
  225. accumulatedLight = SimplifiedHairLighting(tangent, vPositionWS, vViewDirWS, params, vNDC);
  226. }
  227. else
  228. {
  229. accumulatedLight = CalculateLighting(screenCoords, vPositionWS, vViewDirWS, tangent, thickness, params);
  230. }
  231. return accumulatedLight;
  232. }
  233. float3 TressFXShadingFullScreen(float2 pixelCoord, float depth, float3 compressedTangent, float3 baseColor, float thickness, int shaderParamIndex)
  234. {
  235. // The tangent that was compressed to store in the PPLL structure
  236. float3 tangent = normalize(compressedTangent.xyz * 2.f - 1.f);
  237. return TressFXShading(pixelCoord, depth, tangent, baseColor, thickness, shaderParamIndex);
  238. }