depthRenderer.js 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388
  1. import { Color4 } from "../Maths/math.color.js";
  2. import { VertexBuffer } from "../Buffers/buffer.js";
  3. import { Texture } from "../Materials/Textures/texture.js";
  4. import { RenderTargetTexture } from "../Materials/Textures/renderTargetTexture.js";
  5. import { Camera } from "../Cameras/camera.js";
  6. import "../Shaders/depth.fragment.js";
  7. import "../Shaders/depth.vertex.js";
  8. import { _WarnImport } from "../Misc/devTools.js";
  9. import { addClipPlaneUniforms, bindClipPlane, prepareStringDefinesForClipPlanes } from "../Materials/clipPlaneMaterialHelper.js";
  10. import { BindMorphTargetParameters, PrepareAttributesForMorphTargetsInfluencers, PushAttributesForInstances } from "../Materials/materialHelper.functions.js";
  11. /**
  12. * This represents a depth renderer in Babylon.
  13. * A depth renderer will render to it's depth map every frame which can be displayed or used in post processing
  14. */
  15. export class DepthRenderer {
  16. /**
  17. * Sets a specific material to be used to render a mesh/a list of meshes by the depth renderer
  18. * @param mesh mesh or array of meshes
  19. * @param material material to use by the depth render when rendering the mesh(es). If undefined is passed, the specific material created by the depth renderer will be used.
  20. */
  21. setMaterialForRendering(mesh, material) {
  22. this._depthMap.setMaterialForRendering(mesh, material);
  23. }
  24. /**
  25. * Instantiates a depth renderer
  26. * @param scene The scene the renderer belongs to
  27. * @param type The texture type of the depth map (default: Engine.TEXTURETYPE_FLOAT)
  28. * @param camera The camera to be used to render the depth map (default: scene's active camera)
  29. * @param storeNonLinearDepth Defines whether the depth is stored linearly like in Babylon Shadows or directly like glFragCoord.z
  30. * @param samplingMode The sampling mode to be used with the render target (Linear, Nearest...) (default: TRILINEAR_SAMPLINGMODE)
  31. * @param storeCameraSpaceZ Defines whether the depth stored is the Z coordinate in camera space. If true, storeNonLinearDepth has no effect. (Default: false)
  32. * @param name Name of the render target (default: DepthRenderer)
  33. */
  34. constructor(scene, type = 1, camera = null, storeNonLinearDepth = false, samplingMode = Texture.TRILINEAR_SAMPLINGMODE, storeCameraSpaceZ = false, name) {
  35. /** Enable or disable the depth renderer. When disabled, the depth texture is not updated */
  36. this.enabled = true;
  37. /** Force writing the transparent objects into the depth map */
  38. this.forceDepthWriteTransparentMeshes = false;
  39. /**
  40. * Specifies that the depth renderer will only be used within
  41. * the camera it is created for.
  42. * This can help forcing its rendering during the camera processing.
  43. */
  44. this.useOnlyInActiveCamera = false;
  45. /** If true, reverse the culling of materials before writing to the depth texture.
  46. * So, basically, when "true", back facing instead of front facing faces are rasterized into the texture
  47. */
  48. this.reverseCulling = false;
  49. this._scene = scene;
  50. this._storeNonLinearDepth = storeNonLinearDepth;
  51. this._storeCameraSpaceZ = storeCameraSpaceZ;
  52. this.isPacked = type === 0;
  53. if (this.isPacked) {
  54. this.clearColor = new Color4(1.0, 1.0, 1.0, 1.0);
  55. }
  56. else {
  57. this.clearColor = new Color4(storeCameraSpaceZ ? 1e8 : 1.0, 0.0, 0.0, 1.0);
  58. }
  59. DepthRenderer._SceneComponentInitialization(this._scene);
  60. const engine = scene.getEngine();
  61. this._camera = camera;
  62. if (samplingMode !== Texture.NEAREST_SAMPLINGMODE) {
  63. if (type === 1 && !engine._caps.textureFloatLinearFiltering) {
  64. samplingMode = Texture.NEAREST_SAMPLINGMODE;
  65. }
  66. if (type === 2 && !engine._caps.textureHalfFloatLinearFiltering) {
  67. samplingMode = Texture.NEAREST_SAMPLINGMODE;
  68. }
  69. }
  70. // Render target
  71. const format = this.isPacked || !engine._features.supportExtendedTextureFormats ? 5 : 6;
  72. this._depthMap = new RenderTargetTexture(name ?? "DepthRenderer", { width: engine.getRenderWidth(), height: engine.getRenderHeight() }, this._scene, false, true, type, false, samplingMode, undefined, undefined, undefined, format);
  73. this._depthMap.wrapU = Texture.CLAMP_ADDRESSMODE;
  74. this._depthMap.wrapV = Texture.CLAMP_ADDRESSMODE;
  75. this._depthMap.refreshRate = 1;
  76. this._depthMap.renderParticles = false;
  77. this._depthMap.renderList = null;
  78. this._depthMap.noPrePassRenderer = true;
  79. // Camera to get depth map from to support multiple concurrent cameras
  80. this._depthMap.activeCamera = this._camera;
  81. this._depthMap.ignoreCameraViewport = true;
  82. this._depthMap.useCameraPostProcesses = false;
  83. // set default depth value to 1.0 (far away)
  84. this._depthMap.onClearObservable.add((engine) => {
  85. engine.clear(this.clearColor, true, true, true);
  86. });
  87. this._depthMap.onBeforeBindObservable.add(() => {
  88. engine._debugPushGroup?.("depth renderer", 1);
  89. });
  90. this._depthMap.onAfterUnbindObservable.add(() => {
  91. engine._debugPopGroup?.(1);
  92. });
  93. this._depthMap.customIsReadyFunction = (mesh, refreshRate, preWarm) => {
  94. if ((preWarm || refreshRate === 0) && mesh.subMeshes) {
  95. for (let i = 0; i < mesh.subMeshes.length; ++i) {
  96. const subMesh = mesh.subMeshes[i];
  97. const renderingMesh = subMesh.getRenderingMesh();
  98. const batch = renderingMesh._getInstancesRenderList(subMesh._id, !!subMesh.getReplacementMesh());
  99. const hardwareInstancedRendering = engine.getCaps().instancedArrays &&
  100. ((batch.visibleInstances[subMesh._id] !== null && batch.visibleInstances[subMesh._id] !== undefined) || renderingMesh.hasThinInstances);
  101. if (!this.isReady(subMesh, hardwareInstancedRendering)) {
  102. return false;
  103. }
  104. }
  105. }
  106. return true;
  107. };
  108. // Custom render function
  109. const renderSubMesh = (subMesh) => {
  110. const renderingMesh = subMesh.getRenderingMesh();
  111. const effectiveMesh = subMesh.getEffectiveMesh();
  112. const scene = this._scene;
  113. const engine = scene.getEngine();
  114. const material = subMesh.getMaterial();
  115. effectiveMesh._internalAbstractMeshDataInfo._isActiveIntermediate = false;
  116. if (!material || effectiveMesh.infiniteDistance || material.disableDepthWrite || subMesh.verticesCount === 0 || subMesh._renderId === scene.getRenderId()) {
  117. return;
  118. }
  119. // Culling
  120. const detNeg = effectiveMesh._getWorldMatrixDeterminant() < 0;
  121. let sideOrientation = renderingMesh.overrideMaterialSideOrientation ?? material.sideOrientation;
  122. if (detNeg) {
  123. sideOrientation =
  124. sideOrientation === 0
  125. ? 1
  126. : 0;
  127. }
  128. const reverseSideOrientation = sideOrientation === 0;
  129. engine.setState(material.backFaceCulling, 0, false, reverseSideOrientation, this.reverseCulling ? !material.cullBackFaces : material.cullBackFaces);
  130. // Managing instances
  131. const batch = renderingMesh._getInstancesRenderList(subMesh._id, !!subMesh.getReplacementMesh());
  132. if (batch.mustReturn) {
  133. return;
  134. }
  135. const hardwareInstancedRendering = engine.getCaps().instancedArrays &&
  136. ((batch.visibleInstances[subMesh._id] !== null && batch.visibleInstances[subMesh._id] !== undefined) || renderingMesh.hasThinInstances);
  137. const camera = this._camera || scene.activeCamera;
  138. if (this.isReady(subMesh, hardwareInstancedRendering) && camera) {
  139. subMesh._renderId = scene.getRenderId();
  140. const renderingMaterial = effectiveMesh._internalAbstractMeshDataInfo._materialForRenderPass?.[engine.currentRenderPassId];
  141. let drawWrapper = subMesh._getDrawWrapper();
  142. if (!drawWrapper && renderingMaterial) {
  143. drawWrapper = renderingMaterial._getDrawWrapper();
  144. }
  145. const cameraIsOrtho = camera.mode === Camera.ORTHOGRAPHIC_CAMERA;
  146. if (!drawWrapper) {
  147. return;
  148. }
  149. const effect = drawWrapper.effect;
  150. engine.enableEffect(drawWrapper);
  151. if (!hardwareInstancedRendering) {
  152. renderingMesh._bind(subMesh, effect, material.fillMode);
  153. }
  154. if (!renderingMaterial) {
  155. effect.setMatrix("viewProjection", scene.getTransformMatrix());
  156. effect.setMatrix("world", effectiveMesh.getWorldMatrix());
  157. if (this._storeCameraSpaceZ) {
  158. effect.setMatrix("view", scene.getViewMatrix());
  159. }
  160. }
  161. else {
  162. renderingMaterial.bindForSubMesh(effectiveMesh.getWorldMatrix(), effectiveMesh, subMesh);
  163. }
  164. let minZ, maxZ;
  165. if (cameraIsOrtho) {
  166. minZ = !engine.useReverseDepthBuffer && engine.isNDCHalfZRange ? 0 : 1;
  167. maxZ = engine.useReverseDepthBuffer && engine.isNDCHalfZRange ? 0 : 1;
  168. }
  169. else {
  170. minZ = engine.useReverseDepthBuffer && engine.isNDCHalfZRange ? camera.minZ : engine.isNDCHalfZRange ? 0 : camera.minZ;
  171. maxZ = engine.useReverseDepthBuffer && engine.isNDCHalfZRange ? 0 : camera.maxZ;
  172. }
  173. effect.setFloat2("depthValues", minZ, minZ + maxZ);
  174. if (!renderingMaterial) {
  175. // Alpha test
  176. if (material.needAlphaTesting()) {
  177. const alphaTexture = material.getAlphaTestTexture();
  178. if (alphaTexture) {
  179. effect.setTexture("diffuseSampler", alphaTexture);
  180. effect.setMatrix("diffuseMatrix", alphaTexture.getTextureMatrix());
  181. }
  182. }
  183. // Bones
  184. if (renderingMesh.useBones && renderingMesh.computeBonesUsingShaders && renderingMesh.skeleton) {
  185. const skeleton = renderingMesh.skeleton;
  186. if (skeleton.isUsingTextureForMatrices) {
  187. const boneTexture = skeleton.getTransformMatrixTexture(renderingMesh);
  188. if (!boneTexture) {
  189. return;
  190. }
  191. effect.setTexture("boneSampler", boneTexture);
  192. effect.setFloat("boneTextureWidth", 4.0 * (skeleton.bones.length + 1));
  193. }
  194. else {
  195. effect.setMatrices("mBones", skeleton.getTransformMatrices(renderingMesh));
  196. }
  197. }
  198. // Clip planes
  199. bindClipPlane(effect, material, scene);
  200. // Morph targets
  201. BindMorphTargetParameters(renderingMesh, effect);
  202. if (renderingMesh.morphTargetManager && renderingMesh.morphTargetManager.isUsingTextureForTargets) {
  203. renderingMesh.morphTargetManager._bind(effect);
  204. }
  205. // Points cloud rendering
  206. if (material.pointsCloud) {
  207. effect.setFloat("pointSize", material.pointSize);
  208. }
  209. }
  210. // Draw
  211. renderingMesh._processRendering(effectiveMesh, subMesh, effect, material.fillMode, batch, hardwareInstancedRendering, (isInstance, world) => effect.setMatrix("world", world));
  212. }
  213. };
  214. this._depthMap.customRenderFunction = (opaqueSubMeshes, alphaTestSubMeshes, transparentSubMeshes, depthOnlySubMeshes) => {
  215. let index;
  216. if (depthOnlySubMeshes.length) {
  217. for (index = 0; index < depthOnlySubMeshes.length; index++) {
  218. renderSubMesh(depthOnlySubMeshes.data[index]);
  219. }
  220. }
  221. for (index = 0; index < opaqueSubMeshes.length; index++) {
  222. renderSubMesh(opaqueSubMeshes.data[index]);
  223. }
  224. for (index = 0; index < alphaTestSubMeshes.length; index++) {
  225. renderSubMesh(alphaTestSubMeshes.data[index]);
  226. }
  227. if (this.forceDepthWriteTransparentMeshes) {
  228. for (index = 0; index < transparentSubMeshes.length; index++) {
  229. renderSubMesh(transparentSubMeshes.data[index]);
  230. }
  231. }
  232. else {
  233. for (index = 0; index < transparentSubMeshes.length; index++) {
  234. transparentSubMeshes.data[index].getEffectiveMesh()._internalAbstractMeshDataInfo._isActiveIntermediate = false;
  235. }
  236. }
  237. };
  238. }
  239. /**
  240. * Creates the depth rendering effect and checks if the effect is ready.
  241. * @param subMesh The submesh to be used to render the depth map of
  242. * @param useInstances If multiple world instances should be used
  243. * @returns if the depth renderer is ready to render the depth map
  244. */
  245. isReady(subMesh, useInstances) {
  246. const engine = this._scene.getEngine();
  247. const mesh = subMesh.getMesh();
  248. const scene = mesh.getScene();
  249. const renderingMaterial = mesh._internalAbstractMeshDataInfo._materialForRenderPass?.[engine.currentRenderPassId];
  250. if (renderingMaterial) {
  251. return renderingMaterial.isReadyForSubMesh(mesh, subMesh, useInstances);
  252. }
  253. const material = subMesh.getMaterial();
  254. if (!material || material.disableDepthWrite) {
  255. return false;
  256. }
  257. const defines = [];
  258. const attribs = [VertexBuffer.PositionKind];
  259. // Alpha test
  260. if (material && material.needAlphaTesting() && material.getAlphaTestTexture()) {
  261. defines.push("#define ALPHATEST");
  262. if (mesh.isVerticesDataPresent(VertexBuffer.UVKind)) {
  263. attribs.push(VertexBuffer.UVKind);
  264. defines.push("#define UV1");
  265. }
  266. if (mesh.isVerticesDataPresent(VertexBuffer.UV2Kind)) {
  267. attribs.push(VertexBuffer.UV2Kind);
  268. defines.push("#define UV2");
  269. }
  270. }
  271. // Bones
  272. if (mesh.useBones && mesh.computeBonesUsingShaders) {
  273. attribs.push(VertexBuffer.MatricesIndicesKind);
  274. attribs.push(VertexBuffer.MatricesWeightsKind);
  275. if (mesh.numBoneInfluencers > 4) {
  276. attribs.push(VertexBuffer.MatricesIndicesExtraKind);
  277. attribs.push(VertexBuffer.MatricesWeightsExtraKind);
  278. }
  279. defines.push("#define NUM_BONE_INFLUENCERS " + mesh.numBoneInfluencers);
  280. defines.push("#define BonesPerMesh " + (mesh.skeleton ? mesh.skeleton.bones.length + 1 : 0));
  281. const skeleton = subMesh.getRenderingMesh().skeleton;
  282. if (skeleton?.isUsingTextureForMatrices) {
  283. defines.push("#define BONETEXTURE");
  284. }
  285. }
  286. else {
  287. defines.push("#define NUM_BONE_INFLUENCERS 0");
  288. }
  289. // Morph targets
  290. const morphTargetManager = mesh.morphTargetManager;
  291. let numMorphInfluencers = 0;
  292. if (morphTargetManager) {
  293. numMorphInfluencers = morphTargetManager.numMaxInfluencers || morphTargetManager.numInfluencers;
  294. if (numMorphInfluencers > 0) {
  295. defines.push("#define MORPHTARGETS");
  296. defines.push("#define NUM_MORPH_INFLUENCERS " + numMorphInfluencers);
  297. if (morphTargetManager.isUsingTextureForTargets) {
  298. defines.push("#define MORPHTARGETS_TEXTURE");
  299. }
  300. PrepareAttributesForMorphTargetsInfluencers(attribs, mesh, numMorphInfluencers);
  301. }
  302. }
  303. // Points cloud rendering
  304. if (material.pointsCloud) {
  305. defines.push("#define POINTSIZE");
  306. }
  307. // Instances
  308. if (useInstances) {
  309. defines.push("#define INSTANCES");
  310. PushAttributesForInstances(attribs);
  311. if (subMesh.getRenderingMesh().hasThinInstances) {
  312. defines.push("#define THIN_INSTANCES");
  313. }
  314. }
  315. // None linear depth
  316. if (this._storeNonLinearDepth) {
  317. defines.push("#define NONLINEARDEPTH");
  318. }
  319. // Store camera space Z coordinate instead of NDC Z
  320. if (this._storeCameraSpaceZ) {
  321. defines.push("#define STORE_CAMERASPACE_Z");
  322. }
  323. // Float Mode
  324. if (this.isPacked) {
  325. defines.push("#define PACKED");
  326. }
  327. // Clip planes
  328. prepareStringDefinesForClipPlanes(material, scene, defines);
  329. // Get correct effect
  330. const drawWrapper = subMesh._getDrawWrapper(undefined, true);
  331. const cachedDefines = drawWrapper.defines;
  332. const join = defines.join("\n");
  333. if (cachedDefines !== join) {
  334. const uniforms = [
  335. "world",
  336. "mBones",
  337. "boneTextureWidth",
  338. "pointSize",
  339. "viewProjection",
  340. "view",
  341. "diffuseMatrix",
  342. "depthValues",
  343. "morphTargetInfluences",
  344. "morphTargetCount",
  345. "morphTargetTextureInfo",
  346. "morphTargetTextureIndices",
  347. ];
  348. addClipPlaneUniforms(uniforms);
  349. drawWrapper.setEffect(engine.createEffect("depth", attribs, uniforms, ["diffuseSampler", "morphTargets", "boneSampler"], join, undefined, undefined, undefined, {
  350. maxSimultaneousMorphTargets: numMorphInfluencers,
  351. }), join);
  352. }
  353. return drawWrapper.effect.isReady();
  354. }
  355. /**
  356. * Gets the texture which the depth map will be written to.
  357. * @returns The depth map texture
  358. */
  359. getDepthMap() {
  360. return this._depthMap;
  361. }
  362. /**
  363. * Disposes of the depth renderer.
  364. */
  365. dispose() {
  366. const keysToDelete = [];
  367. for (const key in this._scene._depthRenderer) {
  368. const depthRenderer = this._scene._depthRenderer[key];
  369. if (depthRenderer === this) {
  370. keysToDelete.push(key);
  371. }
  372. }
  373. if (keysToDelete.length > 0) {
  374. this._depthMap.dispose();
  375. for (const key of keysToDelete) {
  376. delete this._scene._depthRenderer[key];
  377. }
  378. }
  379. }
  380. }
  381. /**
  382. * @internal
  383. */
  384. DepthRenderer._SceneComponentInitialization = (_) => {
  385. throw _WarnImport("DepthRendererSceneComponent");
  386. };
  387. //# sourceMappingURL=depthRenderer.js.map