prePassRenderer.ts 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268
  1. import { PBRBaseMaterial } from "../Materials/PBR/PBRBaseMaterial";
  2. import { MultiRenderTarget } from "../Materials/Textures/multiRenderTarget";
  3. import { Scene } from "../scene";
  4. import { Engine } from "../Engines/Engine";
  5. import { Constants } from "../Engines/constants";
  6. import { SceneCompositorPostProcess } from "../PostProcesses/sceneCompositorPostProcess";
  7. import { SubSurfaceScatteringPostProcess } from "../PostProcesses/subSurfaceScatteringPostProcess";
  8. import { Effect } from "../Materials/effect";
  9. import { Logger } from "../Misc/logger";
  10. import { _DevTools } from '../Misc/devTools';
  11. import { Color3 } from "../Maths/math.color";
  12. export class PrePassRenderer {
  13. /** @hidden */
  14. public static _SceneComponentInitialization: (scene: Scene) => void = (_) => {
  15. throw _DevTools.WarnImport("PrePassRendererSceneComponent");
  16. }
  17. private _scene: Scene;
  18. private _engine: Engine;
  19. private _isDirty: boolean = false;
  20. public mrtCount: number = 4;
  21. public prePassRT: MultiRenderTarget;
  22. private _mrtTypes = [
  23. Constants.TEXTURETYPE_UNSIGNED_INT, // Original color
  24. Constants.TEXTURETYPE_HALF_FLOAT, // Irradiance
  25. Constants.TEXTURETYPE_HALF_FLOAT, // Depth (world units)
  26. Constants.TEXTURETYPE_UNSIGNED_INT
  27. ];
  28. private _multiRenderAttachments: number[];
  29. private _defaultAttachments: number[];
  30. private _clearAttachments: number[];
  31. public ssDiffusionS: number[] = [];
  32. public ssFilterRadii: number[] = [];
  33. public ssDiffusionD: number[] = [];
  34. public sceneCompositorPostProcess: SceneCompositorPostProcess;
  35. public subSurfaceScatteringPostProcess: SubSurfaceScatteringPostProcess;
  36. private _enabled: boolean = false;
  37. public get enabled() {
  38. return this._enabled;
  39. }
  40. public get samples() {
  41. return this.prePassRT.samples;
  42. }
  43. public set samples(n: number) {
  44. this.prePassRT.samples = n;
  45. }
  46. constructor(scene: Scene) {
  47. this._scene = scene;
  48. this._engine = scene.getEngine();
  49. PrePassRenderer._SceneComponentInitialization(this._scene);
  50. this.prePassRT = new MultiRenderTarget("sceneprePassRT", { width: this._engine.getRenderWidth(), height: this._engine.getRenderHeight() }, this.mrtCount, this._scene,
  51. { generateMipMaps: false, generateDepthTexture: true, defaultType: Constants.TEXTURETYPE_UNSIGNED_INT, types: this._mrtTypes });
  52. this.prePassRT.samples = 1;
  53. this._initializeAttachments();
  54. // Adding default diffusion profile
  55. this.addDiffusionProfile(new Color3(1, 1, 1));
  56. this.sceneCompositorPostProcess = new SceneCompositorPostProcess("sceneCompositor", 1, null, undefined, this._engine);
  57. this.sceneCompositorPostProcess.inputTexture = this.prePassRT.getInternalTexture()!;
  58. this.subSurfaceScatteringPostProcess = new SubSurfaceScatteringPostProcess("subSurfaceScattering", this._scene, 1, null, undefined, this._engine);
  59. }
  60. private _initializeAttachments() {
  61. let gl = this._engine._gl;
  62. this._multiRenderAttachments = [];
  63. this._clearAttachments = [gl.NONE];
  64. this._defaultAttachments = [gl.COLOR_ATTACHMENT0];
  65. for (let i = 0; i < this.mrtCount; i++) {
  66. this._multiRenderAttachments.push((<any>gl)["COLOR_ATTACHMENT" + i]);
  67. if (i > 0) {
  68. this._clearAttachments.push((<any>gl)["COLOR_ATTACHMENT" + i])
  69. this._defaultAttachments.push(gl.NONE)
  70. }
  71. }
  72. }
  73. public get isSupported() {
  74. // TODO
  75. return true;
  76. }
  77. public drawBuffers(effect: Effect) {
  78. if (this.enabled) {
  79. if (effect._multiTarget) {
  80. this._engine.renderToAttachments(this._multiRenderAttachments);
  81. } else {
  82. this._engine.renderToAttachments(this._defaultAttachments);
  83. }
  84. }
  85. }
  86. public _beforeCameraDraw() {
  87. if (this._isDirty) {
  88. this._update();
  89. }
  90. this._bindFrameBuffer();
  91. }
  92. public _afterCameraDraw() {
  93. if (this._enabled) {
  94. // this.sceneCompositorPostProcess.activate(this._scene.activeCamera);
  95. this.sceneCompositorPostProcess.autoClear = false;
  96. this.sceneCompositorPostProcess.activate(this._scene.activeCamera);
  97. this.subSurfaceScatteringPostProcess.activate(this._scene.activeCamera);
  98. this._scene.postProcessManager.directRender([this.sceneCompositorPostProcess], this.subSurfaceScatteringPostProcess.inputTexture);
  99. // this.getEngine().restoreDefaultFramebuffer(); // Restore back buffer if needed
  100. // this._scene.postProcessManager._prepareFrame();
  101. this._scene.postProcessManager.directRender([this.subSurfaceScatteringPostProcess], null, false, 0, 0, false);
  102. }
  103. }
  104. private _checkRTSize() {
  105. var requiredWidth = this._engine.getRenderWidth(true);
  106. var requiredHeight = this._engine.getRenderHeight(true);
  107. var width = this.prePassRT.getRenderWidth();
  108. var height = this.prePassRT.getRenderHeight();
  109. if (width !== requiredWidth || height !== requiredHeight) {
  110. this.prePassRT.resize({ width: requiredWidth, height: requiredHeight });
  111. this.sceneCompositorPostProcess.inputTexture = this.prePassRT.getInternalTexture()!;
  112. }
  113. }
  114. private _bindFrameBuffer() {
  115. if (this._enabled) {
  116. this._checkRTSize();
  117. var internalTexture = this.prePassRT.getInternalTexture();
  118. if (internalTexture) {
  119. this._engine.bindFramebuffer(internalTexture);
  120. } else {
  121. Logger.Error("High Definition pipeline error.");
  122. }
  123. return;
  124. }
  125. }
  126. public clear() {
  127. if (this._enabled) {
  128. this._bindFrameBuffer();
  129. this._engine.clear(this._scene.clearColor,
  130. this._scene.autoClear || this._scene.forceWireframe || this._scene.forcePointsCloud,
  131. this._scene.autoClearDepthAndStencil,
  132. this._scene.autoClearDepthAndStencil);
  133. this._engine.clearColorAttachments(this.prePassRT.getInternalTexture()!, this._clearAttachments);
  134. }
  135. }
  136. private _enable() {
  137. this._enabled = true;
  138. this._scene.prePass = true;
  139. }
  140. private _disable() {
  141. this._enabled = false;
  142. this._scene.prePass = false;
  143. }
  144. public markAsDirty() {
  145. this._isDirty = true;
  146. }
  147. private _update() {
  148. this._disable();
  149. // Subsurface scattering
  150. for (let i = 0; i < this._scene.materials.length; i++) {
  151. const material = this._scene.materials[i] as PBRBaseMaterial;
  152. if (material.subSurface && material.subSurface.isScatteringEnabled) {
  153. this._enable();
  154. }
  155. }
  156. // SSAO 2
  157. // TODO
  158. this._isDirty = false;
  159. if (!this.enabled) {
  160. this._engine.renderToAttachments(this._defaultAttachments);
  161. }
  162. }
  163. public addDiffusionProfile(color: Color3) : number {
  164. if (this.ssDiffusionD.length >= 5) {
  165. // We only suppport 5 diffusion profiles
  166. Logger.Error("You already reached the maximum number of diffusion profiles.");
  167. return -1;
  168. }
  169. // Do not add doubles
  170. for (let i = 0; i < this.ssDiffusionS.length / 3; i++) {
  171. if (this.ssDiffusionS[i * 3] === color.r &&
  172. this.ssDiffusionS[i * 3 + 1] === color.g &&
  173. this.ssDiffusionS[i * 3 + 2] === color.b) {
  174. return i;
  175. }
  176. }
  177. this.ssDiffusionS.push(color.r, color.b, color.g);
  178. this.ssDiffusionD.push(Math.max(Math.max(color.r, color.b), color.g));
  179. this.ssFilterRadii.push(this.getDiffusionProfileParameters(color));
  180. this._scene.ssDiffusionProfileColors.push(color);
  181. return this.ssDiffusionD.length - 1;
  182. }
  183. public clearAllDiffusionProfiles() {
  184. this.ssDiffusionD = [];
  185. this.ssDiffusionS = [];
  186. this.ssFilterRadii = [];
  187. this._scene.ssDiffusionProfileColors = [];
  188. }
  189. public getDiffusionProfileParameters(color: Color3)
  190. {
  191. const cdf = 0.997;
  192. // Importance sample the normalized diffuse reflectance profile for the computed value of 's'.
  193. // ------------------------------------------------------------------------------------
  194. // R[r, phi, s] = s * (Exp[-r * s] + Exp[-r * s / 3]) / (8 * Pi * r)
  195. // PDF[r, phi, s] = r * R[r, phi, s]
  196. // CDF[r, s] = 1 - 1/4 * Exp[-r * s] - 3/4 * Exp[-r * s / 3]
  197. // ------------------------------------------------------------------------------------
  198. // We importance sample the color channel with the widest scattering distance.
  199. const maxScatteringDistance = Math.max(color.r, color.g, color.b);
  200. return this._sampleBurleyDiffusionProfile(cdf, maxScatteringDistance);
  201. }
  202. // https://zero-radiance.github.io/post/sampling-diffusion/
  203. // Performs sampling of a Normalized Burley diffusion profile in polar coordinates.
  204. // 'u' is the random number (the value of the CDF): [0, 1).
  205. // rcp(s) = 1 / ShapeParam = ScatteringDistance.
  206. // Returns the sampled radial distance, s.t. (u = 0 -> r = 0) and (u = 1 -> r = Inf).
  207. private _sampleBurleyDiffusionProfile(u: number, rcpS: number)
  208. {
  209. u = 1 - u; // Convert CDF to CCDF
  210. let g = 1 + (4 * u) * (2 * u + Math.sqrt(1 + (4 * u) * u));
  211. let n = Math.pow(g, -1.0 / 3.0); // g^(-1/3)
  212. let p = (g * n) * n; // g^(+1/3)
  213. let c = 1 + p + n; // 1 + g^(+1/3) + g^(-1/3)
  214. let x = 3 * Math.log(c / (4 * u));
  215. return x * rcpS;
  216. }
  217. public dispose() {
  218. this.sceneCompositorPostProcess.dispose();
  219. this.subSurfaceScatteringPostProcess.dispose();
  220. this.prePassRT.dispose();
  221. }
  222. }