babylon.sphericalPolynomial.ts 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189
  1. module BABYLON {
  2. export class SphericalPolynomial {
  3. public x: Vector3 = Vector3.Zero();
  4. public y: Vector3 = Vector3.Zero();
  5. public z: Vector3 = Vector3.Zero();
  6. public xx: Vector3 = Vector3.Zero();
  7. public yy: Vector3 = Vector3.Zero();
  8. public zz: Vector3 = Vector3.Zero();
  9. public xy: Vector3 = Vector3.Zero();
  10. public yz: Vector3 = Vector3.Zero();
  11. public zx: Vector3 = Vector3.Zero();
  12. public addAmbient(color: Color3): void {
  13. var colorVector = new Vector3(color.r, color.g, color.b);
  14. this.xx = this.xx.add(colorVector);
  15. this.yy = this.yy.add(colorVector);
  16. this.zz = this.zz.add(colorVector);
  17. }
  18. public scale(scale: number)
  19. {
  20. this.x = this.x.scale(scale);
  21. this.y = this.y.scale(scale);
  22. this.z = this.z.scale(scale);
  23. this.xx = this.xx.scale(scale);
  24. this.yy = this.yy.scale(scale);
  25. this.zz = this.zz.scale(scale);
  26. this.yz = this.yz.scale(scale);
  27. this.zx = this.zx.scale(scale);
  28. this.xy = this.xy.scale(scale);
  29. }
  30. public static FromHarmonics(harmonics: SphericalHarmonics): SphericalPolynomial {
  31. var result = new SphericalPolynomial();
  32. result.x = harmonics.L11.scale(1.02333);
  33. result.y = harmonics.L1_1.scale(1.02333);
  34. result.z = harmonics.L10.scale(1.02333);
  35. result.xx = harmonics.L00.scale(0.886277).subtract(harmonics.L20.scale(0.247708)).add(harmonics.L22.scale(0.429043));
  36. result.yy = harmonics.L00.scale(0.886277).subtract(harmonics.L20.scale(0.247708)).subtract(harmonics.L22.scale(0.429043));
  37. result.zz = harmonics.L00.scale(0.886277).add(harmonics.L20.scale(0.495417));
  38. result.yz = harmonics.L2_1.scale(0.858086);
  39. result.zx = harmonics.L21.scale(0.858086);
  40. result.xy = harmonics.L2_2.scale(0.858086);
  41. result.scale(1.0 / Math.PI);
  42. return result;
  43. }
  44. /**
  45. * Constructs a spherical polynomial from an array.
  46. * @param data defines the 9x3 coefficients (x, y, z, xx, yy, zz, yz, zx, xy)
  47. */
  48. public static FromArray(data: ArrayLike<ArrayLike<number>>): SphericalPolynomial {
  49. const sp = new SphericalPolynomial();
  50. Vector3.FromArrayToRef(data[0], 0, sp.x);
  51. Vector3.FromArrayToRef(data[1], 0, sp.y);
  52. Vector3.FromArrayToRef(data[2], 0, sp.z);
  53. Vector3.FromArrayToRef(data[3], 0, sp.xx);
  54. Vector3.FromArrayToRef(data[4], 0, sp.yy);
  55. Vector3.FromArrayToRef(data[5], 0, sp.zz);
  56. Vector3.FromArrayToRef(data[6], 0, sp.yz);
  57. Vector3.FromArrayToRef(data[7], 0, sp.zx);
  58. Vector3.FromArrayToRef(data[8], 0, sp.xy);
  59. return sp;
  60. }
  61. }
  62. export class SphericalHarmonics {
  63. public L00: Vector3 = Vector3.Zero();
  64. public L1_1: Vector3 = Vector3.Zero();
  65. public L10: Vector3 = Vector3.Zero();
  66. public L11: Vector3 = Vector3.Zero();
  67. public L2_2: Vector3 = Vector3.Zero();
  68. public L2_1: Vector3 = Vector3.Zero();
  69. public L20: Vector3 = Vector3.Zero();
  70. public L21: Vector3 = Vector3.Zero();
  71. public L22: Vector3 = Vector3.Zero();
  72. public addLight(direction: Vector3, color: Color3, deltaSolidAngle: number): void {
  73. var colorVector = new Vector3(color.r, color.g, color.b);
  74. var c = colorVector.scale(deltaSolidAngle);
  75. this.L00 = this.L00.add(c.scale(0.282095));
  76. this.L1_1 = this.L1_1.add(c.scale(0.488603 * direction.y));
  77. this.L10 = this.L10.add(c.scale(0.488603 * direction.z));
  78. this.L11 = this.L11.add(c.scale(0.488603 * direction.x));
  79. this.L2_2 = this.L2_2.add(c.scale(1.092548 * direction.x * direction.y));
  80. this.L2_1 = this.L2_1.add(c.scale(1.092548 * direction.y * direction.z));
  81. this.L21 = this.L21.add(c.scale(1.092548 * direction.x * direction.z));
  82. this.L20 = this.L20.add(c.scale(0.315392 * (3.0 * direction.z * direction.z - 1.0)));
  83. this.L22 = this.L22.add(c.scale(0.546274 * (direction.x * direction.x - direction.y * direction.y)));
  84. }
  85. public scale(scale: number): void {
  86. this.L00 = this.L00.scale(scale);
  87. this.L1_1 = this.L1_1.scale(scale);
  88. this.L10 = this.L10.scale(scale);
  89. this.L11 = this.L11.scale(scale);
  90. this.L2_2 = this.L2_2.scale(scale);
  91. this.L2_1 = this.L2_1.scale(scale);
  92. this.L20 = this.L20.scale(scale);
  93. this.L21 = this.L21.scale(scale);
  94. this.L22 = this.L22.scale(scale);
  95. }
  96. public convertIncidentRadianceToIrradiance(): void
  97. {
  98. // Convert from incident radiance (Li) to irradiance (E) by applying convolution with the cosine-weighted hemisphere.
  99. //
  100. // E_lm = A_l * L_lm
  101. //
  102. // In spherical harmonics this convolution amounts to scaling factors for each frequency band.
  103. // This corresponds to equation 5 in "An Efficient Representation for Irradiance Environment Maps", where
  104. // the scaling factors are given in equation 9.
  105. // Constant (Band 0)
  106. this.L00 = this.L00.scale(3.141593);
  107. // Linear (Band 1)
  108. this.L1_1 = this.L1_1.scale(2.094395);
  109. this.L10 = this.L10.scale(2.094395);
  110. this.L11 = this.L11.scale(2.094395);
  111. // Quadratic (Band 2)
  112. this.L2_2 = this.L2_2.scale(0.785398);
  113. this.L2_1 = this.L2_1.scale(0.785398);
  114. this.L20 = this.L20.scale(0.785398);
  115. this.L21 = this.L21.scale(0.785398);
  116. this.L22 = this.L22.scale(0.785398);
  117. }
  118. public convertIrradianceToLambertianRadiance(): void
  119. {
  120. // Convert from irradiance to outgoing radiance for Lambertian BDRF, suitable for efficient shader evaluation.
  121. // L = (1/pi) * E * rho
  122. //
  123. // This is done by an additional scale by 1/pi, so is a fairly trivial operation but important conceptually.
  124. this.scale(1.0 / Math.PI);
  125. // The resultant SH now represents outgoing radiance, so includes the Lambert 1/pi normalisation factor but without albedo (rho) applied
  126. // (The pixel shader must apply albedo after texture fetches, etc).
  127. }
  128. public static FromPolynomial(polynomial: SphericalPolynomial): SphericalHarmonics
  129. {
  130. var result = new SphericalHarmonics();
  131. result.L00 = polynomial.xx.scale(0.376127).add(polynomial.yy.scale(0.376127)).add(polynomial.zz.scale(0.376126));
  132. result.L1_1 = polynomial.y.scale(0.977204);
  133. result.L10 = polynomial.z.scale(0.977204);
  134. result.L11 = polynomial.x.scale(0.977204);
  135. result.L2_2 = polynomial.xy.scale(1.16538);
  136. result.L2_1 = polynomial.yz.scale(1.16538);
  137. result.L20 = polynomial.zz.scale(1.34567).subtract(polynomial.xx.scale(0.672834)).subtract(polynomial.yy.scale(0.672834));
  138. result.L21 = polynomial.zx.scale(1.16538);
  139. result.L22 = polynomial.xx.scale(1.16538).subtract(polynomial.yy.scale(1.16538));
  140. result.scale(Math.PI);
  141. return result;
  142. }
  143. /**
  144. * Constructs a spherical harmonics from an array.
  145. * @param data defines the 9x3 coefficients (l00, l1-1, l10, l11, l2-2, l2-1, l20, l21, l22)
  146. */
  147. public static FromArray(data: ArrayLike<ArrayLike<number>>): SphericalHarmonics {
  148. const sh = new SphericalHarmonics();
  149. Vector3.FromArrayToRef(data[0], 0, sh.L00);
  150. Vector3.FromArrayToRef(data[1], 0, sh.L1_1);
  151. Vector3.FromArrayToRef(data[2], 0, sh.L10);
  152. Vector3.FromArrayToRef(data[3], 0, sh.L11);
  153. Vector3.FromArrayToRef(data[4], 0, sh.L2_2);
  154. Vector3.FromArrayToRef(data[5], 0, sh.L2_1);
  155. Vector3.FromArrayToRef(data[6], 0, sh.L20);
  156. Vector3.FromArrayToRef(data[7], 0, sh.L21);
  157. Vector3.FromArrayToRef(data[8], 0, sh.L22);
  158. return sh;
  159. }
  160. }
  161. }