effectRenderer.ts 9.4 KB

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