babylon.effectLayer.ts 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639
  1. module BABYLON {
  2. /**
  3. * Effect layer options. This helps customizing the behaviour
  4. * of the effect layer.
  5. */
  6. export interface IEffectLayerOptions {
  7. /**
  8. * Multiplication factor apply to the canvas size to compute the render target size
  9. * used to generated the objects (the smaller the faster).
  10. */
  11. mainTextureRatio: number;
  12. /**
  13. * Enforces a fixed size texture to ensure effect stability across devices.
  14. */
  15. mainTextureFixedSize?: number;
  16. /**
  17. * Alpha blending mode used to apply the blur. Default depends of the implementation.
  18. */
  19. alphaBlendingMode: number;
  20. /**
  21. * The camera attached to the layer.
  22. */
  23. camera: Nullable<Camera>;
  24. }
  25. /**
  26. * The effect layer Helps adding post process effect blended with the main pass.
  27. *
  28. * This can be for instance use to generate glow or higlight effects on the scene.
  29. *
  30. * The effect layer class can not be used directly and is intented to inherited from to be
  31. * customized per effects.
  32. */
  33. export abstract class EffectLayer {
  34. private _vertexBuffers: { [key: string]: Nullable<VertexBuffer> } = {};
  35. private _indexBuffer: Nullable<WebGLBuffer>;
  36. private _cachedDefines: string;
  37. private _effectLayerMapGenerationEffect: Effect;
  38. private _effectLayerOptions: IEffectLayerOptions;
  39. private _mergeEffect: Effect;
  40. protected _scene: Scene;
  41. protected _engine: Engine;
  42. protected _maxSize: number = 0;
  43. protected _mainTextureDesiredSize: ISize = { width: 0, height: 0 };
  44. protected _mainTexture: RenderTargetTexture;
  45. protected _shouldRender = true;
  46. protected _postProcesses: PostProcess[] = [];
  47. protected _textures: BaseTexture[] = [];
  48. protected _emissiveTextureAndColor: { texture: Nullable<BaseTexture>, color: Color4 } = { texture: null, color: new Color4() };
  49. /**
  50. * The clear color of the texture used to generate the glow map.
  51. */
  52. public neutralColor: Color4 = new Color4();
  53. /**
  54. * Specifies wether the highlight layer is enabled or not.
  55. */
  56. public isEnabled: boolean = true;
  57. /**
  58. * Gets the camera attached to the layer.
  59. */
  60. public get camera(): Nullable<Camera> {
  61. return this._effectLayerOptions.camera;
  62. }
  63. /**
  64. * An event triggered when the effect layer has been disposed.
  65. */
  66. public onDisposeObservable = new Observable<EffectLayer>();
  67. /**
  68. * An event triggered when the effect layer is about rendering the main texture with the glowy parts.
  69. */
  70. public onBeforeRenderMainTextureObservable = new Observable<EffectLayer>();
  71. /**
  72. * An event triggered when the generated texture is being merged in the scene.
  73. */
  74. public onBeforeComposeObservable = new Observable<EffectLayer>();
  75. /**
  76. * An event triggered when the generated texture has been merged in the scene.
  77. */
  78. public onAfterComposeObservable = new Observable<EffectLayer>();
  79. /**
  80. * An event triggered when the efffect layer changes its size.
  81. */
  82. public onSizeChangedObservable = new Observable<EffectLayer>();
  83. /**
  84. * Instantiates a new effect Layer and references it in the scene.
  85. * @param name The name of the layer
  86. * @param scene The scene to use the layer in
  87. */
  88. constructor(
  89. /** The Friendly of the effect in the scene */
  90. public name: string,
  91. scene: Scene) {
  92. this._scene = scene || Engine.LastCreatedScene;
  93. this._engine = scene.getEngine();
  94. this._maxSize = this._engine.getCaps().maxTextureSize;
  95. this._scene.effectLayers.push(this);
  96. // Generate Buffers
  97. this._generateIndexBuffer();
  98. this._genrateVertexBuffer();
  99. }
  100. /**
  101. * Get the effect name of the layer.
  102. * @return The effect name
  103. */
  104. public abstract getEffectName(): string;
  105. /**
  106. * Checks for the readiness of the element composing the layer.
  107. * @param subMesh the mesh to check for
  108. * @param useInstances specify wether or not to use instances to render the mesh
  109. * @return true if ready otherwise, false
  110. */
  111. public abstract isReady(subMesh: SubMesh, useInstances: boolean): boolean;
  112. /**
  113. * Returns wether or nood the layer needs stencil enabled during the mesh rendering.
  114. * @returns true if the effect requires stencil during the main canvas render pass.
  115. */
  116. public abstract needStencil(): boolean;
  117. /**
  118. * Create the merge effect. This is the shader use to blit the information back
  119. * to the main canvas at the end of the scene rendering.
  120. * @returns The effect containing the shader used to merge the effect on the main canvas
  121. */
  122. protected abstract _createMergeEffect(): Effect;
  123. /**
  124. * Creates the render target textures and post processes used in the effect layer.
  125. */
  126. protected abstract _createTextureAndPostProcesses(): void;
  127. /**
  128. * Implementation specific of rendering the generating effect on the main canvas.
  129. * @param effect The effect used to render through
  130. */
  131. protected abstract _internalRender(effect: Effect): void;
  132. /**
  133. * Sets the required values for both the emissive texture and and the main color.
  134. */
  135. protected abstract _setEmissiveTextureAndColor(mesh: Mesh, subMesh: SubMesh, material: Material): void;
  136. /**
  137. * Free any resources and references associated to a mesh.
  138. * Internal use
  139. * @param mesh The mesh to free.
  140. */
  141. public abstract _disposeMesh(mesh: Mesh): void;
  142. /**
  143. * Initializes the effect layer with the required options.
  144. * @param options Sets of none mandatory options to use with the layer (see IEffectLayerOptions for more information)
  145. */
  146. protected _init(options: Partial<IEffectLayerOptions>): void {
  147. // Adapt options
  148. this._effectLayerOptions = {
  149. mainTextureRatio: 0.5,
  150. alphaBlendingMode: Engine.ALPHA_COMBINE,
  151. camera: null,
  152. ...options,
  153. };
  154. this._setMainTextureSize();
  155. this._createMainTexture();
  156. this._createTextureAndPostProcesses();
  157. this._mergeEffect = this._createMergeEffect();
  158. }
  159. /**
  160. * Generates the index buffer of the full screen quad blending to the main canvas.
  161. */
  162. private _generateIndexBuffer(): void {
  163. // Indices
  164. var indices = [];
  165. indices.push(0);
  166. indices.push(1);
  167. indices.push(2);
  168. indices.push(0);
  169. indices.push(2);
  170. indices.push(3);
  171. this._indexBuffer = this._engine.createIndexBuffer(indices);
  172. }
  173. /**
  174. * Generates the vertex buffer of the full screen quad blending to the main canvas.
  175. */
  176. private _genrateVertexBuffer(): void {
  177. // VBO
  178. var vertices = [];
  179. vertices.push(1, 1);
  180. vertices.push(-1, 1);
  181. vertices.push(-1, -1);
  182. vertices.push(1, -1);
  183. var vertexBuffer = new VertexBuffer(this._engine, vertices, VertexBuffer.PositionKind, false, false, 2);
  184. this._vertexBuffers[VertexBuffer.PositionKind] = vertexBuffer;
  185. }
  186. /**
  187. * Sets the main texture desired size which is the closest power of two
  188. * of the engine canvas size.
  189. */
  190. private _setMainTextureSize(): void {
  191. if (this._effectLayerOptions.mainTextureFixedSize) {
  192. this._mainTextureDesiredSize.width = this._effectLayerOptions.mainTextureFixedSize;
  193. this._mainTextureDesiredSize.height = this._effectLayerOptions.mainTextureFixedSize;
  194. }
  195. else {
  196. this._mainTextureDesiredSize.width = this._engine.getRenderWidth() * this._effectLayerOptions.mainTextureRatio;
  197. this._mainTextureDesiredSize.height = this._engine.getRenderHeight() * this._effectLayerOptions.mainTextureRatio;
  198. this._mainTextureDesiredSize.width = this._engine.needPOTTextures ? Tools.GetExponentOfTwo(this._mainTextureDesiredSize.width, this._maxSize) : this._mainTextureDesiredSize.width;
  199. this._mainTextureDesiredSize.height = this._engine.needPOTTextures ? Tools.GetExponentOfTwo(this._mainTextureDesiredSize.height, this._maxSize) : this._mainTextureDesiredSize.height;
  200. }
  201. this._mainTextureDesiredSize.width = Math.floor(this._mainTextureDesiredSize.width);
  202. this._mainTextureDesiredSize.height = Math.floor(this._mainTextureDesiredSize.height);
  203. }
  204. /**
  205. * Creates the main texture for the effect layer.
  206. */
  207. protected _createMainTexture(): void {
  208. this._mainTexture = new RenderTargetTexture("HighlightLayerMainRTT",
  209. {
  210. width: this._mainTextureDesiredSize.width,
  211. height: this._mainTextureDesiredSize.height
  212. },
  213. this._scene,
  214. false,
  215. true,
  216. Engine.TEXTURETYPE_UNSIGNED_INT);
  217. this._mainTexture.activeCamera = this._effectLayerOptions.camera;
  218. this._mainTexture.wrapU = Texture.CLAMP_ADDRESSMODE;
  219. this._mainTexture.wrapV = Texture.CLAMP_ADDRESSMODE;
  220. this._mainTexture.anisotropicFilteringLevel = 1;
  221. this._mainTexture.updateSamplingMode(Texture.BILINEAR_SAMPLINGMODE);
  222. this._mainTexture.renderParticles = false;
  223. this._mainTexture.renderList = null;
  224. this._mainTexture.ignoreCameraViewport = true;
  225. // Custom render function
  226. this._mainTexture.customRenderFunction = (opaqueSubMeshes: SmartArray<SubMesh>, alphaTestSubMeshes: SmartArray<SubMesh>, transparentSubMeshes: SmartArray<SubMesh>, depthOnlySubMeshes: SmartArray<SubMesh>): void => {
  227. this.onBeforeRenderMainTextureObservable.notifyObservers(this);
  228. var index: number;
  229. let engine = this._scene.getEngine();
  230. if (depthOnlySubMeshes.length) {
  231. engine.setColorWrite(false);
  232. for (index = 0; index < depthOnlySubMeshes.length; index++) {
  233. this._renderSubMesh(depthOnlySubMeshes.data[index]);
  234. }
  235. engine.setColorWrite(true);
  236. }
  237. for (index = 0; index < opaqueSubMeshes.length; index++) {
  238. this._renderSubMesh(opaqueSubMeshes.data[index]);
  239. }
  240. for (index = 0; index < alphaTestSubMeshes.length; index++) {
  241. this._renderSubMesh(alphaTestSubMeshes.data[index]);
  242. }
  243. for (index = 0; index < transparentSubMeshes.length; index++) {
  244. this._renderSubMesh(transparentSubMeshes.data[index]);
  245. }
  246. };
  247. this._mainTexture.onClearObservable.add((engine: Engine) => {
  248. engine.clear(this.neutralColor, true, true, true);
  249. });
  250. }
  251. /**
  252. * Checks for the readiness of the element composing the layer.
  253. * @param subMesh the mesh to check for
  254. * @param useInstances specify wether or not to use instances to render the mesh
  255. * @param emissiveTexture the associated emissive texture used to generate the glow
  256. * @return true if ready otherwise, false
  257. */
  258. protected _isReady(subMesh: SubMesh, useInstances: boolean, emissiveTexture: Nullable<BaseTexture>): boolean {
  259. let material = subMesh.getMaterial();
  260. if (!material) {
  261. return false;
  262. }
  263. if (!material.isReady(subMesh.getMesh(), useInstances)) {
  264. return false;
  265. }
  266. var defines = [];
  267. var attribs = [VertexBuffer.PositionKind];
  268. var mesh = subMesh.getMesh();
  269. var uv1 = false;
  270. var uv2 = false;
  271. // Alpha test
  272. if (material && material.needAlphaTesting()) {
  273. var alphaTexture = material.getAlphaTestTexture();
  274. if (alphaTexture) {
  275. defines.push("#define ALPHATEST");
  276. if (mesh.isVerticesDataPresent(VertexBuffer.UV2Kind) &&
  277. alphaTexture.coordinatesIndex === 1) {
  278. defines.push("#define DIFFUSEUV2");
  279. uv2 = true;
  280. }
  281. else if (mesh.isVerticesDataPresent(VertexBuffer.UVKind)) {
  282. defines.push("#define DIFFUSEUV1");
  283. uv1 = true;
  284. }
  285. }
  286. }
  287. // Emissive
  288. if (emissiveTexture) {
  289. defines.push("#define EMISSIVE");
  290. if (mesh.isVerticesDataPresent(VertexBuffer.UV2Kind) &&
  291. emissiveTexture.coordinatesIndex === 1) {
  292. defines.push("#define EMISSIVEUV2");
  293. uv2 = true;
  294. }
  295. else if (mesh.isVerticesDataPresent(VertexBuffer.UVKind)) {
  296. defines.push("#define EMISSIVEUV1");
  297. uv1 = true;
  298. }
  299. }
  300. if (uv1) {
  301. attribs.push(VertexBuffer.UVKind);
  302. defines.push("#define UV1");
  303. }
  304. if (uv2) {
  305. attribs.push(VertexBuffer.UV2Kind);
  306. defines.push("#define UV2");
  307. }
  308. // Bones
  309. if (mesh.useBones && mesh.computeBonesUsingShaders) {
  310. attribs.push(VertexBuffer.MatricesIndicesKind);
  311. attribs.push(VertexBuffer.MatricesWeightsKind);
  312. if (mesh.numBoneInfluencers > 4) {
  313. attribs.push(VertexBuffer.MatricesIndicesExtraKind);
  314. attribs.push(VertexBuffer.MatricesWeightsExtraKind);
  315. }
  316. defines.push("#define NUM_BONE_INFLUENCERS " + mesh.numBoneInfluencers);
  317. defines.push("#define BonesPerMesh " + (mesh.skeleton ? (mesh.skeleton.bones.length + 1) : 0));
  318. } else {
  319. defines.push("#define NUM_BONE_INFLUENCERS 0");
  320. }
  321. // Instances
  322. if (useInstances) {
  323. defines.push("#define INSTANCES");
  324. attribs.push("world0");
  325. attribs.push("world1");
  326. attribs.push("world2");
  327. attribs.push("world3");
  328. }
  329. // Get correct effect
  330. var join = defines.join("\n");
  331. if (this._cachedDefines !== join) {
  332. this._cachedDefines = join;
  333. this._effectLayerMapGenerationEffect = this._scene.getEngine().createEffect("glowMapGeneration",
  334. attribs,
  335. ["world", "mBones", "viewProjection", "diffuseMatrix", "color", "emissiveMatrix"],
  336. ["diffuseSampler", "emissiveSampler"], join);
  337. }
  338. return this._effectLayerMapGenerationEffect.isReady();
  339. }
  340. /**
  341. * Renders the glowing part of the scene by blending the blurred glowing meshes on top of the rendered scene.
  342. */
  343. public render(): void {
  344. var currentEffect = this._mergeEffect;
  345. // Check
  346. if (!currentEffect.isReady())
  347. return;
  348. for (var i = 0; i < this._postProcesses.length; i++) {
  349. if (!this._postProcesses[i].isReady()) {
  350. return;
  351. }
  352. }
  353. var engine = this._scene.getEngine();
  354. this.onBeforeComposeObservable.notifyObservers(this);
  355. // Render
  356. engine.enableEffect(currentEffect);
  357. engine.setState(false);
  358. // VBOs
  359. engine.bindBuffers(this._vertexBuffers, this._indexBuffer, currentEffect);
  360. // Cache
  361. var previousAlphaMode = engine.getAlphaMode();
  362. // Go Blend.
  363. engine.setAlphaMode(this._effectLayerOptions.alphaBlendingMode);
  364. // Blends the map on the main canvas.
  365. this._internalRender(currentEffect);
  366. // Restore Alpha
  367. engine.setAlphaMode(previousAlphaMode);
  368. this.onAfterComposeObservable.notifyObservers(this);
  369. // Handle size changes.
  370. var size = this._mainTexture.getSize();
  371. this._setMainTextureSize();
  372. if (size.width !== this._mainTextureDesiredSize.width || size.height !== this._mainTextureDesiredSize.height) {
  373. // Recreate RTT and post processes on size change.
  374. this.onSizeChangedObservable.notifyObservers(this);
  375. this._disposeTextureAndPostProcesses();
  376. this._createMainTexture();
  377. this._createTextureAndPostProcesses();
  378. }
  379. }
  380. /**
  381. * Determine if a given mesh will be used in the current effect.
  382. * @param mesh mesh to test
  383. * @returns true if the mesh will be used
  384. */
  385. public hasMesh(mesh: AbstractMesh): boolean {
  386. return true;
  387. }
  388. /**
  389. * Returns true if the layer contains information to display, otherwise false.
  390. * @returns true if the glow layer should be rendered
  391. */
  392. public shouldRender(): boolean {
  393. return this.isEnabled && this._shouldRender;
  394. }
  395. /**
  396. * Returns true if the mesh should render, otherwise false.
  397. * @param mesh The mesh to render
  398. * @returns true if it should render otherwise false
  399. */
  400. protected _shouldRenderMesh(mesh: Mesh): boolean {
  401. return true;
  402. }
  403. /**
  404. * Returns true if the mesh should render, otherwise false.
  405. * @param mesh The mesh to render
  406. * @returns true if it should render otherwise false
  407. */
  408. protected _shouldRenderEmissiveTextureForMesh(mesh: Mesh): boolean {
  409. return true;
  410. }
  411. /**
  412. * Renders the submesh passed in parameter to the generation map.
  413. */
  414. protected _renderSubMesh(subMesh: SubMesh): void {
  415. if (!this.shouldRender()) {
  416. return;
  417. }
  418. var material = subMesh.getMaterial();
  419. var mesh = subMesh.getRenderingMesh();
  420. var scene = this._scene;
  421. var engine = scene.getEngine();
  422. if (!material) {
  423. return;
  424. }
  425. // Do not block in blend mode.
  426. if (material.needAlphaBlendingForMesh(mesh)) {
  427. return;
  428. }
  429. // Culling
  430. engine.setState(material.backFaceCulling);
  431. // Managing instances
  432. var batch = mesh._getInstancesRenderList(subMesh._id);
  433. if (batch.mustReturn) {
  434. return;
  435. }
  436. // Early Exit per mesh
  437. if (!this._shouldRenderMesh(mesh)) {
  438. return;
  439. }
  440. var hardwareInstancedRendering = (engine.getCaps().instancedArrays) && (batch.visibleInstances[subMesh._id] !== null) && (batch.visibleInstances[subMesh._id] !== undefined);
  441. this._setEmissiveTextureAndColor(mesh, subMesh, material);
  442. if (this._isReady(subMesh, hardwareInstancedRendering, this._emissiveTextureAndColor.texture)) {
  443. engine.enableEffect(this._effectLayerMapGenerationEffect);
  444. mesh._bind(subMesh, this._effectLayerMapGenerationEffect, Material.TriangleFillMode);
  445. this._effectLayerMapGenerationEffect.setMatrix("viewProjection", scene.getTransformMatrix());
  446. this._effectLayerMapGenerationEffect.setFloat4("color",
  447. this._emissiveTextureAndColor.color.r,
  448. this._emissiveTextureAndColor.color.g,
  449. this._emissiveTextureAndColor.color.b,
  450. this._emissiveTextureAndColor.color.a);
  451. // Alpha test
  452. if (material && material.needAlphaTesting()) {
  453. var alphaTexture = material.getAlphaTestTexture();
  454. if (alphaTexture) {
  455. this._effectLayerMapGenerationEffect.setTexture("diffuseSampler", alphaTexture);
  456. let textureMatrix = alphaTexture.getTextureMatrix();
  457. if (textureMatrix) {
  458. this._effectLayerMapGenerationEffect.setMatrix("diffuseMatrix", textureMatrix);
  459. }
  460. }
  461. }
  462. // Glow emissive only
  463. if (this._emissiveTextureAndColor.texture) {
  464. this._effectLayerMapGenerationEffect.setTexture("emissiveSampler", this._emissiveTextureAndColor.texture);
  465. this._effectLayerMapGenerationEffect.setMatrix("emissiveMatrix", this._emissiveTextureAndColor.texture.getTextureMatrix());
  466. }
  467. // Bones
  468. if (mesh.useBones && mesh.computeBonesUsingShaders && mesh.skeleton) {
  469. this._effectLayerMapGenerationEffect.setMatrices("mBones", mesh.skeleton.getTransformMatrices(mesh));
  470. }
  471. // Draw
  472. mesh._processRendering(subMesh, this._effectLayerMapGenerationEffect, Material.TriangleFillMode, batch, hardwareInstancedRendering,
  473. (isInstance, world) => this._effectLayerMapGenerationEffect.setMatrix("world", world));
  474. } else {
  475. // Need to reset refresh rate of the shadowMap
  476. this._mainTexture.resetRefreshCounter();
  477. }
  478. }
  479. /**
  480. * Rebuild the required buffers.
  481. * @ignore Internal use only.
  482. */
  483. public _rebuild(): void {
  484. let vb = this._vertexBuffers[VertexBuffer.PositionKind];
  485. if (vb) {
  486. vb._rebuild();
  487. }
  488. this._generateIndexBuffer();
  489. }
  490. /**
  491. * Dispose only the render target textures and post process.
  492. */
  493. private _disposeTextureAndPostProcesses(): void {
  494. this._mainTexture.dispose();
  495. for (var i = 0; i < this._postProcesses.length; i++) {
  496. if (this._postProcesses[i]) {
  497. this._postProcesses[i].dispose();
  498. }
  499. }
  500. this._postProcesses = [];
  501. for (var i = 0; i < this._textures.length; i++) {
  502. if (this._textures[i]) {
  503. this._textures[i].dispose();
  504. }
  505. }
  506. this._textures = [];
  507. }
  508. /**
  509. * Dispose the highlight layer and free resources.
  510. */
  511. public dispose(): void {
  512. var vertexBuffer = this._vertexBuffers[VertexBuffer.PositionKind];
  513. if (vertexBuffer) {
  514. vertexBuffer.dispose();
  515. this._vertexBuffers[VertexBuffer.PositionKind] = null;
  516. }
  517. if (this._indexBuffer) {
  518. this._scene.getEngine()._releaseBuffer(this._indexBuffer);
  519. this._indexBuffer = null;
  520. }
  521. // Clean textures and post processes
  522. this._disposeTextureAndPostProcesses();
  523. // Remove from scene
  524. var index = this._scene.effectLayers.indexOf(this, 0);
  525. if (index > -1) {
  526. this._scene.effectLayers.splice(index, 1);
  527. }
  528. // Callback
  529. this.onDisposeObservable.notifyObservers(this);
  530. this.onDisposeObservable.clear();
  531. this.onBeforeRenderMainTextureObservable.clear();
  532. this.onBeforeComposeObservable.clear();
  533. this.onAfterComposeObservable.clear();
  534. this.onSizeChangedObservable.clear();
  535. }
  536. }
  537. }