PostProcessStageComposite.js 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347
  1. import Check from '../Core/Check.js';
  2. import createGuid from '../Core/createGuid.js';
  3. import defaultValue from '../Core/defaultValue.js';
  4. import defined from '../Core/defined.js';
  5. import defineProperties from '../Core/defineProperties.js';
  6. import destroyObject from '../Core/destroyObject.js';
  7. /**
  8. * A collection of {@link PostProcessStage}s or other post-process composite stages that execute together logically.
  9. * <p>
  10. * All stages are executed in the order of the array. The input texture changes based on the value of <code>inputPreviousStageTexture</code>.
  11. * If <code>inputPreviousStageTexture</code> is <code>true</code>, the input to each stage is the output texture rendered to by the scene or of the stage that executed before it.
  12. * If <code>inputPreviousStageTexture</code> is <code>false</code>, the input texture is the same for each stage in the composite. The input texture is the texture rendered to by the scene
  13. * or the output texture of the previous stage.
  14. * </p>
  15. *
  16. * @alias PostProcessStageComposite
  17. * @constructor
  18. *
  19. * @param {Object} options An object with the following properties:
  20. * @param {Array} options.stages An array of {@link PostProcessStage}s or composites to be executed in order.
  21. * @param {Boolean} [options.inputPreviousStageTexture=true] Whether to execute each post-process stage where the input to one stage is the output of the previous. Otherwise, the input to each contained stage is the output of the stage that executed before the composite.
  22. * @param {String} [options.name=createGuid()] The unique name of this post-process stage for reference by other composites. If a name is not supplied, a GUID will be generated.
  23. * @param {Object} [options.uniforms] An alias to the uniforms of post-process stages.
  24. *
  25. * @exception {DeveloperError} options.stages.length must be greater than 0.0.
  26. *
  27. * @see PostProcessStage
  28. *
  29. * @example
  30. * // Example 1: separable blur filter
  31. * // The input to blurXDirection is the texture rendered to by the scene or the output of the previous stage.
  32. * // The input to blurYDirection is the texture rendered to by blurXDirection.
  33. * scene.postProcessStages.add(new Cesium.PostProcessStageComposite({
  34. * stages : [blurXDirection, blurYDirection]
  35. * }));
  36. *
  37. * @example
  38. * // Example 2: referencing the output of another post-process stage
  39. * scene.postProcessStages.add(new Cesium.PostProcessStageComposite({
  40. * inputPreviousStageTexture : false,
  41. * stages : [
  42. * // The same as Example 1.
  43. * new Cesium.PostProcessStageComposite({
  44. * inputPreviousStageTexture : true
  45. * stages : [blurXDirection, blurYDirection],
  46. * name : 'blur'
  47. * }),
  48. * // The input texture for this stage is the same input texture to blurXDirection since inputPreviousStageTexture is false
  49. * new Cesium.PostProcessStage({
  50. * fragmentShader : compositeShader,
  51. * uniforms : {
  52. * blurTexture : 'blur' // The output of the composite with name 'blur' (the texture that blurYDirection rendered to).
  53. * }
  54. * })
  55. * ]
  56. * });
  57. *
  58. * @example
  59. * // Example 3: create a uniform alias
  60. * var uniforms = {};
  61. * Cesium.defineProperties(uniforms, {
  62. * filterSize : {
  63. * get : function() {
  64. * return blurXDirection.uniforms.filterSize;
  65. * },
  66. * set : function(value) {
  67. * blurXDirection.uniforms.filterSize = blurYDirection.uniforms.filterSize = value;
  68. * }
  69. * }
  70. * });
  71. * scene.postProcessStages.add(new Cesium.PostProcessStageComposite({
  72. * stages : [blurXDirection, blurYDirection],
  73. * uniforms : uniforms
  74. * }));
  75. */
  76. function PostProcessStageComposite(options) {
  77. options = defaultValue(options, defaultValue.EMPTY_OBJECT);
  78. //>>includeStart('debug', pragmas.debug);
  79. Check.defined('options.stages', options.stages);
  80. Check.typeOf.number.greaterThan('options.stages.length', options.stages.length, 0);
  81. //>>includeEnd('debug');
  82. this._stages = options.stages;
  83. this._inputPreviousStageTexture = defaultValue(options.inputPreviousStageTexture, true);
  84. var name = options.name;
  85. if (!defined(name)) {
  86. name = createGuid();
  87. }
  88. this._name = name;
  89. this._uniforms = options.uniforms;
  90. // used by PostProcessStageCollection
  91. this._textureCache = undefined;
  92. this._index = undefined;
  93. this._selected = undefined;
  94. this._selectedShadow = undefined;
  95. this._parentSelected = undefined;
  96. this._parentSelectedShadow = undefined;
  97. this._combinedSelected = undefined;
  98. this._combinedSelectedShadow = undefined;
  99. this._selectedLength = 0;
  100. this._parentSelectedLength = 0;
  101. this._selectedDirty = true;
  102. }
  103. defineProperties(PostProcessStageComposite.prototype, {
  104. /**
  105. * Determines if this post-process stage is ready to be executed.
  106. *
  107. * @memberof PostProcessStageComposite.prototype
  108. * @type {Boolean}
  109. * @readonly
  110. */
  111. ready : {
  112. get : function() {
  113. var stages = this._stages;
  114. var length = stages.length;
  115. for (var i = 0; i < length; ++i) {
  116. if (!stages[i].ready) {
  117. return false;
  118. }
  119. }
  120. return true;
  121. }
  122. },
  123. /**
  124. * The unique name of this post-process stage for reference by other stages in a PostProcessStageComposite.
  125. *
  126. * @memberof PostProcessStageComposite.prototype
  127. * @type {String}
  128. * @readonly
  129. */
  130. name : {
  131. get : function() {
  132. return this._name;
  133. }
  134. },
  135. /**
  136. * Whether or not to execute this post-process stage when ready.
  137. *
  138. * @memberof PostProcessStageComposite.prototype
  139. * @type {Boolean}
  140. */
  141. enabled : {
  142. get : function() {
  143. return this._stages[0].enabled;
  144. },
  145. set : function(value) {
  146. var stages = this._stages;
  147. var length = stages.length;
  148. for (var i = 0; i < length; ++i) {
  149. stages[i].enabled = value;
  150. }
  151. }
  152. },
  153. /**
  154. * An alias to the uniform values of the post-process stages. May be <code>undefined</code>; in which case, get each stage to set uniform values.
  155. * @memberof PostProcessStageComposite.prototype
  156. * @type {Object}
  157. */
  158. uniforms : {
  159. get : function() {
  160. return this._uniforms;
  161. }
  162. },
  163. /**
  164. * All post-process stages are executed in the order of the array. The input texture changes based on the value of <code>inputPreviousStageTexture</code>.
  165. * If <code>inputPreviousStageTexture</code> is <code>true</code>, the input to each stage is the output texture rendered to by the scene or of the stage that executed before it.
  166. * If <code>inputPreviousStageTexture</code> is <code>false</code>, the input texture is the same for each stage in the composite. The input texture is the texture rendered to by the scene
  167. * or the output texture of the previous stage.
  168. *
  169. * @memberof PostProcessStageComposite.prototype
  170. * @type {Boolean}
  171. * @readonly
  172. */
  173. inputPreviousStageTexture : {
  174. get : function() {
  175. return this._inputPreviousStageTexture;
  176. }
  177. },
  178. /**
  179. * The number of post-process stages in this composite.
  180. *
  181. * @memberof PostProcessStageComposite.prototype
  182. * @type {Number}
  183. * @readonly
  184. */
  185. length : {
  186. get : function() {
  187. return this._stages.length;
  188. }
  189. },
  190. /**
  191. * The features selected for applying the post-process.
  192. *
  193. * @memberof PostProcessStageComposite.prototype
  194. * @type {Array}
  195. */
  196. selected : {
  197. get : function() {
  198. return this._selected;
  199. },
  200. set : function(value) {
  201. this._selected = value;
  202. }
  203. },
  204. /**
  205. * @private
  206. */
  207. parentSelected : {
  208. get : function() {
  209. return this._parentSelected;
  210. },
  211. set : function(value) {
  212. this._parentSelected = value;
  213. }
  214. }
  215. });
  216. /**
  217. * @private
  218. */
  219. PostProcessStageComposite.prototype._isSupported = function(context) {
  220. var stages = this._stages;
  221. var length = stages.length;
  222. for (var i = 0; i < length; ++i) {
  223. if (!stages[i]._isSupported(context)) {
  224. return false;
  225. }
  226. }
  227. return true;
  228. };
  229. /**
  230. * Gets the post-process stage at <code>index</code>
  231. *
  232. * @param {Number} index The index of the post-process stage or composite.
  233. * @return {PostProcessStage|PostProcessStageComposite} The post-process stage or composite at index.
  234. *
  235. * @exception {DeveloperError} index must be greater than or equal to 0.
  236. * @exception {DeveloperError} index must be less than {@link PostProcessStageComposite#length}.
  237. */
  238. PostProcessStageComposite.prototype.get = function(index) {
  239. //>>includeStart('debug', pragmas.debug);
  240. Check.typeOf.number.greaterThanOrEquals('index', index, 0);
  241. Check.typeOf.number.lessThan('index', index, this.length);
  242. //>>includeEnd('debug');
  243. return this._stages[index];
  244. };
  245. function isSelectedTextureDirty(stage) {
  246. var length = defined(stage._selected) ? stage._selected.length : 0;
  247. var parentLength = defined(stage._parentSelected) ? stage._parentSelected : 0;
  248. var dirty = stage._selected !== stage._selectedShadow || length !== stage._selectedLength;
  249. dirty = dirty || stage._parentSelected !== stage._parentSelectedShadow || parentLength !== stage._parentSelectedLength;
  250. if (defined(stage._selected) && defined(stage._parentSelected)) {
  251. stage._combinedSelected = stage._selected.concat(stage._parentSelected);
  252. } else if (defined(stage._parentSelected)) {
  253. stage._combinedSelected = stage._parentSelected;
  254. } else {
  255. stage._combinedSelected = stage._selected;
  256. }
  257. if (!dirty && defined(stage._combinedSelected)) {
  258. if (!defined(stage._combinedSelectedShadow)) {
  259. return true;
  260. }
  261. length = stage._combinedSelected.length;
  262. for (var i = 0; i < length; ++i) {
  263. if (stage._combinedSelected[i] !== stage._combinedSelectedShadow[i]) {
  264. return true;
  265. }
  266. }
  267. }
  268. return dirty;
  269. }
  270. /**
  271. * A function that will be called before execute. Updates each post-process stage in the composite.
  272. * @param {Context} context The context.
  273. * @param {Boolean} useLogDepth Whether the scene uses a logarithmic depth buffer.
  274. * @private
  275. */
  276. PostProcessStageComposite.prototype.update = function(context, useLogDepth) {
  277. this._selectedDirty = isSelectedTextureDirty(this);
  278. this._selectedShadow = this._selected;
  279. this._parentSelectedShadow = this._parentSelected;
  280. this._combinedSelectedShadow = this._combinedSelected;
  281. this._selectedLength = defined(this._selected) ? this._selected.length : 0;
  282. this._parentSelectedLength = defined(this._parentSelected) ? this._parentSelected.length : 0;
  283. var stages = this._stages;
  284. var length = stages.length;
  285. for (var i = 0; i < length; ++i) {
  286. var stage = stages[i];
  287. if (this._selectedDirty) {
  288. stage.parentSelected = this._combinedSelected;
  289. }
  290. stage.update(context, useLogDepth);
  291. }
  292. };
  293. /**
  294. * Returns true if this object was destroyed; otherwise, false.
  295. * <p>
  296. * If this object was destroyed, it should not be used; calling any function other than
  297. * <code>isDestroyed</code> will result in a {@link DeveloperError} exception.
  298. * </p>
  299. *
  300. * @returns {Boolean} <code>true</code> if this object was destroyed; otherwise, <code>false</code>.
  301. *
  302. * @see PostProcessStageComposite#destroy
  303. */
  304. PostProcessStageComposite.prototype.isDestroyed = function() {
  305. return false;
  306. };
  307. /**
  308. * Destroys the WebGL resources held by this object. Destroying an object allows for deterministic
  309. * release of WebGL resources, instead of relying on the garbage collector to destroy this object.
  310. * <p>
  311. * Once an object is destroyed, it should not be used; calling any function other than
  312. * <code>isDestroyed</code> will result in a {@link DeveloperError} exception. Therefore,
  313. * assign the return value (<code>undefined</code>) to the object as done in the example.
  314. * </p>
  315. *
  316. * @exception {DeveloperError} This object was destroyed, i.e., destroy() was called.
  317. *
  318. * @see PostProcessStageComposite#isDestroyed
  319. */
  320. PostProcessStageComposite.prototype.destroy = function() {
  321. var stages = this._stages;
  322. var length = stages.length;
  323. for (var i = 0; i < length; ++i) {
  324. stages[i].destroy();
  325. }
  326. return destroyObject(this);
  327. };
  328. export default PostProcessStageComposite;