babylon.geometryBufferRenderer.ts 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358
  1. module BABYLON {
  2. /**
  3. * This renderer is helpfull to fill one of the render target with a geometry buffer.
  4. */
  5. export class GeometryBufferRenderer {
  6. /**
  7. * Constant used to retrieve the position texture index in the G-Buffer textures array
  8. * using getIndex(GeometryBufferRenderer.POSITION_TEXTURE_INDEX)
  9. */
  10. public static readonly POSITION_TEXTURE_TYPE = 1;
  11. /**
  12. * Constant used to retrieve the velocity texture index in the G-Buffer textures array
  13. * using getIndex(GeometryBufferRenderer.VELOCITY_TEXTURE_INDEX)
  14. */
  15. public static readonly VELOCITY_TEXTURE_TYPE = 2;
  16. /**
  17. * Dictionary used to store the previous transformation matrices of each rendered mesh
  18. * in order to compute objects velocities when enableVelocity is set to "true"
  19. * @hidden
  20. */
  21. public _previousTransformationMatrices: { [index: number]: Matrix } = { };
  22. private _scene: Scene;
  23. private _multiRenderTarget: MultiRenderTarget;
  24. private _ratio: number;
  25. private _enablePosition: boolean = false;
  26. private _enableVelocity: boolean = false;
  27. private _positionIndex: number = -1;
  28. private _velocityIndex: number = -1;
  29. protected _effect: Effect;
  30. protected _cachedDefines: string;
  31. /**
  32. * Set the render list (meshes to be rendered) used in the G buffer.
  33. */
  34. public set renderList(meshes: Mesh[]) {
  35. this._multiRenderTarget.renderList = meshes;
  36. }
  37. /**
  38. * Gets wether or not G buffer are supported by the running hardware.
  39. * This requires draw buffer supports
  40. */
  41. public get isSupported(): boolean {
  42. return this._multiRenderTarget.isSupported;
  43. }
  44. /**
  45. * Returns the index of the given texture type in the G-Buffer textures array
  46. * @param textureType The texture type constant. For example GeometryBufferRenderer.POSITION_TEXTURE_INDEX
  47. * @returns the index of the given texture type in the G-Buffer textures array
  48. */
  49. public getTextureIndex(textureType: number): number {
  50. switch (textureType) {
  51. case GeometryBufferRenderer.POSITION_TEXTURE_TYPE: return this._positionIndex;
  52. case GeometryBufferRenderer.VELOCITY_TEXTURE_TYPE: return this._velocityIndex;
  53. default: return -1;
  54. }
  55. }
  56. /**
  57. * Gets a boolean indicating if objects positions are enabled for the G buffer.
  58. */
  59. public get enablePosition(): boolean {
  60. return this._enablePosition;
  61. }
  62. /**
  63. * Sets whether or not objects positions are enabled for the G buffer.
  64. */
  65. public set enablePosition(enable: boolean) {
  66. this._enablePosition = enable;
  67. this.dispose();
  68. this._createRenderTargets();
  69. }
  70. /**
  71. * Gets a boolean indicating if objects velocities are enabled for the G buffer.
  72. */
  73. public get enableVelocity(): boolean {
  74. return this._enableVelocity;
  75. }
  76. /**
  77. * Sets wether or not objects velocities are enabled for the G buffer.
  78. */
  79. public set enableVelocity(enable: boolean) {
  80. this._enableVelocity = enable;
  81. this.dispose();
  82. this._createRenderTargets();
  83. }
  84. /**
  85. * Gets the scene associated with the buffer.
  86. */
  87. public get scene(): Scene {
  88. return this._scene;
  89. }
  90. /**
  91. * Gets the ratio used by the buffer during its creation.
  92. * How big is the buffer related to the main canvas.
  93. */
  94. public get ratio(): number {
  95. return this._ratio;
  96. }
  97. /**
  98. * Creates a new G Buffer for the scene
  99. * @param scene The scene the buffer belongs to
  100. * @param ratio How big is the buffer related to the main canvas.
  101. */
  102. constructor(scene: Scene, ratio: number = 1) {
  103. this._scene = scene;
  104. this._ratio = ratio;
  105. // Register the G Buffer component to the scene.
  106. let component = scene._getComponent(SceneComponentConstants.NAME_GEOMETRYBUFFERRENDERER) as GeometryBufferRendererSceneComponent;
  107. if (!component) {
  108. component = new GeometryBufferRendererSceneComponent(scene);
  109. scene._addComponent(component);
  110. }
  111. // Render target
  112. this._createRenderTargets();
  113. }
  114. /**
  115. * Checks wether everything is ready to render a submesh to the G buffer.
  116. * @param subMesh the submesh to check readiness for
  117. * @param useInstances is the mesh drawn using instance or not
  118. * @returns true if ready otherwise false
  119. */
  120. public isReady(subMesh: SubMesh, useInstances: boolean): boolean {
  121. var material: any = subMesh.getMaterial();
  122. if (material && material.disableDepthWrite) {
  123. return false;
  124. }
  125. var defines = [];
  126. var attribs = [VertexBuffer.PositionKind, VertexBuffer.NormalKind];
  127. var mesh = subMesh.getMesh();
  128. // Alpha test
  129. if (material && material.needAlphaTesting()) {
  130. defines.push("#define ALPHATEST");
  131. if (mesh.isVerticesDataPresent(VertexBuffer.UVKind)) {
  132. attribs.push(VertexBuffer.UVKind);
  133. defines.push("#define UV1");
  134. }
  135. if (mesh.isVerticesDataPresent(VertexBuffer.UV2Kind)) {
  136. attribs.push(VertexBuffer.UV2Kind);
  137. defines.push("#define UV2");
  138. }
  139. }
  140. // Buffers
  141. if (this._enablePosition) {
  142. defines.push("#define POSITION");
  143. defines.push("#define POSITION_INDEX " + this._positionIndex);
  144. }
  145. if (this._enableVelocity) {
  146. defines.push("#define VELOCITY");
  147. defines.push("#define VELOCITY_INDEX " + this._velocityIndex);
  148. }
  149. // Bones
  150. if (mesh.useBones && mesh.computeBonesUsingShaders) {
  151. attribs.push(VertexBuffer.MatricesIndicesKind);
  152. attribs.push(VertexBuffer.MatricesWeightsKind);
  153. if (mesh.numBoneInfluencers > 4) {
  154. attribs.push(VertexBuffer.MatricesIndicesExtraKind);
  155. attribs.push(VertexBuffer.MatricesWeightsExtraKind);
  156. }
  157. defines.push("#define NUM_BONE_INFLUENCERS " + mesh.numBoneInfluencers);
  158. defines.push("#define BonesPerMesh " + (mesh.skeleton ? mesh.skeleton.bones.length + 1 : 0));
  159. } else {
  160. defines.push("#define NUM_BONE_INFLUENCERS 0");
  161. }
  162. // Instances
  163. if (useInstances) {
  164. defines.push("#define INSTANCES");
  165. attribs.push("world0");
  166. attribs.push("world1");
  167. attribs.push("world2");
  168. attribs.push("world3");
  169. }
  170. // Setup textures count
  171. defines.push("#define RENDER_TARGET_COUNT " + this._multiRenderTarget.textures.length);
  172. // Get correct effect
  173. var join = defines.join("\n");
  174. if (this._cachedDefines !== join) {
  175. this._cachedDefines = join;
  176. this._effect = this._scene.getEngine().createEffect("geometry",
  177. attribs,
  178. ["world", "mBones", "viewProjection", "diffuseMatrix", "view", "previousWorldViewProjection"],
  179. ["diffuseSampler"], join,
  180. undefined, undefined, undefined,
  181. { buffersCount: this._enablePosition ? 3 : 2 });
  182. }
  183. return this._effect.isReady();
  184. }
  185. /**
  186. * Gets the current underlying G Buffer.
  187. * @returns the buffer
  188. */
  189. public getGBuffer(): MultiRenderTarget {
  190. return this._multiRenderTarget;
  191. }
  192. /**
  193. * Gets the number of samples used to render the buffer (anti aliasing).
  194. */
  195. public get samples(): number {
  196. return this._multiRenderTarget.samples;
  197. }
  198. /**
  199. * Sets the number of samples used to render the buffer (anti aliasing).
  200. */
  201. public set samples(value: number) {
  202. this._multiRenderTarget.samples = value;
  203. }
  204. /**
  205. * Disposes the renderer and frees up associated resources.
  206. */
  207. public dispose(): void {
  208. this.getGBuffer().dispose();
  209. }
  210. protected _createRenderTargets(): void {
  211. var engine = this._scene.getEngine();
  212. var count = 2;
  213. if (this._enablePosition) {
  214. this._positionIndex = count;
  215. count++;
  216. }
  217. if (this._enableVelocity) {
  218. this._velocityIndex = count;
  219. count++;
  220. }
  221. this._multiRenderTarget = new MultiRenderTarget("gBuffer",
  222. { width: engine.getRenderWidth() * this._ratio, height: engine.getRenderHeight() * this._ratio }, count, this._scene,
  223. { generateMipMaps: false, generateDepthTexture: true, defaultType: Engine.TEXTURETYPE_FLOAT });
  224. if (!this.isSupported) {
  225. return;
  226. }
  227. this._multiRenderTarget.wrapU = Texture.CLAMP_ADDRESSMODE;
  228. this._multiRenderTarget.wrapV = Texture.CLAMP_ADDRESSMODE;
  229. this._multiRenderTarget.refreshRate = 1;
  230. this._multiRenderTarget.renderParticles = false;
  231. this._multiRenderTarget.renderList = null;
  232. // set default depth value to 1.0 (far away)
  233. this._multiRenderTarget.onClearObservable.add((engine: Engine) => {
  234. engine.clear(new Color4(0.0, 0.0, 0.0, 1.0), true, true, true);
  235. });
  236. // Custom render function
  237. var renderSubMesh = (subMesh: SubMesh): void => {
  238. var mesh = subMesh.getRenderingMesh();
  239. var scene = this._scene;
  240. var engine = scene.getEngine();
  241. let material = subMesh.getMaterial();
  242. if (!material) {
  243. return;
  244. }
  245. // Velocity
  246. if (!this._previousTransformationMatrices[mesh.uniqueId]) {
  247. this._previousTransformationMatrices[mesh.uniqueId] = Matrix.Identity();
  248. }
  249. // Culling
  250. engine.setState(material.backFaceCulling, 0, false, scene.useRightHandedSystem);
  251. // Managing instances
  252. var batch = mesh._getInstancesRenderList(subMesh._id);
  253. if (batch.mustReturn) {
  254. return;
  255. }
  256. var hardwareInstancedRendering = (engine.getCaps().instancedArrays) && (batch.visibleInstances[subMesh._id] !== null);
  257. if (this.isReady(subMesh, hardwareInstancedRendering)) {
  258. engine.enableEffect(this._effect);
  259. mesh._bind(subMesh, this._effect, Material.TriangleFillMode);
  260. this._effect.setMatrix("viewProjection", scene.getTransformMatrix());
  261. this._effect.setMatrix("view", scene.getViewMatrix());
  262. // Alpha test
  263. if (material && material.needAlphaTesting()) {
  264. var alphaTexture = material.getAlphaTestTexture();
  265. if (alphaTexture) {
  266. this._effect.setTexture("diffuseSampler", alphaTexture);
  267. this._effect.setMatrix("diffuseMatrix", alphaTexture.getTextureMatrix());
  268. }
  269. }
  270. // Bones
  271. if (mesh.useBones && mesh.computeBonesUsingShaders && mesh.skeleton) {
  272. this._effect.setMatrices("mBones", mesh.skeleton.getTransformMatrices(mesh));
  273. }
  274. // Velocity
  275. this._effect.setMatrix("previousWorldViewProjection", this._previousTransformationMatrices[mesh.uniqueId]);
  276. // Draw
  277. mesh._processRendering(subMesh, this._effect, Material.TriangleFillMode, batch, hardwareInstancedRendering,
  278. (isInstance, world) => this._effect.setMatrix("world", world));
  279. }
  280. // Velocity
  281. this._previousTransformationMatrices[mesh.uniqueId] = mesh.getWorldMatrix().multiply(this._scene.getTransformMatrix());
  282. };
  283. this._multiRenderTarget.customRenderFunction = (opaqueSubMeshes: SmartArray<SubMesh>, alphaTestSubMeshes: SmartArray<SubMesh>, transparentSubMeshes: SmartArray<SubMesh>, depthOnlySubMeshes: SmartArray<SubMesh>): void => {
  284. var index;
  285. if (depthOnlySubMeshes.length) {
  286. engine.setColorWrite(false);
  287. for (index = 0; index < depthOnlySubMeshes.length; index++) {
  288. renderSubMesh(depthOnlySubMeshes.data[index]);
  289. }
  290. engine.setColorWrite(true);
  291. }
  292. for (index = 0; index < opaqueSubMeshes.length; index++) {
  293. renderSubMesh(opaqueSubMeshes.data[index]);
  294. }
  295. for (index = 0; index < alphaTestSubMeshes.length; index++) {
  296. renderSubMesh(alphaTestSubMeshes.data[index]);
  297. }
  298. };
  299. }
  300. }
  301. }