effectRenderer.ts 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275
  1. import { Nullable } from '../types';
  2. import { Texture } from '../Materials/Textures/texture';
  3. import { Engine } from '../Engines/engine';
  4. import { VertexBuffer } from '../Meshes/buffer';
  5. import { Viewport } from '../Maths/math.viewport';
  6. import { Constants } from '../Engines/constants';
  7. import { Observable } from '../Misc/observable';
  8. import { Effect } from './effect';
  9. import { DataBuffer } from '../Meshes/dataBuffer';
  10. // Prevents ES6 Crash if not imported.
  11. import "../Shaders/postprocess.vertex";
  12. /**
  13. * Effect Render Options
  14. */
  15. export interface IEffectRendererOptions {
  16. /**
  17. * Defines the vertices positions.
  18. */
  19. positions?: number[];
  20. /**
  21. * Defines the indices.
  22. */
  23. indices?: number[];
  24. }
  25. /**
  26. * Helper class to render one or more effects
  27. */
  28. export class EffectRenderer {
  29. // Fullscreen quad buffers by default.
  30. private static _DefaultOptions: IEffectRendererOptions = {
  31. positions: [1, 1, -1, 1, -1, -1, 1, -1],
  32. indices: [0, 1, 2, 0, 2, 3]
  33. };
  34. private _vertexBuffers: {[key: string]: VertexBuffer};
  35. private _indexBuffer: DataBuffer;
  36. private _ringBufferIndex = 0;
  37. private _ringScreenBuffer: Nullable<Array<Texture>> = null;
  38. private _fullscreenViewport = new Viewport(0, 0, 1, 1);
  39. private _getNextFrameBuffer(incrementIndex = true) {
  40. if (!this._ringScreenBuffer) {
  41. this._ringScreenBuffer = [];
  42. for (var i = 0; i < 2; i++) {
  43. var internalTexture = this.engine.createRenderTargetTexture(
  44. {
  45. width: this.engine.getRenderWidth(true),
  46. height: this.engine.getRenderHeight(true),
  47. },
  48. {
  49. generateDepthBuffer: false,
  50. generateStencilBuffer: false,
  51. generateMipMaps: false,
  52. samplingMode: Constants.TEXTURE_NEAREST_NEAREST,
  53. },
  54. );
  55. var texture = new Texture("", null);
  56. texture._texture = internalTexture;
  57. this._ringScreenBuffer.push(texture);
  58. }
  59. }
  60. var ret = this._ringScreenBuffer[this._ringBufferIndex];
  61. if (incrementIndex) {
  62. this._ringBufferIndex = (this._ringBufferIndex + 1) % 2;
  63. }
  64. return ret;
  65. }
  66. /**
  67. * Creates an effect renderer
  68. * @param engine the engine to use for rendering
  69. * @param options defines the options of the effect renderer
  70. */
  71. constructor(private engine: Engine, options: IEffectRendererOptions = EffectRenderer._DefaultOptions) {
  72. options = {
  73. ...EffectRenderer._DefaultOptions,
  74. ...options,
  75. };
  76. this._vertexBuffers = {
  77. [VertexBuffer.PositionKind]: new VertexBuffer(engine, options.positions!, VertexBuffer.PositionKind, false, false, 2),
  78. };
  79. this._indexBuffer = engine.createIndexBuffer(options.indices!);
  80. // No need here for full screen render.
  81. engine.setDepthBuffer(false);
  82. engine.setStencilBuffer(false);
  83. }
  84. /**
  85. * Sets the current viewport in normalized coordinates 0-1
  86. * @param viewport Defines the viewport to set (defaults to 0 0 1 1)
  87. */
  88. public setViewport(viewport = this._fullscreenViewport): void {
  89. this.engine.setViewport(viewport);
  90. }
  91. /**
  92. * Sets the current effect wrapper to use during draw.
  93. * The effect needs to be ready before calling this api.
  94. * This also sets the default full screen position attribute.
  95. * @param effectWrapper Defines the effect to draw with
  96. */
  97. public applyEffectWrapper(effectWrapper: EffectWrapper): void {
  98. this.engine.enableEffect(effectWrapper.effect);
  99. this.engine.bindBuffers(this._vertexBuffers, this._indexBuffer, effectWrapper.effect);
  100. effectWrapper.onApplyObservable.notifyObservers({});
  101. }
  102. /**
  103. * Draws a full screen quad.
  104. */
  105. public draw(): void {
  106. this.engine.drawElementsType(Constants.MATERIAL_TriangleFillMode, 0, 6);
  107. }
  108. /**
  109. * renders one or more effects to a specified texture
  110. * @param effectWrappers list of effects to renderer
  111. * @param outputTexture texture to draw to, if null it will render to the screen
  112. */
  113. public render(effectWrappers: Array<EffectWrapper> | EffectWrapper, outputTexture: Nullable<Texture> = null) {
  114. if (!Array.isArray(effectWrappers)) {
  115. effectWrappers = [effectWrappers];
  116. }
  117. // Ensure all effects are ready
  118. for (var wrapper of effectWrappers) {
  119. if (!wrapper.effect.isReady()) {
  120. return;
  121. }
  122. }
  123. effectWrappers.forEach((effectWrapper, i) => {
  124. var renderTo = outputTexture;
  125. // for any next effect make it's input the output of the previous effect
  126. if (i !== 0) {
  127. effectWrapper.effect.onBindObservable.addOnce(() => {
  128. effectWrapper.effect.setTexture("textureSampler", this._getNextFrameBuffer(false));
  129. });
  130. }
  131. // Set the output to the next screenbuffer
  132. if ((effectWrappers as Array<EffectWrapper>).length > 1 && i != (effectWrappers as Array<EffectWrapper>).length - 1) {
  133. renderTo = this._getNextFrameBuffer();
  134. } else {
  135. renderTo = outputTexture;
  136. }
  137. // Reset state
  138. this.setViewport();
  139. this.applyEffectWrapper(effectWrapper);
  140. if (renderTo) {
  141. this.engine.bindFramebuffer(renderTo.getInternalTexture()!);
  142. }
  143. this.draw();
  144. if (renderTo) {
  145. this.engine.unBindFramebuffer(renderTo.getInternalTexture()!);
  146. }
  147. });
  148. }
  149. /**
  150. * Disposes of the effect renderer
  151. */
  152. dispose() {
  153. if (this._ringScreenBuffer) {
  154. this._ringScreenBuffer.forEach((b) => {
  155. b.dispose();
  156. });
  157. this._ringScreenBuffer = null;
  158. }
  159. var vertexBuffer = this._vertexBuffers[VertexBuffer.PositionKind];
  160. if (vertexBuffer) {
  161. vertexBuffer.dispose();
  162. delete this._vertexBuffers[VertexBuffer.PositionKind];
  163. }
  164. if (this._indexBuffer) {
  165. this.engine._releaseBuffer(this._indexBuffer);
  166. }
  167. }
  168. }
  169. /**
  170. * Options to create an EffectWrapper
  171. */
  172. interface EffectWrapperCreationOptions {
  173. /**
  174. * Engine to use to create the effect
  175. */
  176. engine: Engine;
  177. /**
  178. * Fragment shader for the effect
  179. */
  180. fragmentShader: string;
  181. /**
  182. * Vertex shader for the effect
  183. */
  184. vertexShader?: string;
  185. /**
  186. * Attributes to use in the shader
  187. */
  188. attributeNames?: Array<string>;
  189. /**
  190. * Uniforms to use in the shader
  191. */
  192. uniformNames?: Array<string>;
  193. /**
  194. * Texture sampler names to use in the shader
  195. */
  196. samplerNames?: Array<string>;
  197. /**
  198. * The friendly name of the effect displayed in Spector.
  199. */
  200. name?: string;
  201. }
  202. /**
  203. * Wraps an effect to be used for rendering
  204. */
  205. export class EffectWrapper {
  206. /**
  207. * Event that is fired right before the effect is drawn (should be used to update uniforms)
  208. */
  209. public onApplyObservable = new Observable<{}>();
  210. /**
  211. * The underlying effect
  212. */
  213. public effect: Effect;
  214. /**
  215. * Creates an effect to be renderer
  216. * @param creationOptions options to create the effect
  217. */
  218. constructor(creationOptions: EffectWrapperCreationOptions) {
  219. let effectCreationOptions: any;
  220. if (creationOptions.vertexShader) {
  221. effectCreationOptions = {
  222. fragmentSource: creationOptions.fragmentShader,
  223. vertexSource: creationOptions.vertexShader,
  224. spectorName: creationOptions.name || "effectWrapper"
  225. };
  226. }
  227. else {
  228. effectCreationOptions = {
  229. fragmentSource: creationOptions.fragmentShader,
  230. vertex: "postprocess",
  231. spectorName: creationOptions.name || "effectWrapper"
  232. };
  233. }
  234. this.effect = new Effect(effectCreationOptions,
  235. creationOptions.attributeNames || ["position"],
  236. creationOptions.uniformNames || ["scale"],
  237. creationOptions.samplerNames,
  238. creationOptions.engine);
  239. }
  240. /**
  241. * Disposes of the effect wrapper
  242. */
  243. public dispose() {
  244. this.effect.dispose();
  245. }
  246. }