subSurfaceConfiguration.js 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170
  1. import { Logger } from "../Misc/logger.js";
  2. import { Color3 } from "../Maths/math.color.js";
  3. import { SubSurfaceScatteringPostProcess } from "../PostProcesses/subSurfaceScatteringPostProcess.js";
  4. import { SceneComponentConstants } from "../sceneComponent.js";
  5. import { _WarnImport } from "../Misc/devTools.js";
  6. /**
  7. * Contains all parameters needed for the prepass to perform
  8. * screen space subsurface scattering
  9. */
  10. export class SubSurfaceConfiguration {
  11. /**
  12. * Diffusion profile color for subsurface scattering
  13. */
  14. get ssDiffusionS() {
  15. return this._ssDiffusionS;
  16. }
  17. /**
  18. * Diffusion profile max color channel value for subsurface scattering
  19. */
  20. get ssDiffusionD() {
  21. return this._ssDiffusionD;
  22. }
  23. /**
  24. * Diffusion profile filter radius for subsurface scattering
  25. */
  26. get ssFilterRadii() {
  27. return this._ssFilterRadii;
  28. }
  29. /**
  30. * Builds a subsurface configuration object
  31. * @param scene The scene
  32. */
  33. constructor(scene) {
  34. this._ssDiffusionS = [];
  35. this._ssFilterRadii = [];
  36. this._ssDiffusionD = [];
  37. /**
  38. * Is subsurface enabled
  39. */
  40. this.enabled = false;
  41. /**
  42. * Does the output of this prepass need to go through imageprocessing
  43. */
  44. this.needsImageProcessing = true;
  45. /**
  46. * Name of the configuration
  47. */
  48. this.name = SceneComponentConstants.NAME_SUBSURFACE;
  49. /**
  50. * Diffusion profile colors for subsurface scattering
  51. * You can add one diffusion color using `addDiffusionProfile` on `scene.prePassRenderer`
  52. * See ...
  53. * Note that you can only store up to 5 of them
  54. */
  55. this.ssDiffusionProfileColors = [];
  56. /**
  57. * Defines the ratio real world => scene units.
  58. * Used for subsurface scattering
  59. */
  60. this.metersPerUnit = 1;
  61. /**
  62. * Textures that should be present in the MRT for this effect to work
  63. */
  64. this.texturesRequired = [
  65. 5,
  66. 7,
  67. 4,
  68. 0,
  69. ];
  70. // Adding default diffusion profile
  71. this.addDiffusionProfile(new Color3(1, 1, 1));
  72. this._scene = scene;
  73. SubSurfaceConfiguration._SceneComponentInitialization(this._scene);
  74. }
  75. /**
  76. * Adds a new diffusion profile.
  77. * Useful for more realistic subsurface scattering on diverse materials.
  78. * @param color The color of the diffusion profile. Should be the average color of the material.
  79. * @returns The index of the diffusion profile for the material subsurface configuration
  80. */
  81. addDiffusionProfile(color) {
  82. if (this.ssDiffusionD.length >= 5) {
  83. // We only suppport 5 diffusion profiles
  84. Logger.Error("You already reached the maximum number of diffusion profiles.");
  85. return 0; // default profile
  86. }
  87. // Do not add doubles
  88. for (let i = 0; i < this._ssDiffusionS.length / 3; i++) {
  89. if (this._ssDiffusionS[i * 3] === color.r && this._ssDiffusionS[i * 3 + 1] === color.g && this._ssDiffusionS[i * 3 + 2] === color.b) {
  90. return i;
  91. }
  92. }
  93. this._ssDiffusionS.push(color.r, color.b, color.g);
  94. this._ssDiffusionD.push(Math.max(Math.max(color.r, color.b), color.g));
  95. this._ssFilterRadii.push(this.getDiffusionProfileParameters(color));
  96. this.ssDiffusionProfileColors.push(color);
  97. return this._ssDiffusionD.length - 1;
  98. }
  99. /**
  100. * Creates the sss post process
  101. * @returns The created post process
  102. */
  103. createPostProcess() {
  104. this.postProcess = new SubSurfaceScatteringPostProcess("subSurfaceScattering", this._scene, 1, null, undefined, this._scene.getEngine());
  105. this.postProcess.autoClear = false;
  106. return this.postProcess;
  107. }
  108. /**
  109. * Deletes all diffusion profiles.
  110. * Note that in order to render subsurface scattering, you should have at least 1 diffusion profile.
  111. */
  112. clearAllDiffusionProfiles() {
  113. this._ssDiffusionD = [];
  114. this._ssDiffusionS = [];
  115. this._ssFilterRadii = [];
  116. this.ssDiffusionProfileColors = [];
  117. }
  118. /**
  119. * Disposes this object
  120. */
  121. dispose() {
  122. this.clearAllDiffusionProfiles();
  123. if (this.postProcess) {
  124. this.postProcess.dispose();
  125. }
  126. }
  127. /**
  128. * @internal
  129. * https://zero-radiance.github.io/post/sampling-diffusion/
  130. *
  131. * Importance sample the normalized diffuse reflectance profile for the computed value of 's'.
  132. * ------------------------------------------------------------------------------------
  133. * R[r, phi, s] = s * (Exp[-r * s] + Exp[-r * s / 3]) / (8 * Pi * r)
  134. * PDF[r, phi, s] = r * R[r, phi, s]
  135. * CDF[r, s] = 1 - 1/4 * Exp[-r * s] - 3/4 * Exp[-r * s / 3]
  136. * ------------------------------------------------------------------------------------
  137. * We importance sample the color channel with the widest scattering distance.
  138. */
  139. getDiffusionProfileParameters(color) {
  140. const cdf = 0.997;
  141. const maxScatteringDistance = Math.max(color.r, color.g, color.b);
  142. return this._sampleBurleyDiffusionProfile(cdf, maxScatteringDistance);
  143. }
  144. /**
  145. * Performs sampling of a Normalized Burley diffusion profile in polar coordinates.
  146. * 'u' is the random number (the value of the CDF): [0, 1).
  147. * rcp(s) = 1 / ShapeParam = ScatteringDistance.
  148. * Returns the sampled radial distance, s.t. (u = 0 -> r = 0) and (u = 1 -> r = Inf).
  149. * @param u
  150. * @param rcpS
  151. * @returns The sampled radial distance
  152. */
  153. _sampleBurleyDiffusionProfile(u, rcpS) {
  154. u = 1 - u; // Convert CDF to CCDF
  155. const g = 1 + 4 * u * (2 * u + Math.sqrt(1 + 4 * u * u));
  156. const n = Math.pow(g, -1.0 / 3.0); // g^(-1/3)
  157. const p = g * n * n; // g^(+1/3)
  158. const c = 1 + p + n; // 1 + g^(+1/3) + g^(-1/3)
  159. const x = 3 * Math.log(c / (4 * u));
  160. return x * rcpS;
  161. }
  162. }
  163. /**
  164. * @internal
  165. */
  166. SubSurfaceConfiguration._SceneComponentInitialization = (_) => {
  167. throw _WarnImport("SubSurfaceSceneComponent");
  168. };
  169. //# sourceMappingURL=subSurfaceConfiguration.js.map