geometryBufferRenderer.ts 17 KB

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