subSurfaceConfiguration.ts 5.9 KB

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