geometryBufferRenderer.ts 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442
  1. import { Matrix, Color4 } from "../Maths/math";
  2. import { VertexBuffer } from "../Meshes/buffer";
  3. import { SubMesh } from "../Meshes/subMesh";
  4. import { Mesh } from "../Meshes/mesh";
  5. import { Constants } from "../Engines/constants";
  6. import { SmartArray } from "../Misc/smartArray";
  7. import { Texture } from "../Materials/Textures/texture";
  8. import { MultiRenderTarget } from "../Materials/Textures/multiRenderTarget";
  9. import { Effect } from "../Materials/effect";
  10. import { Material } from "../Materials/material";
  11. import { MaterialHelper } from "../Materials/materialHelper";
  12. import { Scene } from "../scene";
  13. import { AbstractMesh } from "../Meshes/abstractMesh";
  14. import "../Shaders/geometry.fragment";
  15. import "../Shaders/geometry.vertex";
  16. import { _DevTools } from '../Misc/devTools';
  17. /** @hidden */
  18. interface ISavedTransformationMatrix {
  19. world: Matrix;
  20. viewProjection: Matrix;
  21. }
  22. /**
  23. * This renderer is helpfull to fill one of the render target with a geometry buffer.
  24. */
  25. export class GeometryBufferRenderer {
  26. /**
  27. * Constant used to retrieve the position texture index in the G-Buffer textures array
  28. * using getIndex(GeometryBufferRenderer.POSITION_TEXTURE_INDEX)
  29. */
  30. public static readonly POSITION_TEXTURE_TYPE = 1;
  31. /**
  32. * Constant used to retrieve the velocity texture index in the G-Buffer textures array
  33. * using getIndex(GeometryBufferRenderer.VELOCITY_TEXTURE_INDEX)
  34. */
  35. public static readonly VELOCITY_TEXTURE_TYPE = 2;
  36. /**
  37. * Dictionary used to store the previous transformation matrices of each rendered mesh
  38. * in order to compute objects velocities when enableVelocity is set to "true"
  39. * @hidden
  40. */
  41. public _previousTransformationMatrices: { [index: number]: ISavedTransformationMatrix } = {};
  42. /**
  43. * Dictionary used to store the previous bones transformation matrices of each rendered mesh
  44. * in order to compute objects velocities when enableVelocity is set to "true"
  45. * @hidden
  46. */
  47. public _previousBonesTransformationMatrices: { [index: number]: Float32Array } = {};
  48. /**
  49. * Array used to store the ignored skinned meshes while computing velocity map (typically used by the motion blur post-process).
  50. * Avoids computing bones velocities and computes only mesh's velocity itself (position, rotation, scaling).
  51. */
  52. public excludedSkinnedMeshesFromVelocity: AbstractMesh[] = [];
  53. private _scene: Scene;
  54. private _multiRenderTarget: MultiRenderTarget;
  55. private _ratio: number;
  56. private _enablePosition: boolean = false;
  57. private _enableVelocity: boolean = false;
  58. private _positionIndex: number = -1;
  59. private _velocityIndex: number = -1;
  60. protected _effect: Effect;
  61. protected _cachedDefines: string;
  62. /**
  63. * Set the render list (meshes to be rendered) used in the G buffer.
  64. */
  65. public set renderList(meshes: Mesh[]) {
  66. this._multiRenderTarget.renderList = meshes;
  67. }
  68. /**
  69. * Gets wether or not G buffer are supported by the running hardware.
  70. * This requires draw buffer supports
  71. */
  72. public get isSupported(): boolean {
  73. return this._multiRenderTarget.isSupported;
  74. }
  75. /**
  76. * Returns the index of the given texture type in the G-Buffer textures array
  77. * @param textureType The texture type constant. For example GeometryBufferRenderer.POSITION_TEXTURE_INDEX
  78. * @returns the index of the given texture type in the G-Buffer textures array
  79. */
  80. public getTextureIndex(textureType: number): number {
  81. switch (textureType) {
  82. case GeometryBufferRenderer.POSITION_TEXTURE_TYPE: return this._positionIndex;
  83. case GeometryBufferRenderer.VELOCITY_TEXTURE_TYPE: return this._velocityIndex;
  84. default: return -1;
  85. }
  86. }
  87. /**
  88. * Gets a boolean indicating if objects positions are enabled for the G buffer.
  89. */
  90. public get enablePosition(): boolean {
  91. return this._enablePosition;
  92. }
  93. /**
  94. * Sets whether or not objects positions are enabled for the G buffer.
  95. */
  96. public set enablePosition(enable: boolean) {
  97. this._enablePosition = enable;
  98. this.dispose();
  99. this._createRenderTargets();
  100. }
  101. /**
  102. * Gets a boolean indicating if objects velocities are enabled for the G buffer.
  103. */
  104. public get enableVelocity(): boolean {
  105. return this._enableVelocity;
  106. }
  107. /**
  108. * Sets wether or not objects velocities are enabled for the G buffer.
  109. */
  110. public set enableVelocity(enable: boolean) {
  111. this._enableVelocity = enable;
  112. if (!enable) {
  113. this._previousTransformationMatrices = {};
  114. }
  115. this.dispose();
  116. this._createRenderTargets();
  117. }
  118. /**
  119. * Gets the scene associated with the buffer.
  120. */
  121. public get scene(): Scene {
  122. return this._scene;
  123. }
  124. /**
  125. * Gets the ratio used by the buffer during its creation.
  126. * How big is the buffer related to the main canvas.
  127. */
  128. public get ratio(): number {
  129. return this._ratio;
  130. }
  131. /** @hidden */
  132. public static _SceneComponentInitialization: (scene: Scene) => void = (_) => {
  133. throw _DevTools.WarnImport("GeometryBufferRendererSceneComponent");
  134. }
  135. /**
  136. * Creates a new G Buffer for the scene
  137. * @param scene The scene the buffer belongs to
  138. * @param ratio How big is the buffer related to the main canvas.
  139. */
  140. constructor(scene: Scene, ratio: number = 1) {
  141. this._scene = scene;
  142. this._ratio = ratio;
  143. GeometryBufferRenderer._SceneComponentInitialization(this._scene);
  144. // Render target
  145. this._createRenderTargets();
  146. }
  147. /**
  148. * Checks wether everything is ready to render a submesh to the G buffer.
  149. * @param subMesh the submesh to check readiness for
  150. * @param useInstances is the mesh drawn using instance or not
  151. * @returns true if ready otherwise false
  152. */
  153. public isReady(subMesh: SubMesh, useInstances: boolean): boolean {
  154. var material: any = subMesh.getMaterial();
  155. if (material && material.disableDepthWrite) {
  156. return false;
  157. }
  158. var defines = [];
  159. var attribs = [VertexBuffer.PositionKind, VertexBuffer.NormalKind];
  160. var mesh = subMesh.getMesh();
  161. // Alpha test
  162. if (material && material.needAlphaTesting()) {
  163. defines.push("#define ALPHATEST");
  164. if (mesh.isVerticesDataPresent(VertexBuffer.UVKind)) {
  165. attribs.push(VertexBuffer.UVKind);
  166. defines.push("#define UV1");
  167. }
  168. if (mesh.isVerticesDataPresent(VertexBuffer.UV2Kind)) {
  169. attribs.push(VertexBuffer.UV2Kind);
  170. defines.push("#define UV2");
  171. }
  172. }
  173. // Buffers
  174. if (this._enablePosition) {
  175. defines.push("#define POSITION");
  176. defines.push("#define POSITION_INDEX " + this._positionIndex);
  177. }
  178. if (this._enableVelocity) {
  179. defines.push("#define VELOCITY");
  180. defines.push("#define VELOCITY_INDEX " + this._velocityIndex);
  181. if (this.excludedSkinnedMeshesFromVelocity.indexOf(mesh) === -1) {
  182. defines.push("#define BONES_VELOCITY_ENABLED");
  183. }
  184. }
  185. // Bones
  186. if (mesh.useBones && mesh.computeBonesUsingShaders) {
  187. attribs.push(VertexBuffer.MatricesIndicesKind);
  188. attribs.push(VertexBuffer.MatricesWeightsKind);
  189. if (mesh.numBoneInfluencers > 4) {
  190. attribs.push(VertexBuffer.MatricesIndicesExtraKind);
  191. attribs.push(VertexBuffer.MatricesWeightsExtraKind);
  192. }
  193. defines.push("#define NUM_BONE_INFLUENCERS " + mesh.numBoneInfluencers);
  194. defines.push("#define BonesPerMesh " + (mesh.skeleton ? mesh.skeleton.bones.length + 1 : 0));
  195. } else {
  196. defines.push("#define NUM_BONE_INFLUENCERS 0");
  197. }
  198. // Morph targets
  199. const morphTargetManager = (mesh as Mesh).morphTargetManager;
  200. let numMorphInfluencers = 0;
  201. if (morphTargetManager) {
  202. if (morphTargetManager.numInfluencers > 0) {
  203. numMorphInfluencers = morphTargetManager.numInfluencers;
  204. defines.push("#define MORPHTARGETS");
  205. defines.push("#define NUM_MORPH_INFLUENCERS " + numMorphInfluencers);
  206. MaterialHelper.PrepareAttributesForMorphTargets(attribs, mesh, { "NUM_MORPH_INFLUENCERS": numMorphInfluencers });
  207. }
  208. }
  209. // Instances
  210. if (useInstances) {
  211. defines.push("#define INSTANCES");
  212. MaterialHelper.PushAttributesForInstances(attribs);
  213. }
  214. // Setup textures count
  215. defines.push("#define RENDER_TARGET_COUNT " + this._multiRenderTarget.textures.length);
  216. // Get correct effect
  217. var join = defines.join("\n");
  218. if (this._cachedDefines !== join) {
  219. this._cachedDefines = join;
  220. this._effect = this._scene.getEngine().createEffect("geometry",
  221. attribs,
  222. ["world", "mBones", "viewProjection", "diffuseMatrix", "view", "previousWorld", "previousViewProjection", "mPreviousBones", "morphTargetInfluences"],
  223. ["diffuseSampler"], join,
  224. undefined, undefined, undefined,
  225. { buffersCount: this._enablePosition ? 3 : 2, maxSimultaneousMorphTargets: numMorphInfluencers});
  226. }
  227. return this._effect.isReady();
  228. }
  229. /**
  230. * Gets the current underlying G Buffer.
  231. * @returns the buffer
  232. */
  233. public getGBuffer(): MultiRenderTarget {
  234. return this._multiRenderTarget;
  235. }
  236. /**
  237. * Gets the number of samples used to render the buffer (anti aliasing).
  238. */
  239. public get samples(): number {
  240. return this._multiRenderTarget.samples;
  241. }
  242. /**
  243. * Sets the number of samples used to render the buffer (anti aliasing).
  244. */
  245. public set samples(value: number) {
  246. this._multiRenderTarget.samples = value;
  247. }
  248. /**
  249. * Disposes the renderer and frees up associated resources.
  250. */
  251. public dispose(): void {
  252. this.getGBuffer().dispose();
  253. }
  254. protected _createRenderTargets(): void {
  255. var engine = this._scene.getEngine();
  256. var count = 2;
  257. if (this._enablePosition) {
  258. this._positionIndex = count;
  259. count++;
  260. }
  261. if (this._enableVelocity) {
  262. this._velocityIndex = count;
  263. count++;
  264. }
  265. this._multiRenderTarget = new MultiRenderTarget("gBuffer",
  266. { width: engine.getRenderWidth() * this._ratio, height: engine.getRenderHeight() * this._ratio }, count, this._scene,
  267. { generateMipMaps: false, generateDepthTexture: true, defaultType: Constants.TEXTURETYPE_FLOAT });
  268. if (!this.isSupported) {
  269. return;
  270. }
  271. this._multiRenderTarget.wrapU = Texture.CLAMP_ADDRESSMODE;
  272. this._multiRenderTarget.wrapV = Texture.CLAMP_ADDRESSMODE;
  273. this._multiRenderTarget.refreshRate = 1;
  274. this._multiRenderTarget.renderParticles = false;
  275. this._multiRenderTarget.renderList = null;
  276. // set default depth value to 1.0 (far away)
  277. this._multiRenderTarget.onClearObservable.add((engine) => {
  278. engine.clear(new Color4(0.0, 0.0, 0.0, 1.0), true, true, true);
  279. });
  280. // Custom render function
  281. var renderSubMesh = (subMesh: SubMesh): void => {
  282. var mesh = subMesh.getRenderingMesh();
  283. var scene = this._scene;
  284. var engine = scene.getEngine();
  285. let material = subMesh.getMaterial();
  286. if (!material) {
  287. return;
  288. }
  289. // Velocity
  290. if (this._enableVelocity && !this._previousTransformationMatrices[mesh.uniqueId]) {
  291. this._previousTransformationMatrices[mesh.uniqueId] = {
  292. world: Matrix.Identity(),
  293. viewProjection: scene.getTransformMatrix()
  294. };
  295. if (mesh.skeleton) {
  296. const bonesTransformations = mesh.skeleton.getTransformMatrices(mesh);
  297. this._previousBonesTransformationMatrices[mesh.uniqueId] = this._copyBonesTransformationMatrices(bonesTransformations, new Float32Array(bonesTransformations.length));
  298. }
  299. }
  300. // Culling
  301. engine.setState(material.backFaceCulling, 0, false, scene.useRightHandedSystem);
  302. // Managing instances
  303. var batch = mesh._getInstancesRenderList(subMesh._id);
  304. if (batch.mustReturn) {
  305. return;
  306. }
  307. var hardwareInstancedRendering = (engine.getCaps().instancedArrays) && (batch.visibleInstances[subMesh._id] !== null);
  308. if (this.isReady(subMesh, hardwareInstancedRendering)) {
  309. engine.enableEffect(this._effect);
  310. mesh._bind(subMesh, this._effect, Material.TriangleFillMode);
  311. this._effect.setMatrix("viewProjection", scene.getTransformMatrix());
  312. this._effect.setMatrix("view", scene.getViewMatrix());
  313. // Alpha test
  314. if (material && material.needAlphaTesting()) {
  315. var alphaTexture = material.getAlphaTestTexture();
  316. if (alphaTexture) {
  317. this._effect.setTexture("diffuseSampler", alphaTexture);
  318. this._effect.setMatrix("diffuseMatrix", alphaTexture.getTextureMatrix());
  319. }
  320. }
  321. // Bones
  322. if (mesh.useBones && mesh.computeBonesUsingShaders && mesh.skeleton) {
  323. this._effect.setMatrices("mBones", mesh.skeleton.getTransformMatrices(mesh));
  324. if (this._enableVelocity) {
  325. this._effect.setMatrices("mPreviousBones", this._previousBonesTransformationMatrices[mesh.uniqueId]);
  326. }
  327. }
  328. // Morph targets
  329. MaterialHelper.BindMorphTargetParameters(mesh, this._effect);
  330. // Velocity
  331. if (this._enableVelocity) {
  332. this._effect.setMatrix("previousWorld", this._previousTransformationMatrices[mesh.uniqueId].world);
  333. this._effect.setMatrix("previousViewProjection", this._previousTransformationMatrices[mesh.uniqueId].viewProjection);
  334. }
  335. // Draw
  336. mesh._processRendering(subMesh, this._effect, Material.TriangleFillMode, batch, hardwareInstancedRendering,
  337. (isInstance, world) => this._effect.setMatrix("world", world));
  338. }
  339. // Velocity
  340. if (this._enableVelocity) {
  341. this._previousTransformationMatrices[mesh.uniqueId].world = mesh.getWorldMatrix().clone();
  342. this._previousTransformationMatrices[mesh.uniqueId].viewProjection = this._scene.getTransformMatrix().clone();
  343. if (mesh.skeleton) {
  344. this._copyBonesTransformationMatrices(mesh.skeleton.getTransformMatrices(mesh), this._previousBonesTransformationMatrices[mesh.uniqueId]);
  345. }
  346. }
  347. };
  348. this._multiRenderTarget.customRenderFunction = (opaqueSubMeshes: SmartArray<SubMesh>, alphaTestSubMeshes: SmartArray<SubMesh>, transparentSubMeshes: SmartArray<SubMesh>, depthOnlySubMeshes: SmartArray<SubMesh>): void => {
  349. var index;
  350. if (depthOnlySubMeshes.length) {
  351. engine.setColorWrite(false);
  352. for (index = 0; index < depthOnlySubMeshes.length; index++) {
  353. renderSubMesh(depthOnlySubMeshes.data[index]);
  354. }
  355. engine.setColorWrite(true);
  356. }
  357. for (index = 0; index < opaqueSubMeshes.length; index++) {
  358. renderSubMesh(opaqueSubMeshes.data[index]);
  359. }
  360. for (index = 0; index < alphaTestSubMeshes.length; index++) {
  361. renderSubMesh(alphaTestSubMeshes.data[index]);
  362. }
  363. };
  364. }
  365. // Copies the bones transformation matrices into the target array and returns the target's reference
  366. private _copyBonesTransformationMatrices(source: Float32Array, target: Float32Array): Float32Array {
  367. for (let i = 0; i < source.length; i++) {
  368. target[i] = source[i];
  369. }
  370. return target;
  371. }
  372. }