effectRenderer.ts 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238
  1. import { Nullable } from '../types';
  2. import { InternalTexture } from './Textures/internalTexture';
  3. import { RenderTargetTexture } from './Textures/renderTargetTexture';
  4. import { ThinEngine } from '../Engines/thinEngine';
  5. import { VertexBuffer } from '../Meshes/buffer';
  6. import { Viewport } from '../Maths/math.viewport';
  7. import { Constants } from '../Engines/constants';
  8. import { Observable } from '../Misc/observable';
  9. import { Effect } from './effect';
  10. import { DataBuffer } from '../Meshes/dataBuffer';
  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. * You can access the previous rendering in your shader by declaring a sampler named textureSampler
  29. */
  30. export class EffectRenderer {
  31. // Fullscreen quad buffers by default.
  32. private static _DefaultOptions: IEffectRendererOptions = {
  33. positions: [1, 1, -1, 1, -1, -1, 1, -1],
  34. indices: [0, 1, 2, 0, 2, 3],
  35. };
  36. private _vertexBuffers: {[key: string]: VertexBuffer};
  37. private _indexBuffer: DataBuffer;
  38. private _fullscreenViewport = new Viewport(0, 0, 1, 1);
  39. /**
  40. * Creates an effect renderer
  41. * @param engine the engine to use for rendering
  42. * @param options defines the options of the effect renderer
  43. */
  44. constructor(private engine: ThinEngine, options: IEffectRendererOptions = EffectRenderer._DefaultOptions) {
  45. options = {
  46. ...EffectRenderer._DefaultOptions,
  47. ...options,
  48. };
  49. this._vertexBuffers = {
  50. [VertexBuffer.PositionKind]: new VertexBuffer(engine, options.positions!, VertexBuffer.PositionKind, false, false, 2),
  51. };
  52. this._indexBuffer = engine.createIndexBuffer(options.indices!);
  53. }
  54. /**
  55. * Sets the current viewport in normalized coordinates 0-1
  56. * @param viewport Defines the viewport to set (defaults to 0 0 1 1)
  57. */
  58. public setViewport(viewport = this._fullscreenViewport): void {
  59. this.engine.setViewport(viewport);
  60. }
  61. /**
  62. * Binds the embedded attributes buffer to the effect.
  63. * @param effect Defines the effect to bind the attributes for
  64. */
  65. public bindBuffers(effect: Effect): void {
  66. this.engine.bindBuffers(this._vertexBuffers, this._indexBuffer, effect);
  67. }
  68. /**
  69. * Sets the current effect wrapper to use during draw.
  70. * The effect needs to be ready before calling this api.
  71. * This also sets the default full screen position attribute.
  72. * @param effectWrapper Defines the effect to draw with
  73. */
  74. public applyEffectWrapper(effectWrapper: EffectWrapper): void {
  75. this.engine.depthCullingState.depthTest = false;
  76. this.engine.stencilState.stencilTest = false;
  77. this.engine.enableEffect(effectWrapper.effect);
  78. this.bindBuffers(effectWrapper.effect);
  79. effectWrapper.onApplyObservable.notifyObservers({});
  80. }
  81. /**
  82. * Draws a full screen quad.
  83. */
  84. public draw(): void {
  85. this.engine.drawElementsType(Constants.MATERIAL_TriangleFillMode, 0, 6);
  86. }
  87. private isRenderTargetTexture(texture: InternalTexture | RenderTargetTexture): texture is RenderTargetTexture {
  88. return (texture as RenderTargetTexture).renderList !== undefined;
  89. }
  90. /**
  91. * renders one or more effects to a specified texture
  92. * @param effectWrapper the effect to renderer
  93. * @param outputTexture texture to draw to, if null it will render to the screen.
  94. */
  95. public render(effectWrapper: EffectWrapper, outputTexture: Nullable<InternalTexture | RenderTargetTexture> = null) {
  96. // Ensure effect is ready
  97. if (!effectWrapper.effect.isReady()) {
  98. return ;
  99. }
  100. // Reset state
  101. this.setViewport();
  102. const out = outputTexture === null ? null : this.isRenderTargetTexture(outputTexture) ? outputTexture.getInternalTexture()! : outputTexture;
  103. if (out) {
  104. this.engine.bindFramebuffer(out);
  105. }
  106. this.applyEffectWrapper(effectWrapper);
  107. this.draw();
  108. if (out) {
  109. this.engine.unBindFramebuffer(out);
  110. }
  111. }
  112. /**
  113. * Disposes of the effect renderer
  114. */
  115. dispose() {
  116. var vertexBuffer = this._vertexBuffers[VertexBuffer.PositionKind];
  117. if (vertexBuffer) {
  118. vertexBuffer.dispose();
  119. delete this._vertexBuffers[VertexBuffer.PositionKind];
  120. }
  121. if (this._indexBuffer) {
  122. this.engine._releaseBuffer(this._indexBuffer);
  123. }
  124. }
  125. }
  126. /**
  127. * Options to create an EffectWrapper
  128. */
  129. interface EffectWrapperCreationOptions {
  130. /**
  131. * Engine to use to create the effect
  132. */
  133. engine: ThinEngine;
  134. /**
  135. * Fragment shader for the effect
  136. */
  137. fragmentShader: string;
  138. /**
  139. * Vertex shader for the effect
  140. */
  141. vertexShader?: string;
  142. /**
  143. * Attributes to use in the shader
  144. */
  145. attributeNames?: Array<string>;
  146. /**
  147. * Uniforms to use in the shader
  148. */
  149. uniformNames?: Array<string>;
  150. /**
  151. * Texture sampler names to use in the shader
  152. */
  153. samplerNames?: Array<string>;
  154. /**
  155. * The friendly name of the effect displayed in Spector.
  156. */
  157. name?: string;
  158. }
  159. /**
  160. * Wraps an effect to be used for rendering
  161. */
  162. export class EffectWrapper {
  163. /**
  164. * Event that is fired right before the effect is drawn (should be used to update uniforms)
  165. */
  166. public onApplyObservable = new Observable<{}>();
  167. /**
  168. * The underlying effect
  169. */
  170. public effect: Effect;
  171. /**
  172. * Creates an effect to be renderer
  173. * @param creationOptions options to create the effect
  174. */
  175. constructor(creationOptions: EffectWrapperCreationOptions) {
  176. let effectCreationOptions: any;
  177. const uniformNames = creationOptions.uniformNames || [];
  178. if (creationOptions.vertexShader) {
  179. effectCreationOptions = {
  180. fragmentSource: creationOptions.fragmentShader,
  181. vertexSource: creationOptions.vertexShader,
  182. spectorName: creationOptions.name || "effectWrapper"
  183. };
  184. }
  185. else {
  186. // Default scale to use in post process vertex shader.
  187. uniformNames.push("scale");
  188. effectCreationOptions = {
  189. fragmentSource: creationOptions.fragmentShader,
  190. vertex: "postprocess",
  191. spectorName: creationOptions.name || "effectWrapper"
  192. };
  193. // Sets the default scale to identity for the post process vertex shader.
  194. this.onApplyObservable.add(() => {
  195. this.effect.setFloat2("scale", 1, 1);
  196. });
  197. }
  198. this.effect = new Effect(effectCreationOptions,
  199. creationOptions.attributeNames || ["position"],
  200. uniformNames,
  201. creationOptions.samplerNames,
  202. creationOptions.engine);
  203. }
  204. /**
  205. * Disposes of the effect wrapper
  206. */
  207. public dispose() {
  208. this.effect.dispose();
  209. }
  210. }