babylon.hdrRenderingPipeline.js 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373
  1. var __extends = (this && this.__extends) || function (d, b) {
  2. for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
  3. function __() { this.constructor = d; }
  4. __.prototype = b.prototype;
  5. d.prototype = new __();
  6. };
  7. var BABYLON;
  8. (function (BABYLON) {
  9. var HDRRenderingPipeline = (function (_super) {
  10. __extends(HDRRenderingPipeline, _super);
  11. /**
  12. * @constructor
  13. * @param {string} name - The rendering pipeline name
  14. * @param {BABYLON.Scene} scene - The scene linked to this pipeline
  15. * @param {any} ratio - The size of the postprocesses (0.5 means that your postprocess will have a width = canvas.width 0.5 and a height = canvas.height 0.5)
  16. * @param {BABYLON.PostProcess} originalPostProcess - the custom original color post-process. Must be "reusable". Can be null.
  17. * @param {BABYLON.Camera[]} cameras - The array of cameras that the rendering pipeline will be attached to
  18. */
  19. function HDRRenderingPipeline(name, scene, ratio, originalPostProcess, cameras) {
  20. var _this = this;
  21. if (originalPostProcess === void 0) { originalPostProcess = null; }
  22. _super.call(this, scene.getEngine(), name);
  23. /**
  24. * Public members
  25. */
  26. // Gaussian Blur
  27. /**
  28. * Gaussian blur coefficient
  29. * @type {number}
  30. */
  31. this.gaussCoeff = 0.3;
  32. /**
  33. * Gaussian blur mean
  34. * @type {number}
  35. */
  36. this.gaussMean = 1.0;
  37. /**
  38. * Gaussian blur standard deviation
  39. * @type {number}
  40. */
  41. this.gaussStandDev = 0.8;
  42. // HDR
  43. /**
  44. * Exposure, controls the overall intensity of the pipeline
  45. * @type {number}
  46. */
  47. this.exposure = 1.0;
  48. /**
  49. * Minimum luminance that the post-process can output. Luminance is >= 0
  50. * @type {number}
  51. */
  52. this.minimumLuminance = 1.0;
  53. /**
  54. * Maximum luminance that the post-process can output. Must be suprerior to minimumLuminance
  55. * @type {number}
  56. */
  57. this.maximumLuminance = 1e20;
  58. /**
  59. * Increase rate for luminance: eye adaptation speed to dark
  60. * @type {number}
  61. */
  62. this.luminanceIncreaserate = 0.5;
  63. /**
  64. * Decrease rate for luminance: eye adaptation speed to bright
  65. * @type {number}
  66. */
  67. this.luminanceDecreaseRate = 0.5;
  68. // Bright pass
  69. /**
  70. * Minimum luminance needed to compute HDR
  71. * @type {number}
  72. */
  73. this.brightThreshold = 0.8;
  74. this._needUpdate = true;
  75. this._scene = scene;
  76. // Bright pass
  77. this._createBrightPassPostProcess(scene, ratio);
  78. // Down sample X4
  79. this._createDownSampleX4PostProcess(scene, ratio);
  80. // Create gaussian blur post-processes
  81. this._createGaussianBlurPostProcess(scene, ratio);
  82. // Texture adder
  83. this._createTextureAdderPostProcess(scene, ratio);
  84. // Luminance generator
  85. this._createLuminanceGeneratorPostProcess(scene);
  86. // HDR
  87. this._createHDRPostProcess(scene, ratio);
  88. // Pass postprocess
  89. if (originalPostProcess === null) {
  90. this._originalPostProcess = new BABYLON.PassPostProcess("hdr", ratio, null, BABYLON.Texture.BILINEAR_SAMPLINGMODE, scene.getEngine(), false);
  91. }
  92. else {
  93. this._originalPostProcess = originalPostProcess;
  94. }
  95. // Configure pipeline
  96. this.addEffect(new BABYLON.PostProcessRenderEffect(scene.getEngine(), "HDRPassPostProcess", function () { return _this._originalPostProcess; }, true));
  97. this.addEffect(new BABYLON.PostProcessRenderEffect(scene.getEngine(), "HDRBrightPass", function () { return _this._brightPassPostProcess; }, true));
  98. this.addEffect(new BABYLON.PostProcessRenderEffect(scene.getEngine(), "HDRDownSampleX4", function () { return _this._downSampleX4PostProcess; }, true));
  99. this.addEffect(new BABYLON.PostProcessRenderEffect(scene.getEngine(), "HDRGaussianBlurH", function () { return _this._guassianBlurHPostProcess; }, true));
  100. this.addEffect(new BABYLON.PostProcessRenderEffect(scene.getEngine(), "HDRGaussianBlurV", function () { return _this._guassianBlurVPostProcess; }, true));
  101. this.addEffect(new BABYLON.PostProcessRenderEffect(scene.getEngine(), "HDRTextureAdder", function () { return _this._textureAdderPostProcess; }, true));
  102. var addDownSamplerPostProcess = function (id) {
  103. _this.addEffect(new BABYLON.PostProcessRenderEffect(scene.getEngine(), "HDRDownSampler" + id, function () { return _this._downSamplePostProcesses[id]; }, true));
  104. };
  105. for (var i = HDRRenderingPipeline.LUM_STEPS - 1; i >= 0; i--) {
  106. addDownSamplerPostProcess(i);
  107. }
  108. this.addEffect(new BABYLON.PostProcessRenderEffect(scene.getEngine(), "HDR", function () { return _this._hdrPostProcess; }, true));
  109. // Finish
  110. scene.postProcessRenderPipelineManager.addPipeline(this);
  111. if (cameras !== null) {
  112. scene.postProcessRenderPipelineManager.attachCamerasToRenderPipeline(name, cameras);
  113. }
  114. this.update();
  115. }
  116. /**
  117. * Tells the pipeline to update its post-processes
  118. */
  119. HDRRenderingPipeline.prototype.update = function () {
  120. this._needUpdate = true;
  121. };
  122. /**
  123. * Returns the current calculated luminance
  124. */
  125. HDRRenderingPipeline.prototype.getCurrentLuminance = function () {
  126. return this._hdrCurrentLuminance;
  127. };
  128. /**
  129. * Returns the currently drawn luminance
  130. */
  131. HDRRenderingPipeline.prototype.getOutputLuminance = function () {
  132. return this._hdrOutputLuminance;
  133. };
  134. /**
  135. * Releases the rendering pipeline and its internal effects. Detaches pipeline from cameras
  136. */
  137. HDRRenderingPipeline.prototype.dispose = function () {
  138. this._originalPostProcess = undefined;
  139. this._brightPassPostProcess = undefined;
  140. this._downSampleX4PostProcess = undefined;
  141. this._guassianBlurHPostProcess = undefined;
  142. this._guassianBlurVPostProcess = undefined;
  143. this._textureAdderPostProcess = undefined;
  144. for (var i = HDRRenderingPipeline.LUM_STEPS - 1; i >= 0; i--) {
  145. this._downSamplePostProcesses[i] = undefined;
  146. }
  147. this._hdrPostProcess = undefined;
  148. this._scene.postProcessRenderPipelineManager.detachCamerasFromRenderPipeline(this._name, this._scene.cameras);
  149. };
  150. /**
  151. * Creates the HDR post-process and computes the luminance adaptation
  152. */
  153. HDRRenderingPipeline.prototype._createHDRPostProcess = function (scene, ratio) {
  154. var _this = this;
  155. var hdrLastLuminance = 0.0;
  156. this._hdrOutputLuminance = -1.0;
  157. this._hdrCurrentLuminance = 1.0;
  158. this._hdrPostProcess = new BABYLON.PostProcess("hdr", "hdr", ["exposure", "avgLuminance"], ["otherSampler"], ratio, null, BABYLON.Texture.BILINEAR_SAMPLINGMODE, scene.getEngine(), false, "#define HDR");
  159. this._hdrPostProcess.onApply = function (effect) {
  160. if (_this._hdrOutputLuminance < 0.0) {
  161. _this._hdrOutputLuminance = _this._hdrCurrentLuminance;
  162. }
  163. else {
  164. var dt = (hdrLastLuminance - (hdrLastLuminance + scene.getEngine().getDeltaTime())) / 1000.0;
  165. if (_this._hdrCurrentLuminance < _this._hdrOutputLuminance + _this.luminanceDecreaseRate * dt) {
  166. _this._hdrOutputLuminance += _this.luminanceDecreaseRate * dt;
  167. }
  168. else if (_this._hdrCurrentLuminance > _this._hdrOutputLuminance - _this.luminanceIncreaserate * dt) {
  169. _this._hdrOutputLuminance -= _this.luminanceIncreaserate * dt;
  170. }
  171. else {
  172. _this._hdrOutputLuminance = _this._hdrCurrentLuminance;
  173. }
  174. }
  175. _this._hdrOutputLuminance = BABYLON.Tools.Clamp(_this._hdrOutputLuminance, _this.minimumLuminance, _this.maximumLuminance);
  176. hdrLastLuminance += scene.getEngine().getDeltaTime();
  177. effect.setTextureFromPostProcess("textureSampler", _this._textureAdderPostProcess);
  178. effect.setTextureFromPostProcess("otherSampler", _this._originalPostProcess);
  179. effect.setFloat("exposure", _this.exposure);
  180. effect.setFloat("avgLuminance", _this._hdrOutputLuminance);
  181. _this._needUpdate = false;
  182. };
  183. };
  184. /**
  185. * Texture Adder post-process
  186. */
  187. HDRRenderingPipeline.prototype._createTextureAdderPostProcess = function (scene, ratio) {
  188. var _this = this;
  189. this._textureAdderPostProcess = new BABYLON.PostProcess("hdr", "hdr", [], ["otherSampler"], ratio, null, BABYLON.Texture.BILINEAR_SAMPLINGMODE, scene.getEngine(), false, "#define TEXTURE_ADDER");
  190. this._textureAdderPostProcess.onApply = function (effect) {
  191. effect.setTextureFromPostProcess("otherSampler", _this._originalPostProcess);
  192. };
  193. };
  194. /**
  195. * Down sample X4 post-process
  196. */
  197. HDRRenderingPipeline.prototype._createDownSampleX4PostProcess = function (scene, ratio) {
  198. var _this = this;
  199. var downSampleX4Offsets = new Array(32);
  200. this._downSampleX4PostProcess = new BABYLON.PostProcess("hdr", "hdr", ["dsOffsets"], [], ratio / 4, null, BABYLON.Texture.BILINEAR_SAMPLINGMODE, scene.getEngine(), false, "#define DOWN_SAMPLE_X4");
  201. this._downSampleX4PostProcess.onApply = function (effect) {
  202. if (_this._needUpdate) {
  203. var id = 0;
  204. for (var i = -2; i < 2; i++) {
  205. for (var j = -2; j < 2; j++) {
  206. downSampleX4Offsets[id] = (i + 0.5) * (1.0 / _this._downSampleX4PostProcess.width);
  207. downSampleX4Offsets[id + 1] = (j + 0.5) * (1.0 / _this._downSampleX4PostProcess.height);
  208. id += 2;
  209. }
  210. }
  211. }
  212. effect.setArray2("dsOffsets", downSampleX4Offsets);
  213. };
  214. };
  215. /**
  216. * Bright pass post-process
  217. */
  218. HDRRenderingPipeline.prototype._createBrightPassPostProcess = function (scene, ratio) {
  219. var _this = this;
  220. var brightOffsets = new Array(8);
  221. var brightPassCallback = function (effect) {
  222. if (_this._needUpdate) {
  223. var sU = (1.0 / _this._brightPassPostProcess.width);
  224. var sV = (1.0 / _this._brightPassPostProcess.height);
  225. brightOffsets[0] = -0.5 * sU;
  226. brightOffsets[1] = 0.5 * sV;
  227. brightOffsets[2] = 0.5 * sU;
  228. brightOffsets[3] = 0.5 * sV;
  229. brightOffsets[4] = -0.5 * sU;
  230. brightOffsets[5] = -0.5 * sV;
  231. brightOffsets[6] = 0.5 * sU;
  232. brightOffsets[7] = -0.5 * sV;
  233. }
  234. effect.setArray2("dsOffsets", brightOffsets);
  235. effect.setFloat("brightThreshold", _this.brightThreshold);
  236. };
  237. this._brightPassPostProcess = new BABYLON.PostProcess("hdr", "hdr", ["dsOffsets", "brightThreshold"], [], ratio, null, BABYLON.Texture.BILINEAR_SAMPLINGMODE, scene.getEngine(), false, "#define BRIGHT_PASS");
  238. this._brightPassPostProcess.onApply = brightPassCallback;
  239. };
  240. /**
  241. * Luminance generator. Creates the luminance post-process and down sample post-processes
  242. */
  243. HDRRenderingPipeline.prototype._createLuminanceGeneratorPostProcess = function (scene) {
  244. var _this = this;
  245. var lumSteps = HDRRenderingPipeline.LUM_STEPS;
  246. var luminanceOffsets = new Array(8);
  247. var downSampleOffsets = new Array(18);
  248. var halfDestPixelSize;
  249. this._downSamplePostProcesses = new Array(lumSteps);
  250. // Utils for luminance
  251. var luminanceUpdateSourceOffsets = function (width, height) {
  252. var sU = (1.0 / width);
  253. var sV = (1.0 / height);
  254. luminanceOffsets[0] = -0.5 * sU;
  255. luminanceOffsets[1] = 0.5 * sV;
  256. luminanceOffsets[2] = 0.5 * sU;
  257. luminanceOffsets[3] = 0.5 * sV;
  258. luminanceOffsets[4] = -0.5 * sU;
  259. luminanceOffsets[5] = -0.5 * sV;
  260. luminanceOffsets[6] = 0.5 * sU;
  261. luminanceOffsets[7] = -0.5 * sV;
  262. };
  263. var luminanceUpdateDestOffsets = function (width, height) {
  264. var id = 0;
  265. for (var x = -1; x < 2; x++) {
  266. for (var y = -1; y < 2; y++) {
  267. downSampleOffsets[id] = (x) / width;
  268. downSampleOffsets[id + 1] = (y) / height;
  269. id += 2;
  270. }
  271. }
  272. };
  273. // Luminance callback
  274. var luminanceCallback = function (effect) {
  275. if (_this._needUpdate) {
  276. luminanceUpdateSourceOffsets(_this._textureAdderPostProcess.width, _this._textureAdderPostProcess.height);
  277. }
  278. effect.setTextureFromPostProcess("textureSampler", _this._textureAdderPostProcess);
  279. effect.setArray2("lumOffsets", luminanceOffsets);
  280. };
  281. // Down sample callbacks
  282. var downSampleCallback = function (indice) {
  283. var i = indice;
  284. return function (effect) {
  285. luminanceUpdateSourceOffsets(_this._downSamplePostProcesses[i].width, _this._downSamplePostProcesses[i].height);
  286. luminanceUpdateDestOffsets(_this._downSamplePostProcesses[i].width, _this._downSamplePostProcesses[i].height);
  287. halfDestPixelSize = 0.5 / _this._downSamplePostProcesses[i].width;
  288. effect.setTextureFromPostProcess("textureSampler", _this._downSamplePostProcesses[i + 1]);
  289. effect.setFloat("halfDestPixelSize", halfDestPixelSize);
  290. effect.setArray2("dsOffsets", downSampleOffsets);
  291. };
  292. };
  293. var downSampleAfterRenderCallback = function (effect) {
  294. // Unpack result
  295. var pixel = scene.getEngine().readPixels(0, 0, 1, 1);
  296. var bit_shift = new BABYLON.Vector4(1.0 / (255.0 * 255.0 * 255.0), 1.0 / (255.0 * 255.0), 1.0 / 255.0, 1.0);
  297. _this._hdrCurrentLuminance = (pixel[0] * bit_shift.x + pixel[1] * bit_shift.y + pixel[2] * bit_shift.z + pixel[3] * bit_shift.w) / 100.0;
  298. };
  299. // Create luminance post-process
  300. var ratio = { width: Math.pow(3, lumSteps - 1), height: Math.pow(3, lumSteps - 1) };
  301. this._downSamplePostProcesses[lumSteps - 1] = new BABYLON.PostProcess("hdr", "hdr", ["lumOffsets"], [], ratio, null, BABYLON.Texture.NEAREST_SAMPLINGMODE, scene.getEngine(), false, "#define LUMINANCE_GENERATOR", BABYLON.Engine.TEXTURETYPE_FLOAT);
  302. this._downSamplePostProcesses[lumSteps - 1].onApply = luminanceCallback;
  303. // Create down sample post-processes
  304. for (var i = lumSteps - 2; i >= 0; i--) {
  305. var length = Math.pow(3, i);
  306. ratio = { width: length, height: length };
  307. var defines = "#define DOWN_SAMPLE\n";
  308. if (i === 0) {
  309. defines += "#define FINAL_DOWN_SAMPLE\n"; // To pack the result
  310. }
  311. this._downSamplePostProcesses[i] = new BABYLON.PostProcess("hdr", "hdr", ["dsOffsets", "halfDestPixelSize"], [], ratio, null, BABYLON.Texture.NEAREST_SAMPLINGMODE, scene.getEngine(), false, defines, BABYLON.Engine.TEXTURETYPE_FLOAT);
  312. this._downSamplePostProcesses[i].onApply = downSampleCallback(i);
  313. if (i === 0) {
  314. this._downSamplePostProcesses[i].onAfterRender = downSampleAfterRenderCallback;
  315. }
  316. }
  317. };
  318. /**
  319. * Gaussian blur post-processes. Horizontal and Vertical
  320. */
  321. HDRRenderingPipeline.prototype._createGaussianBlurPostProcess = function (scene, ratio) {
  322. var _this = this;
  323. var blurOffsetsW = new Array(9);
  324. var blurOffsetsH = new Array(9);
  325. var blurWeights = new Array(9);
  326. var uniforms = ["blurOffsets", "blurWeights"];
  327. // Utils for gaussian blur
  328. var calculateBlurOffsets = function (height) {
  329. var lastOutputDimensions = {
  330. width: scene.getEngine().getRenderWidth() * (ratio / 4),
  331. height: scene.getEngine().getRenderHeight() * (ratio / 4)
  332. };
  333. for (var i = 0; i < 9; i++) {
  334. var value = (i - 4.0) * (1.0 / (height === true ? lastOutputDimensions.height : lastOutputDimensions.width));
  335. if (height) {
  336. blurOffsetsH[i] = value;
  337. }
  338. else {
  339. blurOffsetsW[i] = value;
  340. }
  341. }
  342. };
  343. var calculateWeights = function () {
  344. var x = 0.0;
  345. for (var i = 0; i < 9; i++) {
  346. x = (i - 4.0) / 4.0;
  347. blurWeights[i] = _this.gaussCoeff * (1.0 / Math.sqrt(2.0 * Math.PI * _this.gaussStandDev)) * Math.exp((-((x - _this.gaussMean) * (x - _this.gaussMean))) / (2.0 * _this.gaussStandDev * _this.gaussStandDev));
  348. }
  349. };
  350. // Callback
  351. var gaussianBlurCallback = function (height) {
  352. return function (effect) {
  353. if (_this._needUpdate) {
  354. calculateWeights();
  355. calculateBlurOffsets(height);
  356. }
  357. effect.setArray("blurOffsets", height ? blurOffsetsH : blurOffsetsW);
  358. effect.setArray("blurWeights", blurWeights);
  359. };
  360. };
  361. // Create horizontal gaussian blur post-processes
  362. this._guassianBlurHPostProcess = new BABYLON.PostProcess("hdr", "hdr", uniforms, [], ratio / 4, null, BABYLON.Texture.BILINEAR_SAMPLINGMODE, scene.getEngine(), false, "#define GAUSSIAN_BLUR_H");
  363. this._guassianBlurHPostProcess.onApply = gaussianBlurCallback(false);
  364. // Create vertical gaussian blur post-process
  365. this._guassianBlurVPostProcess = new BABYLON.PostProcess("hdr", "hdr", uniforms, [], ratio / 4, null, BABYLON.Texture.BILINEAR_SAMPLINGMODE, scene.getEngine(), false, "#define GAUSSIAN_BLUR_V");
  366. this._guassianBlurVPostProcess.onApply = gaussianBlurCallback(true);
  367. };
  368. // Luminance generator
  369. HDRRenderingPipeline.LUM_STEPS = 6;
  370. return HDRRenderingPipeline;
  371. })(BABYLON.PostProcessRenderPipeline);
  372. BABYLON.HDRRenderingPipeline = HDRRenderingPipeline;
  373. })(BABYLON || (BABYLON = {}));