PostProcessStageCollection.js 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827
  1. import arraySlice from '../Core/arraySlice.js';
  2. import Check from '../Core/Check.js';
  3. import defined from '../Core/defined.js';
  4. import defineProperties from '../Core/defineProperties.js';
  5. import destroyObject from '../Core/destroyObject.js';
  6. import DeveloperError from '../Core/DeveloperError.js';
  7. import PixelFormat from '../Core/PixelFormat.js';
  8. import PixelDatatype from '../Renderer/PixelDatatype.js';
  9. import Sampler from '../Renderer/Sampler.js';
  10. import Texture from '../Renderer/Texture.js';
  11. import TextureMagnificationFilter from '../Renderer/TextureMagnificationFilter.js';
  12. import TextureMinificationFilter from '../Renderer/TextureMinificationFilter.js';
  13. import TextureWrap from '../Renderer/TextureWrap.js';
  14. import PassThrough from '../Shaders/PostProcessStages/PassThrough.js';
  15. import PostProcessStageLibrary from './PostProcessStageLibrary.js';
  16. import PostProcessStageTextureCache from './PostProcessStageTextureCache.js';
  17. import Tonemapper from './Tonemapper.js';
  18. var stackScratch = [];
  19. /**
  20. * A collection of {@link PostProcessStage}s and/or {@link PostProcessStageComposite}s.
  21. * <p>
  22. * The input texture for each post-process stage is the texture rendered to by the scene or the texture rendered
  23. * to by the previous stage in the collection.
  24. * </p>
  25. * <p>
  26. * If the ambient occlusion or bloom stages are enabled, they will execute before all other stages.
  27. * </p>
  28. * <p>
  29. * If the FXAA stage is enabled, it will execute after all other stages.
  30. * </p>
  31. *
  32. * @alias PostProcessStageCollection
  33. * @constructor
  34. */
  35. function PostProcessStageCollection() {
  36. var fxaa = PostProcessStageLibrary.createFXAAStage();
  37. var ao = PostProcessStageLibrary.createAmbientOcclusionStage();
  38. var bloom = PostProcessStageLibrary.createBloomStage();
  39. // Auto-exposure is currently disabled because most shaders output a value in [0.0, 1.0].
  40. // Some shaders, such as the atmosphere and ground atmosphere, output values slightly over 1.0.
  41. this._autoExposureEnabled = false;
  42. this._autoExposure = PostProcessStageLibrary.createAutoExposureStage();
  43. this._tonemapping = undefined;
  44. this._tonemapper = undefined;
  45. // set tonemapper and tonemapping
  46. this.tonemapper = Tonemapper.ACES;
  47. var tonemapping = this._tonemapping;
  48. fxaa.enabled = false;
  49. ao.enabled = false;
  50. bloom.enabled = false;
  51. tonemapping.enabled = false; // will be enabled if necessary in update
  52. var textureCache = new PostProcessStageTextureCache(this);
  53. var stageNames = {};
  54. var stack = stackScratch;
  55. stack.push(fxaa, ao, bloom, tonemapping);
  56. while (stack.length > 0) {
  57. var stage = stack.pop();
  58. stageNames[stage.name] = stage;
  59. stage._textureCache = textureCache;
  60. var length = stage.length;
  61. if (defined(length)) {
  62. for (var i = 0; i < length; ++i) {
  63. stack.push(stage.get(i));
  64. }
  65. }
  66. }
  67. this._stages = [];
  68. this._activeStages = [];
  69. this._previousActiveStages = [];
  70. this._randomTexture = undefined; // For AO
  71. var that = this;
  72. ao.uniforms.randomTexture = function() {
  73. return that._randomTexture;
  74. };
  75. this._ao = ao;
  76. this._bloom = bloom;
  77. this._fxaa = fxaa;
  78. this._lastLength = undefined;
  79. this._aoEnabled = undefined;
  80. this._bloomEnabled = undefined;
  81. this._tonemappingEnabled = undefined;
  82. this._fxaaEnabled = undefined;
  83. this._stagesRemoved = false;
  84. this._textureCacheDirty = false;
  85. this._stageNames = stageNames;
  86. this._textureCache = textureCache;
  87. }
  88. defineProperties(PostProcessStageCollection.prototype, {
  89. /**
  90. * Determines if all of the post-process stages in the collection are ready to be executed.
  91. *
  92. * @memberof PostProcessStageCollection.prototype
  93. * @type {Boolean}
  94. * @readonly
  95. */
  96. ready : {
  97. get : function() {
  98. var readyAndEnabled = false;
  99. var stages = this._stages;
  100. var length = stages.length;
  101. for (var i = length - 1; i >= 0; --i) {
  102. var stage = stages[i];
  103. readyAndEnabled = readyAndEnabled || (stage.ready && stage.enabled);
  104. }
  105. var fxaa = this._fxaa;
  106. var ao = this._ao;
  107. var bloom = this._bloom;
  108. var tonemapping = this._tonemapping;
  109. readyAndEnabled = readyAndEnabled || (fxaa.ready && fxaa.enabled);
  110. readyAndEnabled = readyAndEnabled || (ao.ready && ao.enabled);
  111. readyAndEnabled = readyAndEnabled || (bloom.ready && bloom.enabled);
  112. readyAndEnabled = readyAndEnabled || (tonemapping.ready && tonemapping.enabled);
  113. return readyAndEnabled;
  114. }
  115. },
  116. /**
  117. * A post-process stage for Fast Approximate Anti-aliasing.
  118. * <p>
  119. * When enabled, this stage will execute after all others.
  120. * </p>
  121. *
  122. * @memberof PostProcessStageCollection.prototype
  123. * @type {PostProcessStage}
  124. * @readonly
  125. */
  126. fxaa : {
  127. get : function() {
  128. return this._fxaa;
  129. }
  130. },
  131. /**
  132. * A post-process stage that applies Horizon-based Ambient Occlusion (HBAO) to the input texture.
  133. * <p>
  134. * Ambient occlusion simulates shadows from ambient light. These shadows would always be present when the
  135. * surface receives light and regardless of the light's position.
  136. * </p>
  137. * <p>
  138. * The uniforms have the following properties: <code>intensity</code>, <code>bias</code>, <code>lengthCap</code>,
  139. * <code>stepSize</code>, <code>frustumLength</code>, <code>ambientOcclusionOnly</code>,
  140. * <code>delta</code>, <code>sigma</code>, and <code>blurStepSize</code>.
  141. * </p>
  142. * <ul>
  143. * <li><code>intensity</code> is a scalar value used to lighten or darken the shadows exponentially. Higher values make the shadows darker. The default value is <code>3.0</code>.</li>
  144. *
  145. * <li><code>bias</code> is a scalar value representing an angle in radians. If the dot product between the normal of the sample and the vector to the camera is less than this value,
  146. * sampling stops in the current direction. This is used to remove shadows from near planar edges. The default value is <code>0.1</code>.</li>
  147. *
  148. * <li><code>lengthCap</code> is a scalar value representing a length in meters. If the distance from the current sample to first sample is greater than this value,
  149. * sampling stops in the current direction. The default value is <code>0.26</code>.</li>
  150. *
  151. * <li><code>stepSize</code> is a scalar value indicating the distance to the next texel sample in the current direction. The default value is <code>1.95</code>.</li>
  152. *
  153. * <li><code>frustumLength</code> is a scalar value in meters. If the current fragment has a distance from the camera greater than this value, ambient occlusion is not computed for the fragment.
  154. * The default value is <code>1000.0</code>.</li>
  155. *
  156. * <li><code>ambientOcclusionOnly</code> is a boolean value. When <code>true</code>, only the shadows generated are written to the output. When <code>false</code>, the input texture is modulated
  157. * with the ambient occlusion. This is a useful debug option for seeing the effects of changing the uniform values. The default value is <code>false</code>.</li>
  158. * </ul>
  159. * <p>
  160. * <code>delta</code>, <code>sigma</code>, and <code>blurStepSize</code> are the same properties as {@link PostProcessStageLibrary#createBlurStage}.
  161. * The blur is applied to the shadows generated from the image to make them smoother.
  162. * </p>
  163. * <p>
  164. * When enabled, this stage will execute before all others.
  165. * </p>
  166. *
  167. * @memberof PostProcessStageCollection.prototype
  168. * @type {PostProcessStageComposite}
  169. * @readonly
  170. */
  171. ambientOcclusion : {
  172. get : function() {
  173. return this._ao;
  174. }
  175. },
  176. /**
  177. * A post-process stage for a bloom effect.
  178. * <p>
  179. * A bloom effect adds glow effect, makes bright areas brighter, and dark areas darker.
  180. * </p>
  181. * <p>
  182. * This stage has the following uniforms: <code>contrast</code>, <code>brightness</code>, <code>glowOnly</code>,
  183. * <code>delta</code>, <code>sigma</code>, and <code>stepSize</code>.
  184. * </p>
  185. * <ul>
  186. * <li><code>contrast</code> is a scalar value in the range [-255.0, 255.0] and affects the contract of the effect. The default value is <code>128.0</code>.</li>
  187. *
  188. * <li><code>brightness</code> is a scalar value. The input texture RGB value is converted to hue, saturation, and brightness (HSB) then this value is
  189. * added to the brightness. The default value is <code>-0.3</code>.</li>
  190. *
  191. * <li><code>glowOnly</code> is a boolean value. When <code>true</code>, only the glow effect will be shown. When <code>false</code>, the glow will be added to the input texture.
  192. * The default value is <code>false</code>. This is a debug option for viewing the effects when changing the other uniform values.</li>
  193. * </ul>
  194. * <p>
  195. * <code>delta</code>, <code>sigma</code>, and <code>stepSize</code> are the same properties as {@link PostProcessStageLibrary#createBlurStage}.
  196. * The blur is applied to the shadows generated from the image to make them smoother.
  197. * </p>
  198. * <p>
  199. * When enabled, this stage will execute before all others.
  200. * </p>
  201. *
  202. * @memberOf PostProcessStageCollection.prototype
  203. * @type {PostProcessStageComposite}
  204. * @readonly
  205. */
  206. bloom : {
  207. get : function() {
  208. return this._bloom;
  209. }
  210. },
  211. /**
  212. * The number of post-process stages in this collection.
  213. *
  214. * @memberof PostProcessStageCollection.prototype
  215. * @type {Number}
  216. * @readonly
  217. */
  218. length : {
  219. get : function() {
  220. removeStages(this);
  221. return this._stages.length;
  222. }
  223. },
  224. /**
  225. * A reference to the last texture written to when executing the post-process stages in this collection.
  226. *
  227. * @memberof PostProcessStageCollection.prototype
  228. * @type {Texture}
  229. * @readonly
  230. * @private
  231. */
  232. outputTexture : {
  233. get : function() {
  234. var fxaa = this._fxaa;
  235. if (fxaa.enabled && fxaa.ready) {
  236. return this.getOutputTexture(fxaa.name);
  237. }
  238. var stages = this._stages;
  239. var length = stages.length;
  240. for (var i = length - 1; i >= 0; --i) {
  241. var stage = stages[i];
  242. if (defined(stage) && stage.ready && stage.enabled) {
  243. return this.getOutputTexture(stage.name);
  244. }
  245. }
  246. var tonemapping = this._tonemapping;
  247. if (tonemapping.enabled && tonemapping.ready) {
  248. return this.getOutputTexture(tonemapping.name);
  249. }
  250. var bloom = this._bloom;
  251. if (bloom.enabled && bloom.ready) {
  252. return this.getOutputTexture(bloom.name);
  253. }
  254. var ao = this._ao;
  255. if (ao.enabled && ao.ready) {
  256. return this.getOutputTexture(ao.name);
  257. }
  258. return undefined;
  259. }
  260. },
  261. /**
  262. * Whether the collection has a stage that has selected features.
  263. *
  264. * @memberof PostProcessStageCollection.prototype
  265. * @type {Boolean}
  266. * @readonly
  267. * @private
  268. */
  269. hasSelected : {
  270. get : function() {
  271. var stages = arraySlice(this._stages);
  272. while (stages.length > 0) {
  273. var stage = stages.pop();
  274. if (!defined(stage)) {
  275. continue;
  276. }
  277. if (defined(stage.selected)) {
  278. return true;
  279. }
  280. var length = stage.length;
  281. if (defined(length)) {
  282. for (var i = 0; i < length; ++i) {
  283. stages.push(stage.get(i));
  284. }
  285. }
  286. }
  287. return false;
  288. }
  289. },
  290. /**
  291. * Gets and sets the tonemapping algorithm used when rendering with high dynamic range.
  292. *
  293. * @memberof PostProcessStageCollection.prototype
  294. * @type {Tonemapper}
  295. * @private
  296. */
  297. tonemapper : {
  298. get : function() {
  299. return this._tonemapper;
  300. },
  301. set : function(value) {
  302. if (this._tonemapper === value) {
  303. return;
  304. }
  305. //>>includeStart('debug', pragmas.debug);
  306. if (!Tonemapper.validate(value)) {
  307. throw new DeveloperError('tonemapper was set to an invalid value.');
  308. }
  309. //>>includeEnd('debug');
  310. if (defined(this._tonemapping)) {
  311. delete this._stageNames[this._tonemapping.name];
  312. this._tonemapping.destroy();
  313. }
  314. var useAutoExposure = this._autoExposureEnabled;
  315. var tonemapper;
  316. switch(value) {
  317. case Tonemapper.REINHARD:
  318. tonemapper = PostProcessStageLibrary.createReinhardTonemappingStage(useAutoExposure);
  319. break;
  320. case Tonemapper.MODIFIED_REINHARD:
  321. tonemapper = PostProcessStageLibrary.createModifiedReinhardTonemappingStage(useAutoExposure);
  322. break;
  323. case Tonemapper.FILMIC:
  324. tonemapper = PostProcessStageLibrary.createFilmicTonemappingStage(useAutoExposure);
  325. break;
  326. default:
  327. tonemapper = PostProcessStageLibrary.createAcesTonemappingStage(useAutoExposure);
  328. break;
  329. }
  330. if (useAutoExposure) {
  331. var autoexposure = this._autoExposure;
  332. tonemapper.uniforms.autoExposure = function() {
  333. return autoexposure.outputTexture;
  334. };
  335. }
  336. this._tonemapper = value;
  337. this._tonemapping = tonemapper;
  338. if (defined(this._stageNames)) {
  339. this._stageNames[tonemapper.name] = tonemapper;
  340. tonemapper._textureCache = this._textureCache;
  341. }
  342. this._textureCacheDirty = true;
  343. }
  344. }
  345. });
  346. function removeStages(collection) {
  347. if (!collection._stagesRemoved) {
  348. return;
  349. }
  350. collection._stagesRemoved = false;
  351. var newStages = [];
  352. var stages = collection._stages;
  353. var length = stages.length;
  354. for (var i = 0, j = 0; i < length; ++i) {
  355. var stage = stages[i];
  356. if (stage) {
  357. stage._index = j++;
  358. newStages.push(stage);
  359. }
  360. }
  361. collection._stages = newStages;
  362. }
  363. /**
  364. * Adds the post-process stage to the collection.
  365. *
  366. * @param {PostProcessStage|PostProcessStageComposite} stage The post-process stage to add to the collection.
  367. * @return {PostProcessStage|PostProcessStageComposite} The post-process stage that was added to the collection.
  368. *
  369. * @exception {DeveloperError} The post-process stage has already been added to the collection or does not have a unique name.
  370. */
  371. PostProcessStageCollection.prototype.add = function(stage) {
  372. //>>includeStart('debug', pragmas.debug);
  373. Check.typeOf.object('stage', stage);
  374. //>>includeEnd('debug');
  375. var stageNames = this._stageNames;
  376. var stack = stackScratch;
  377. stack.push(stage);
  378. while (stack.length > 0) {
  379. var currentStage = stack.pop();
  380. //>>includeStart('debug', pragmas.debug);
  381. if (defined(stageNames[currentStage.name])) {
  382. throw new DeveloperError(currentStage.name + ' has already been added to the collection or does not have a unique name.');
  383. }
  384. //>>includeEnd('debug');
  385. stageNames[currentStage.name] = currentStage;
  386. currentStage._textureCache = this._textureCache;
  387. var length = currentStage.length;
  388. if (defined(length)) {
  389. for (var i = 0; i < length; ++i) {
  390. stack.push(currentStage.get(i));
  391. }
  392. }
  393. }
  394. var stages = this._stages;
  395. stage._index = stages.length;
  396. stages.push(stage);
  397. this._textureCacheDirty = true;
  398. return stage;
  399. };
  400. /**
  401. * Removes a post-process stage from the collection and destroys it.
  402. *
  403. * @param {PostProcessStage|PostProcessStageComposite} stage The post-process stage to remove from the collection.
  404. * @return {Boolean} Whether the post-process stage was removed.
  405. */
  406. PostProcessStageCollection.prototype.remove = function(stage) {
  407. if (!this.contains(stage)) {
  408. return false;
  409. }
  410. var stageNames = this._stageNames;
  411. var stack = stackScratch;
  412. stack.push(stage);
  413. while (stack.length > 0) {
  414. var currentStage = stack.pop();
  415. delete stageNames[currentStage.name];
  416. var length = currentStage.length;
  417. if (defined(length)) {
  418. for (var i = 0; i < length; ++i) {
  419. stack.push(currentStage.get(i));
  420. }
  421. }
  422. }
  423. this._stages[stage._index] = undefined;
  424. this._stagesRemoved = true;
  425. this._textureCacheDirty = true;
  426. stage._index = undefined;
  427. stage._textureCache = undefined;
  428. stage.destroy();
  429. return true;
  430. };
  431. /**
  432. * Returns whether the collection contains a post-process stage.
  433. *
  434. * @param {PostProcessStage|PostProcessStageComposite} stage The post-process stage.
  435. * @return {Boolean} Whether the collection contains the post-process stage.
  436. */
  437. PostProcessStageCollection.prototype.contains = function(stage) {
  438. return defined(stage) && defined(stage._index) && stage._textureCache === this._textureCache;
  439. };
  440. /**
  441. * Gets the post-process stage at <code>index</code>.
  442. *
  443. * @param {Number} index The index of the post-process stage.
  444. * @return {PostProcessStage|PostProcessStageComposite} The post-process stage at index.
  445. */
  446. PostProcessStageCollection.prototype.get = function(index) {
  447. removeStages(this);
  448. var stages = this._stages;
  449. //>>includeStart('debug', pragmas.debug);
  450. var length = stages.length;
  451. Check.typeOf.number.greaterThanOrEquals('stages length', length, 0);
  452. Check.typeOf.number.greaterThanOrEquals('index', index, 0);
  453. Check.typeOf.number.lessThan('index', index, length);
  454. //>>includeEnd('debug');
  455. return stages[index];
  456. };
  457. /**
  458. * Removes all post-process stages from the collection and destroys them.
  459. */
  460. PostProcessStageCollection.prototype.removeAll = function() {
  461. var stages = this._stages;
  462. var length = stages.length;
  463. for (var i = 0; i < length; ++i) {
  464. this.remove(stages[i]);
  465. }
  466. stages.length = 0;
  467. };
  468. /**
  469. * Gets a post-process stage in the collection by its name.
  470. *
  471. * @param {String} name The name of the post-process stage.
  472. * @return {PostProcessStage|PostProcessStageComposite} The post-process stage.
  473. *
  474. * @private
  475. */
  476. PostProcessStageCollection.prototype.getStageByName = function(name) {
  477. return this._stageNames[name];
  478. };
  479. /**
  480. * Called before the post-process stages in the collection are executed. Calls update for each stage and creates WebGL resources.
  481. *
  482. * @param {Context} context The context.
  483. * @param {Boolean} useLogDepth Whether the scene uses a logarithmic depth buffer.
  484. *
  485. * @private
  486. */
  487. PostProcessStageCollection.prototype.update = function(context, useLogDepth, useHdr) {
  488. removeStages(this);
  489. var previousActiveStages = this._activeStages;
  490. var activeStages = this._activeStages = this._previousActiveStages;
  491. this._previousActiveStages = previousActiveStages;
  492. var stages = this._stages;
  493. var length = activeStages.length = stages.length;
  494. var i;
  495. var stage;
  496. var count = 0;
  497. for (i = 0; i < length; ++i) {
  498. stage = stages[i];
  499. if (stage.ready && stage.enabled && stage._isSupported(context)) {
  500. activeStages[count++] = stage;
  501. }
  502. }
  503. activeStages.length = count;
  504. var activeStagesChanged = count !== previousActiveStages.length;
  505. if (!activeStagesChanged) {
  506. for (i = 0; i < count; ++i) {
  507. if (activeStages[i] !== previousActiveStages[i]) {
  508. activeStagesChanged = true;
  509. break;
  510. }
  511. }
  512. }
  513. var ao = this._ao;
  514. var bloom = this._bloom;
  515. var autoexposure = this._autoExposure;
  516. var tonemapping = this._tonemapping;
  517. var fxaa = this._fxaa;
  518. tonemapping.enabled = useHdr;
  519. var aoEnabled = ao.enabled && ao._isSupported(context);
  520. var bloomEnabled = bloom.enabled && bloom._isSupported(context);
  521. var tonemappingEnabled = tonemapping.enabled && tonemapping._isSupported(context);
  522. var fxaaEnabled = fxaa.enabled && fxaa._isSupported(context);
  523. if (activeStagesChanged || this._textureCacheDirty || count !== this._lastLength || aoEnabled !== this._aoEnabled ||
  524. bloomEnabled !== this._bloomEnabled || tonemappingEnabled !== this._tonemappingEnabled || fxaaEnabled !== this._fxaaEnabled) {
  525. // The number of stages to execute has changed.
  526. // Update dependencies and recreate framebuffers.
  527. this._textureCache.updateDependencies();
  528. this._lastLength = count;
  529. this._aoEnabled = aoEnabled;
  530. this._bloomEnabled = bloomEnabled;
  531. this._tonemappingEnabled = tonemappingEnabled;
  532. this._fxaaEnabled = fxaaEnabled;
  533. this._textureCacheDirty = false;
  534. }
  535. if (defined(this._randomTexture) && !aoEnabled) {
  536. this._randomTexture.destroy();
  537. this._randomTexture = undefined;
  538. }
  539. if (!defined(this._randomTexture) && aoEnabled) {
  540. length = 256 * 256 * 3;
  541. var random = new Uint8Array(length);
  542. for (i = 0; i < length; i += 3) {
  543. random[i] = Math.floor(Math.random() * 255.0);
  544. }
  545. this._randomTexture = new Texture({
  546. context : context,
  547. pixelFormat : PixelFormat.RGB,
  548. pixelDatatype : PixelDatatype.UNSIGNED_BYTE,
  549. source : {
  550. arrayBufferView : random,
  551. width : 256,
  552. height : 256
  553. },
  554. sampler : new Sampler({
  555. wrapS : TextureWrap.REPEAT,
  556. wrapT : TextureWrap.REPEAT,
  557. minificationFilter : TextureMinificationFilter.NEAREST,
  558. magnificationFilter : TextureMagnificationFilter.NEAREST
  559. })
  560. });
  561. }
  562. this._textureCache.update(context);
  563. fxaa.update(context, useLogDepth);
  564. ao.update(context, useLogDepth);
  565. bloom.update(context, useLogDepth);
  566. tonemapping.update(context, useLogDepth);
  567. if (this._autoExposureEnabled) {
  568. autoexposure.update(context, useLogDepth);
  569. }
  570. length = stages.length;
  571. for (i = 0; i < length; ++i) {
  572. stages[i].update(context, useLogDepth);
  573. }
  574. };
  575. /**
  576. * Clears all of the framebuffers used by the stages.
  577. *
  578. * @param {Context} context The context.
  579. *
  580. * @private
  581. */
  582. PostProcessStageCollection.prototype.clear = function(context) {
  583. this._textureCache.clear(context);
  584. if (this._autoExposureEnabled) {
  585. this._autoExposure.clear(context);
  586. }
  587. };
  588. function getOutputTexture(stage) {
  589. while (defined(stage.length)) {
  590. stage = stage.get(stage.length - 1);
  591. }
  592. return stage.outputTexture;
  593. }
  594. /**
  595. * Gets the output texture of a stage with the given name.
  596. *
  597. * @param {String} stageName The name of the stage.
  598. * @return {Texture|undefined} The texture rendered to by the stage with the given name.
  599. *
  600. * @private
  601. */
  602. PostProcessStageCollection.prototype.getOutputTexture = function(stageName) {
  603. var stage = this.getStageByName(stageName);
  604. if (!defined(stage)) {
  605. return undefined;
  606. }
  607. return getOutputTexture(stage);
  608. };
  609. function execute(stage, context, colorTexture, depthTexture, idTexture) {
  610. if (defined(stage.execute)) {
  611. stage.execute(context, colorTexture, depthTexture, idTexture);
  612. return;
  613. }
  614. var length = stage.length;
  615. var i;
  616. if (stage.inputPreviousStageTexture) {
  617. execute(stage.get(0), context, colorTexture, depthTexture, idTexture);
  618. for (i = 1; i < length; ++i) {
  619. execute(stage.get(i), context, getOutputTexture(stage.get(i - 1)), depthTexture, idTexture);
  620. }
  621. } else {
  622. for (i = 0; i < length; ++i) {
  623. execute(stage.get(i), context, colorTexture, depthTexture, idTexture);
  624. }
  625. }
  626. }
  627. /**
  628. * Executes all ready and enabled stages in the collection.
  629. *
  630. * @param {Context} context The context.
  631. * @param {Texture} colorTexture The color texture rendered to by the scene.
  632. * @param {Texture} depthTexture The depth texture written to by the scene.
  633. * @param {Texture} idTexture The id texture written to by the scene.
  634. *
  635. * @private
  636. */
  637. PostProcessStageCollection.prototype.execute = function(context, colorTexture, depthTexture, idTexture) {
  638. var activeStages = this._activeStages;
  639. var length = activeStages.length;
  640. var fxaa = this._fxaa;
  641. var ao = this._ao;
  642. var bloom = this._bloom;
  643. var autoexposure = this._autoExposure;
  644. var tonemapping = this._tonemapping;
  645. var aoEnabled = ao.enabled && ao._isSupported(context);
  646. var bloomEnabled = bloom.enabled && bloom._isSupported(context);
  647. var autoExposureEnabled = this._autoExposureEnabled;
  648. var tonemappingEnabled = tonemapping.enabled && tonemapping._isSupported(context);
  649. var fxaaEnabled = fxaa.enabled && fxaa._isSupported(context);
  650. if (!fxaaEnabled && !aoEnabled && !bloomEnabled && !tonemappingEnabled && length === 0) {
  651. return;
  652. }
  653. var initialTexture = colorTexture;
  654. if (aoEnabled && ao.ready) {
  655. execute(ao, context, initialTexture, depthTexture, idTexture);
  656. initialTexture = getOutputTexture(ao);
  657. }
  658. if (bloomEnabled && bloom.ready) {
  659. execute(bloom, context, initialTexture, depthTexture, idTexture);
  660. initialTexture = getOutputTexture(bloom);
  661. }
  662. if (autoExposureEnabled && autoexposure.ready) {
  663. execute(autoexposure, context, initialTexture, depthTexture, idTexture);
  664. }
  665. if (tonemappingEnabled && tonemapping.ready) {
  666. execute(tonemapping, context, initialTexture, depthTexture, idTexture);
  667. initialTexture = getOutputTexture(tonemapping);
  668. }
  669. var lastTexture = initialTexture;
  670. if (length > 0) {
  671. execute(activeStages[0], context, initialTexture, depthTexture, idTexture);
  672. for (var i = 1; i < length; ++i) {
  673. execute(activeStages[i], context, getOutputTexture(activeStages[i - 1]), depthTexture, idTexture);
  674. }
  675. lastTexture = getOutputTexture(activeStages[length - 1]);
  676. }
  677. if (fxaaEnabled && fxaa.ready) {
  678. execute(fxaa, context, lastTexture, depthTexture, idTexture);
  679. }
  680. };
  681. /**
  682. * Copies the output of all executed stages to the color texture of a framebuffer.
  683. *
  684. * @param {Context} context The context.
  685. * @param {Framebuffer} framebuffer The framebuffer to copy to.
  686. *
  687. * @private
  688. */
  689. PostProcessStageCollection.prototype.copy = function(context, framebuffer) {
  690. if (!defined(this._copyColorCommand)) {
  691. var that = this;
  692. this._copyColorCommand = context.createViewportQuadCommand(PassThrough, {
  693. uniformMap : {
  694. colorTexture : function() {
  695. return that.outputTexture;
  696. }
  697. },
  698. owner : this
  699. });
  700. }
  701. this._copyColorCommand.framebuffer = framebuffer;
  702. this._copyColorCommand.execute(context);
  703. };
  704. /**
  705. * Returns true if this object was destroyed; otherwise, false.
  706. * <p>
  707. * If this object was destroyed, it should not be used; calling any function other than
  708. * <code>isDestroyed</code> will result in a {@link DeveloperError} exception.
  709. * </p>
  710. *
  711. * @returns {Boolean} <code>true</code> if this object was destroyed; otherwise, <code>false</code>.
  712. *
  713. * @see PostProcessStageCollection#destroy
  714. */
  715. PostProcessStageCollection.prototype.isDestroyed = function() {
  716. return false;
  717. };
  718. /**
  719. * Destroys the WebGL resources held by this object. Destroying an object allows for deterministic
  720. * release of WebGL resources, instead of relying on the garbage collector to destroy this object.
  721. * <p>
  722. * Once an object is destroyed, it should not be used; calling any function other than
  723. * <code>isDestroyed</code> will result in a {@link DeveloperError} exception. Therefore,
  724. * assign the return value (<code>undefined</code>) to the object as done in the example.
  725. * </p>
  726. *
  727. * @exception {DeveloperError} This object was destroyed, i.e., destroy() was called.
  728. *
  729. * @see PostProcessStageCollection#isDestroyed
  730. */
  731. PostProcessStageCollection.prototype.destroy = function() {
  732. this._fxaa.destroy();
  733. this._ao.destroy();
  734. this._bloom.destroy();
  735. this._autoExposure.destroy();
  736. this._tonemapping.destroy();
  737. this.removeAll();
  738. this._textureCache = this._textureCache && this._textureCache.destroy();
  739. return destroyObject(this);
  740. };
  741. export default PostProcessStageCollection;