CommonLighting.hlsl 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461
  1. #ifndef UNITY_COMMON_LIGHTING_INCLUDED
  2. #define UNITY_COMMON_LIGHTING_INCLUDED
  3. // These clamping function to max of floating point 16 bit are use to prevent INF in code in case of extreme value
  4. TEMPLATE_1_REAL(ClampToFloat16Max, value, return min(value, HALF_MAX))
  5. // Ligthing convention
  6. // Light direction is oriented backward (-Z). i.e in shader code, light direction is -lightData.forward
  7. //-----------------------------------------------------------------------------
  8. // Helper functions
  9. //-----------------------------------------------------------------------------
  10. // Performs the mapping of the vector 'v' centered within the axis-aligned cube
  11. // of dimensions [-1, 1]^3 to a vector centered within the unit sphere.
  12. // The function expects 'v' to be within the cube (possibly unexpected results otherwise).
  13. // Ref: http://mathproofs.blogspot.com/2005/07/mapping-cube-to-sphere.html
  14. real3 MapCubeToSphere(real3 v)
  15. {
  16. real3 v2 = v * v;
  17. real2 vr3 = v2.xy * rcp(3.0);
  18. return v * sqrt((real3)1.0 - 0.5 * v2.yzx - 0.5 * v2.zxy + vr3.yxx * v2.zzy);
  19. }
  20. // Computes the squared magnitude of the vector computed by MapCubeToSphere().
  21. real ComputeCubeToSphereMapSqMagnitude(real3 v)
  22. {
  23. real3 v2 = v * v;
  24. // Note: dot(v, v) is often computed before this function is called,
  25. // so the compiler should optimize and use the precomputed result here.
  26. return dot(v, v) - v2.x * v2.y - v2.y * v2.z - v2.z * v2.x + v2.x * v2.y * v2.z;
  27. }
  28. // texelArea = 4.0 / (resolution * resolution).
  29. // Ref: http://bpeers.com/blog/?itemid=1017
  30. // This version is less accurate, but much faster than this one:
  31. // http://www.rorydriscoll.com/2012/01/15/cubemap-texel-solid-angle/
  32. real ComputeCubemapTexelSolidAngle(real3 L, real texelArea)
  33. {
  34. // Stretch 'L' by (1/d) so that it points at a side of a [-1, 1]^2 cube.
  35. real d = Max3(abs(L.x), abs(L.y), abs(L.z));
  36. // Since 'L' is a unit vector, we can directly compute its
  37. // new (inverse) length without dividing 'L' by 'd' first.
  38. real invDist = d;
  39. // dw = dA * cosTheta / (dist * dist), cosTheta = 1.0 / dist,
  40. // where 'dA' is the area of the cube map texel.
  41. return texelArea * invDist * invDist * invDist;
  42. }
  43. // Only makes sense for Monte-Carlo integration.
  44. // Normalize by dividing by the total weight (or the number of samples) in the end.
  45. // Integrate[6*(u^2+v^2+1)^(-3/2), {u,-1,1},{v,-1,1}] = 4 * Pi
  46. // Ref: "Stupid Spherical Harmonics Tricks", p. 9.
  47. real ComputeCubemapTexelSolidAngle(real2 uv)
  48. {
  49. real u = uv.x, v = uv.y;
  50. return pow(1 + u * u + v * v, -1.5);
  51. }
  52. real ConvertEvToLuminance(real ev)
  53. {
  54. return exp2(ev - 3.0);
  55. }
  56. real ConvertLuminanceToEv(real luminance)
  57. {
  58. real k = 12.5f;
  59. return log2((luminance * 100.0) / k);
  60. }
  61. //-----------------------------------------------------------------------------
  62. // Attenuation functions
  63. //-----------------------------------------------------------------------------
  64. // Ref: Moving Frostbite to PBR.
  65. // Non physically based hack to limit light influence to attenuationRadius.
  66. // Square the result to smoothen the function.
  67. real DistanceWindowing(real distSquare, real rangeAttenuationScale, real rangeAttenuationBias)
  68. {
  69. // If (range attenuation is enabled)
  70. // rangeAttenuationScale = 1 / r^2
  71. // rangeAttenuationBias = 1
  72. // Else
  73. // rangeAttenuationScale = 2^12 / r^2
  74. // rangeAttenuationBias = 2^24
  75. return saturate(rangeAttenuationBias - Sq(distSquare * rangeAttenuationScale));
  76. }
  77. real SmoothDistanceWindowing(real distSquare, real rangeAttenuationScale, real rangeAttenuationBias)
  78. {
  79. real factor = DistanceWindowing(distSquare, rangeAttenuationScale, rangeAttenuationBias);
  80. return Sq(factor);
  81. }
  82. #define PUNCTUAL_LIGHT_THRESHOLD 0.01 // 1cm (in Unity 1 is 1m)
  83. // Return physically based quadratic attenuation + influence limit to reach 0 at attenuationRadius
  84. real SmoothWindowedDistanceAttenuation(real distSquare, real distRcp, real rangeAttenuationScale, real rangeAttenuationBias)
  85. {
  86. real attenuation = min(distRcp, 1.0 / PUNCTUAL_LIGHT_THRESHOLD);
  87. attenuation *= DistanceWindowing(distSquare, rangeAttenuationScale, rangeAttenuationBias);
  88. // Effectively results in (distRcp)^2 * SmoothDistanceWindowing(...).
  89. return Sq(attenuation);
  90. }
  91. // Square the result to smoothen the function.
  92. real AngleAttenuation(real cosFwd, real lightAngleScale, real lightAngleOffset)
  93. {
  94. return saturate(cosFwd * lightAngleScale + lightAngleOffset);
  95. }
  96. real SmoothAngleAttenuation(real cosFwd, real lightAngleScale, real lightAngleOffset)
  97. {
  98. real attenuation = AngleAttenuation(cosFwd, lightAngleScale, lightAngleOffset);
  99. return Sq(attenuation);
  100. }
  101. // Combines SmoothWindowedDistanceAttenuation() and SmoothAngleAttenuation() in an efficient manner.
  102. // distances = {d, d^2, 1/d, d_proj}, where d_proj = dot(lightToSample, lightData.forward).
  103. real PunctualLightAttenuation(real4 distances, real rangeAttenuationScale, real rangeAttenuationBias,
  104. real lightAngleScale, real lightAngleOffset)
  105. {
  106. real distSq = distances.y;
  107. real distRcp = distances.z;
  108. real distProj = distances.w;
  109. real cosFwd = distProj * distRcp;
  110. real attenuation = min(distRcp, 1.0 / PUNCTUAL_LIGHT_THRESHOLD);
  111. attenuation *= DistanceWindowing(distSq, rangeAttenuationScale, rangeAttenuationBias);
  112. attenuation *= AngleAttenuation(cosFwd, lightAngleScale, lightAngleOffset);
  113. // Effectively results in SmoothWindowedDistanceAttenuation(...) * SmoothAngleAttenuation(...).
  114. return Sq(attenuation);
  115. }
  116. // Applies SmoothDistanceWindowing() after transforming the attenuation ellipsoid into a sphere.
  117. // If r = rsqrt(invSqRadius), then the ellipsoid is defined s.t. r1 = r / invAspectRatio, r2 = r3 = r.
  118. // The transformation is performed along the major axis of the ellipsoid (corresponding to 'r1').
  119. // Both the ellipsoid (e.i. 'axis') and 'unL' should be in the same coordinate system.
  120. // 'unL' should be computed from the center of the ellipsoid.
  121. real EllipsoidalDistanceAttenuation(real3 unL, real3 axis, real invAspectRatio,
  122. real rangeAttenuationScale, real rangeAttenuationBias)
  123. {
  124. // Project the unnormalized light vector onto the axis.
  125. real projL = dot(unL, axis);
  126. // Transform the light vector so that we can work with
  127. // with the ellipsoid as if it was a sphere with the radius of light's range.
  128. real diff = projL - projL * invAspectRatio;
  129. unL -= diff * axis;
  130. real sqDist = dot(unL, unL);
  131. return SmoothDistanceWindowing(sqDist, rangeAttenuationScale, rangeAttenuationBias);
  132. }
  133. // Applies SmoothDistanceWindowing() using the axis-aligned ellipsoid of the given dimensions.
  134. // Both the ellipsoid and 'unL' should be in the same coordinate system.
  135. // 'unL' should be computed from the center of the ellipsoid.
  136. real EllipsoidalDistanceAttenuation(real3 unL, real3 invHalfDim,
  137. real rangeAttenuationScale, real rangeAttenuationBias)
  138. {
  139. // Transform the light vector so that we can work with
  140. // with the ellipsoid as if it was a unit sphere.
  141. unL *= invHalfDim;
  142. real sqDist = dot(unL, unL);
  143. return SmoothDistanceWindowing(sqDist, rangeAttenuationScale, rangeAttenuationBias);
  144. }
  145. // Applies SmoothDistanceWindowing() after mapping the axis-aligned box to a sphere.
  146. // If the diagonal of the box is 'd', invHalfDim = rcp(0.5 * d).
  147. // Both the box and 'unL' should be in the same coordinate system.
  148. // 'unL' should be computed from the center of the box.
  149. real BoxDistanceAttenuation(real3 unL, real3 invHalfDim,
  150. real rangeAttenuationScale, real rangeAttenuationBias)
  151. {
  152. real attenuation = 0.0;
  153. // Transform the light vector so that we can work with
  154. // with the box as if it was a [-1, 1]^2 cube.
  155. unL *= invHalfDim;
  156. // Our algorithm expects the input vector to be within the cube.
  157. if (!(Max3(abs(unL.x), abs(unL.y), abs(unL.z)) > 1.0))
  158. {
  159. real sqDist = ComputeCubeToSphereMapSqMagnitude(unL);
  160. attenuation = SmoothDistanceWindowing(sqDist, rangeAttenuationScale, rangeAttenuationBias);
  161. }
  162. return attenuation;
  163. }
  164. //-----------------------------------------------------------------------------
  165. // IES Helper
  166. //-----------------------------------------------------------------------------
  167. real2 GetIESTextureCoordinate(real3x3 lightToWord, real3 L)
  168. {
  169. // IES need to be sample in light space
  170. real3 dir = mul(lightToWord, -L); // Using matrix on left side do a transpose
  171. // convert to spherical coordinate
  172. real2 sphericalCoord; // .x is theta, .y is phi
  173. // Texture is encoded with cos(phi), scale from -1..1 to 0..1
  174. sphericalCoord.y = (dir.z * 0.5) + 0.5;
  175. real theta = atan2(dir.y, dir.x);
  176. sphericalCoord.x = theta * INV_TWO_PI;
  177. return sphericalCoord;
  178. }
  179. //-----------------------------------------------------------------------------
  180. // Lighting functions
  181. //-----------------------------------------------------------------------------
  182. // Ref: Horizon Occlusion for Normal Mapped Reflections: http://marmosetco.tumblr.com/post/81245981087
  183. real GetHorizonOcclusion(real3 V, real3 normalWS, real3 vertexNormal, real horizonFade)
  184. {
  185. real3 R = reflect(-V, normalWS);
  186. real specularOcclusion = saturate(1.0 + horizonFade * dot(R, vertexNormal));
  187. // smooth it
  188. return specularOcclusion * specularOcclusion;
  189. }
  190. // Ref: Moving Frostbite to PBR - Gotanda siggraph 2011
  191. // Return specular occlusion based on ambient occlusion (usually get from SSAO) and view/roughness info
  192. real GetSpecularOcclusionFromAmbientOcclusion(real NdotV, real ambientOcclusion, real roughness)
  193. {
  194. return saturate(PositivePow(NdotV + ambientOcclusion, exp2(-16.0 * roughness - 1.0)) - 1.0 + ambientOcclusion);
  195. }
  196. // ref: Practical Realtime Strategies for Accurate Indirect Occlusion
  197. // Update ambient occlusion to colored ambient occlusion based on statitics of how light is bouncing in an object and with the albedo of the object
  198. real3 GTAOMultiBounce(real visibility, real3 albedo)
  199. {
  200. real3 a = 2.0404 * albedo - 0.3324;
  201. real3 b = -4.7951 * albedo + 0.6417;
  202. real3 c = 2.7552 * albedo + 0.6903;
  203. real x = visibility;
  204. return max(x, ((x * a + b) * x + c) * x);
  205. }
  206. // Based on Oat and Sander's 2008 technique
  207. // Area/solidAngle of intersection of two cone
  208. real SphericalCapIntersectionSolidArea(real cosC1, real cosC2, real cosB)
  209. {
  210. real r1 = FastACos(cosC1);
  211. real r2 = FastACos(cosC2);
  212. real rd = FastACos(cosB);
  213. real area = 0.0;
  214. if (rd <= max(r1, r2) - min(r1, r2))
  215. {
  216. // One cap is completely inside the other
  217. area = TWO_PI - TWO_PI * max(cosC1, cosC2);
  218. }
  219. else if (rd >= r1 + r2)
  220. {
  221. // No intersection exists
  222. area = 0.0;
  223. }
  224. else
  225. {
  226. real diff = abs(r1 - r2);
  227. real den = r1 + r2 - diff;
  228. real x = 1.0 - saturate((rd - diff) / max(den, 0.0001));
  229. area = smoothstep(0.0, 1.0, x);
  230. area *= TWO_PI - TWO_PI * max(cosC1, cosC2);
  231. }
  232. return area;
  233. }
  234. // ref: Practical Realtime Strategies for Accurate Indirect Occlusion
  235. // http://blog.selfshadow.com/publications/s2016-shading-course/#course_content
  236. // Original Cone-Cone method with cosine weighted assumption (p129 s2016_pbs_activision_occlusion)
  237. real GetSpecularOcclusionFromBentAO(real3 V, real3 bentNormalWS, real3 normalWS, real ambientOcclusion, real roughness)
  238. {
  239. // Retrieve cone angle
  240. // Ambient occlusion is cosine weighted, thus use following equation. See slide 129
  241. real cosAv = sqrt(1.0 - ambientOcclusion);
  242. roughness = max(roughness, 0.01); // Clamp to 0.01 to avoid edge cases
  243. real cosAs = exp2((-log(10.0) / log(2.0)) * Sq(roughness));
  244. real cosB = dot(bentNormalWS, reflect(-V, normalWS));
  245. return SphericalCapIntersectionSolidArea(cosAv, cosAs, cosB) / (TWO_PI * (1.0 - cosAs));
  246. }
  247. // Ref: Steve McAuley - Energy-Conserving Wrapped Diffuse
  248. real ComputeWrappedDiffuseLighting(real NdotL, real w)
  249. {
  250. return saturate((NdotL + w) / ((1.0 + w) * (1.0 + w)));
  251. }
  252. // Jimenez variant for eye
  253. real ComputeWrappedPowerDiffuseLighting(real NdotL, real w, real p)
  254. {
  255. return pow(saturate((NdotL + w) / (1.0 + w)), p) * (p + 1) / (w * 2.0 + 2.0);
  256. }
  257. // Ref: The Technical Art of Uncharted 4 - Brinck and Maximov 2016
  258. real ComputeMicroShadowing(real AO, real NdotL, real opacity)
  259. {
  260. real aperture = 2.0 * AO * AO;
  261. real microshadow = saturate(NdotL + aperture - 1.0);
  262. return lerp(1.0, microshadow, opacity);
  263. }
  264. real3 ComputeShadowColor(real shadow, real3 shadowTint, real penumbraFlag)
  265. {
  266. // The origin expression is
  267. // lerp(real3(1.0, 1.0, 1.0) - ((1.0 - shadow) * (real3(1.0, 1.0, 1.0) - shadowTint))
  268. // , shadow * lerp(shadowTint, lerp(shadowTint, real3(1.0, 1.0, 1.0), shadow), shadow)
  269. // , penumbraFlag);
  270. // it has been simplified to this
  271. real3 invTint = real3(1.0, 1.0, 1.0) - shadowTint;
  272. real shadow3 = shadow * shadow * shadow;
  273. return lerp(real3(1.0, 1.0, 1.0) - ((1.0 - shadow) * invTint)
  274. , shadow3 * invTint + shadow * shadowTint,
  275. penumbraFlag);
  276. }
  277. // This is the same method as the one above. Simply the shadow is a real3 to support colored shadows.
  278. real3 ComputeShadowColor(real3 shadow, real3 shadowTint, real penumbraFlag)
  279. {
  280. // The origin expression is
  281. // lerp(real3(1.0, 1.0, 1.0) - ((1.0 - shadow) * (real3(1.0, 1.0, 1.0) - shadowTint))
  282. // , shadow * lerp(shadowTint, lerp(shadowTint, real3(1.0, 1.0, 1.0), shadow), shadow)
  283. // , penumbraFlag);
  284. // it has been simplified to this
  285. real3 invTint = real3(1.0, 1.0, 1.0) - shadowTint;
  286. real3 shadow3 = shadow * shadow * shadow;
  287. return lerp(real3(1.0, 1.0, 1.0) - ((1.0 - shadow) * invTint)
  288. , shadow3 * invTint + shadow * shadowTint,
  289. penumbraFlag);
  290. }
  291. //-----------------------------------------------------------------------------
  292. // Helper functions
  293. //--------------------------------------------------------------------------- --
  294. // Ref: "Crafting a Next-Gen Material Pipeline for The Order: 1886".
  295. real ClampNdotV(real NdotV)
  296. {
  297. return max(NdotV, 0.0001); // Approximately 0.0057 degree bias
  298. }
  299. // Helper function to return a set of common angle used when evaluating BSDF
  300. // NdotL and NdotV are unclamped
  301. void GetBSDFAngle(real3 V, real3 L, real NdotL, real NdotV,
  302. out real LdotV, out real NdotH, out real LdotH, out real invLenLV)
  303. {
  304. // Optimized math. Ref: PBR Diffuse Lighting for GGX + Smith Microsurfaces (slide 114).
  305. LdotV = dot(L, V);
  306. invLenLV = rsqrt(max(2.0 * LdotV + 2.0, FLT_EPS)); // invLenLV = rcp(length(L + V)), clamp to avoid rsqrt(0) = inf, inf * 0 = NaN
  307. NdotH = saturate((NdotL + NdotV) * invLenLV);
  308. LdotH = saturate(invLenLV * LdotV + invLenLV);
  309. }
  310. // Inputs: normalized normal and view vectors.
  311. // Outputs: front-facing normal, and the new non-negative value of the cosine of the view angle.
  312. // Important: call Orthonormalize() on the tangent and recompute the bitangent afterwards.
  313. real3 GetViewReflectedNormal(real3 N, real3 V, out real NdotV)
  314. {
  315. // Fragments of front-facing geometry can have back-facing normals due to interpolation,
  316. // normal mapping and decals. This can cause visible artifacts from both direct (negative or
  317. // extremely high values) and indirect (incorrect lookup direction) lighting.
  318. // There are several ways to avoid this problem. To list a few:
  319. //
  320. // 1. Setting { NdotV = max(<N,V>, SMALL_VALUE) }. This effectively removes normal mapping
  321. // from the affected fragments, making the surface appear flat.
  322. //
  323. // 2. Setting { NdotV = abs(<N,V>) }. This effectively reverses the convexity of the surface.
  324. // It also reduces light leaking from non-shadow-casting lights. Note that 'NdotV' can still
  325. // be 0 in this case.
  326. //
  327. // It's important to understand that simply changing the value of the cosine is insufficient.
  328. // For one, it does not solve the incorrect lookup direction problem, since the normal itself
  329. // is not modified. There is a more insidious issue, however. 'NdotV' is a constituent element
  330. // of the mathematical system describing the relationships between different vectors - and
  331. // not just normal and view vectors, but also light vectors, half vectors, tangent vectors, etc.
  332. // Changing only one angle (or its cosine) leaves the system in an inconsistent state, where
  333. // certain relationships can take on different values depending on whether 'NdotV' is used
  334. // in the calculation or not. Therefore, it is important to change the normal (or another
  335. // vector) in order to leave the system in a consistent state.
  336. //
  337. // We choose to follow the conceptual approach (2) by reflecting the normal around the
  338. // (<N,V> = 0) boundary if necessary, as it allows us to preserve some normal mapping details.
  339. NdotV = dot(N, V);
  340. // N = (NdotV >= 0.0) ? N : (N - 2.0 * NdotV * V);
  341. N += (2.0 * saturate(-NdotV)) * V;
  342. NdotV = abs(NdotV);
  343. return N;
  344. }
  345. // Generates an orthonormal (row-major) basis from a unit vector. TODO: make it column-major.
  346. // The resulting rotation matrix has the determinant of +1.
  347. // Ref: 'ortho_basis_pixar_r2' from http://marc-b-reynolds.github.io/quaternions/2016/07/06/Orthonormal.html
  348. real3x3 GetLocalFrame(real3 localZ)
  349. {
  350. real x = localZ.x;
  351. real y = localZ.y;
  352. real z = localZ.z;
  353. real sz = FastSign(z);
  354. real a = 1 / (sz + z);
  355. real ya = y * a;
  356. real b = x * ya;
  357. real c = x * sz;
  358. real3 localX = real3(c * x * a - 1, sz * b, c);
  359. real3 localY = real3(b, y * ya - sz, y);
  360. // Note: due to the quaternion formulation, the generated frame is rotated by 180 degrees,
  361. // s.t. if localZ = {0, 0, 1}, then localX = {-1, 0, 0} and localY = {0, -1, 0}.
  362. return real3x3(localX, localY, localZ);
  363. }
  364. // Generates an orthonormal (row-major) basis from a unit vector. TODO: make it column-major.
  365. // The resulting rotation matrix has the determinant of +1.
  366. real3x3 GetLocalFrame(real3 localZ, real3 localX)
  367. {
  368. real3 localY = cross(localZ, localX);
  369. return real3x3(localX, localY, localZ);
  370. }
  371. // Construct a right-handed view-dependent orthogonal basis around the normal:
  372. // b0-b2 is the view-normal aka reflection plane.
  373. real3x3 GetOrthoBasisViewNormal(real3 V, real3 N, real unclampedNdotV, bool testSingularity = false)
  374. {
  375. real3x3 orthoBasisViewNormal;
  376. if (testSingularity && (abs(1.0 - unclampedNdotV) <= FLT_EPS))
  377. {
  378. // In this case N == V, and azimuth orientation around N shouldn't matter for the caller,
  379. // we can use any quaternion-based method, like Frisvad or Reynold's (Pixar):
  380. orthoBasisViewNormal = GetLocalFrame(N);
  381. }
  382. else
  383. {
  384. orthoBasisViewNormal[0] = normalize(V - N * unclampedNdotV);
  385. orthoBasisViewNormal[2] = N;
  386. orthoBasisViewNormal[1] = cross(orthoBasisViewNormal[2], orthoBasisViewNormal[0]);
  387. }
  388. return orthoBasisViewNormal;
  389. }
  390. // Move this here since it's used by both LightLoop.hlsl and RaytracingLightLoop.hlsl
  391. bool IsMatchingLightLayer(uint lightLayers, uint renderingLayers)
  392. {
  393. return (lightLayers & renderingLayers) != 0;
  394. }
  395. #endif // UNITY_COMMON_LIGHTING_INCLUDED