effectRenderer.ts 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286
  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. * Restores engine states
  83. */
  84. public restoreStates(): void {
  85. this.engine.depthCullingState.depthTest = true;
  86. this.engine.stencilState.stencilTest = true;
  87. }
  88. /**
  89. * Draws a full screen quad.
  90. */
  91. public draw(): void {
  92. this.engine.drawElementsType(Constants.MATERIAL_TriangleFillMode, 0, 6);
  93. }
  94. private isRenderTargetTexture(texture: InternalTexture | RenderTargetTexture): texture is RenderTargetTexture {
  95. return (texture as RenderTargetTexture).renderList !== undefined;
  96. }
  97. /**
  98. * renders one or more effects to a specified texture
  99. * @param effectWrapper the effect to renderer
  100. * @param outputTexture texture to draw to, if null it will render to the screen.
  101. */
  102. public render(effectWrapper: EffectWrapper, outputTexture: Nullable<InternalTexture | RenderTargetTexture> = null) {
  103. // Ensure effect is ready
  104. if (!effectWrapper.effect.isReady()) {
  105. return;
  106. }
  107. // Reset state
  108. this.setViewport();
  109. const out = outputTexture === null ? null : this.isRenderTargetTexture(outputTexture) ? outputTexture.getInternalTexture()! : outputTexture;
  110. if (out) {
  111. this.engine.bindFramebuffer(out);
  112. }
  113. this.applyEffectWrapper(effectWrapper);
  114. this.draw();
  115. if (out) {
  116. this.engine.unBindFramebuffer(out);
  117. }
  118. this.restoreStates();
  119. }
  120. /**
  121. * Disposes of the effect renderer
  122. */
  123. dispose() {
  124. var vertexBuffer = this._vertexBuffers[VertexBuffer.PositionKind];
  125. if (vertexBuffer) {
  126. vertexBuffer.dispose();
  127. delete this._vertexBuffers[VertexBuffer.PositionKind];
  128. }
  129. if (this._indexBuffer) {
  130. this.engine._releaseBuffer(this._indexBuffer);
  131. }
  132. }
  133. }
  134. /**
  135. * Options to create an EffectWrapper
  136. */
  137. interface EffectWrapperCreationOptions {
  138. /**
  139. * Engine to use to create the effect
  140. */
  141. engine: ThinEngine;
  142. /**
  143. * Fragment shader for the effect
  144. */
  145. fragmentShader: string;
  146. /**
  147. * Use the shader store instead of direct source code
  148. */
  149. useShaderStore?: boolean;
  150. /**
  151. * Vertex shader for the effect
  152. */
  153. vertexShader?: string;
  154. /**
  155. * Attributes to use in the shader
  156. */
  157. attributeNames?: Array<string>;
  158. /**
  159. * Uniforms to use in the shader
  160. */
  161. uniformNames?: Array<string>;
  162. /**
  163. * Texture sampler names to use in the shader
  164. */
  165. samplerNames?: Array<string>;
  166. /**
  167. * Defines to use in the shader
  168. */
  169. defines?: Array<string>;
  170. /**
  171. * Callback when effect is compiled
  172. */
  173. onCompiled?: Nullable<(effect: Effect) => void>;
  174. /**
  175. * The friendly name of the effect displayed in Spector.
  176. */
  177. name?: string;
  178. }
  179. /**
  180. * Wraps an effect to be used for rendering
  181. */
  182. export class EffectWrapper {
  183. /**
  184. * Event that is fired right before the effect is drawn (should be used to update uniforms)
  185. */
  186. public onApplyObservable = new Observable<{}>();
  187. /**
  188. * The underlying effect
  189. */
  190. public effect: Effect;
  191. /**
  192. * Creates an effect to be renderer
  193. * @param creationOptions options to create the effect
  194. */
  195. constructor(creationOptions: EffectWrapperCreationOptions) {
  196. let effectCreationOptions: any;
  197. const uniformNames = creationOptions.uniformNames || [];
  198. if (creationOptions.vertexShader) {
  199. effectCreationOptions = {
  200. fragmentSource: creationOptions.fragmentShader,
  201. vertexSource: creationOptions.vertexShader,
  202. spectorName: creationOptions.name || "effectWrapper"
  203. };
  204. }
  205. else {
  206. // Default scale to use in post process vertex shader.
  207. uniformNames.push("scale");
  208. effectCreationOptions = {
  209. fragmentSource: creationOptions.fragmentShader,
  210. vertex: "postprocess",
  211. spectorName: creationOptions.name || "effectWrapper"
  212. };
  213. // Sets the default scale to identity for the post process vertex shader.
  214. this.onApplyObservable.add(() => {
  215. this.effect.setFloat2("scale", 1, 1);
  216. });
  217. }
  218. const defines = creationOptions.defines ? creationOptions.defines.join("\n") : "";
  219. if (creationOptions.useShaderStore) {
  220. effectCreationOptions.fragment = effectCreationOptions.fragmentSource;
  221. if (!effectCreationOptions.vertex) {
  222. effectCreationOptions.vertex = effectCreationOptions.vertexSource;
  223. }
  224. delete effectCreationOptions.fragmentSource;
  225. delete effectCreationOptions.vertexSource;
  226. this.effect = creationOptions.engine.createEffect(effectCreationOptions.spectorName,
  227. creationOptions.attributeNames || ["position"],
  228. uniformNames,
  229. creationOptions.samplerNames,
  230. defines,
  231. undefined,
  232. creationOptions.onCompiled
  233. );
  234. } else {
  235. this.effect = new Effect(effectCreationOptions,
  236. creationOptions.attributeNames || ["position"],
  237. uniformNames,
  238. creationOptions.samplerNames,
  239. creationOptions.engine,
  240. defines,
  241. undefined,
  242. creationOptions.onCompiled,
  243. );
  244. }
  245. }
  246. /**
  247. * Disposes of the effect wrapper
  248. */
  249. public dispose() {
  250. this.effect.dispose();
  251. }
  252. }