Нема описа
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

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