motionBlurPostProcess.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276
  1. import { __decorate } from "../tslib.es6.js";
  2. import { Logger } from "../Misc/logger.js";
  3. import { Matrix, TmpVectors, Vector2 } from "../Maths/math.vector.js";
  4. import { PostProcess } from "./postProcess.js";
  5. import { GeometryBufferRenderer } from "../Rendering/geometryBufferRenderer.js";
  6. import { MotionBlurConfiguration } from "../Rendering/motionBlurConfiguration.js";
  7. import "../Animations/animatable.js";
  8. import "../Rendering/geometryBufferRendererSceneComponent.js";
  9. import "../Shaders/motionBlur.fragment.js";
  10. import { serialize } from "../Misc/decorators.js";
  11. import { SerializationHelper } from "../Misc/decorators.serialization.js";
  12. import { RegisterClass } from "../Misc/typeStore.js";
  13. /**
  14. * The Motion Blur Post Process which blurs an image based on the objects velocity in scene.
  15. * Velocity can be affected by each object's rotation, position and scale depending on the transformation speed.
  16. * As an example, all you have to do is to create the post-process:
  17. * var mb = new BABYLON.MotionBlurPostProcess(
  18. * 'mb', // The name of the effect.
  19. * scene, // The scene containing the objects to blur according to their velocity.
  20. * 1.0, // The required width/height ratio to downsize to before computing the render pass.
  21. * camera // The camera to apply the render pass to.
  22. * );
  23. * Then, all objects moving, rotating and/or scaling will be blurred depending on the transformation speed.
  24. */
  25. export class MotionBlurPostProcess extends PostProcess {
  26. /**
  27. * Gets the number of iterations are used for motion blur quality. Default value is equal to 32
  28. */
  29. get motionBlurSamples() {
  30. return this._motionBlurSamples;
  31. }
  32. /**
  33. * Sets the number of iterations to be used for motion blur quality
  34. */
  35. set motionBlurSamples(samples) {
  36. this._motionBlurSamples = samples;
  37. this._updateEffect();
  38. }
  39. /**
  40. * Gets whether or not the motion blur post-process is in object based mode.
  41. */
  42. get isObjectBased() {
  43. return this._isObjectBased;
  44. }
  45. /**
  46. * Sets whether or not the motion blur post-process is in object based mode.
  47. */
  48. set isObjectBased(value) {
  49. if (this._isObjectBased === value) {
  50. return;
  51. }
  52. this._isObjectBased = value;
  53. this._applyMode();
  54. }
  55. get _geometryBufferRenderer() {
  56. if (!this._forceGeometryBuffer) {
  57. return null;
  58. }
  59. return this._scene.geometryBufferRenderer;
  60. }
  61. get _prePassRenderer() {
  62. if (this._forceGeometryBuffer) {
  63. return null;
  64. }
  65. return this._scene.prePassRenderer;
  66. }
  67. /**
  68. * Gets a string identifying the name of the class
  69. * @returns "MotionBlurPostProcess" string
  70. */
  71. getClassName() {
  72. return "MotionBlurPostProcess";
  73. }
  74. /**
  75. * Creates a new instance MotionBlurPostProcess
  76. * @param name The name of the effect.
  77. * @param scene The scene containing the objects to blur according to their velocity.
  78. * @param options The required width/height ratio to downsize to before computing the render pass.
  79. * @param camera The camera to apply the render pass to.
  80. * @param samplingMode The sampling mode to be used when computing the pass. (default: 0)
  81. * @param engine The engine which the post process will be applied. (default: current engine)
  82. * @param reusable If the post process can be reused on the same frame. (default: false)
  83. * @param textureType Type of textures used when performing the post process. (default: 0)
  84. * @param blockCompilation If compilation of the shader should not be done in the constructor. The updateEffect method can be used to compile the shader at a later time. (default: true)
  85. * @param forceGeometryBuffer If this post process should use geometry buffer instead of prepass (default: false)
  86. */
  87. constructor(name, scene, options, camera, samplingMode, engine, reusable, textureType = 0, blockCompilation = false, forceGeometryBuffer = false) {
  88. super(name, "motionBlur", ["motionStrength", "motionScale", "screenSize", "inverseViewProjection", "prevViewProjection", "projection"], ["velocitySampler", "depthSampler"], options, camera, samplingMode, engine, reusable, "#define GEOMETRY_SUPPORTED\n#define SAMPLES 64.0\n#define OBJECT_BASED", textureType, undefined, null, blockCompilation);
  89. /**
  90. * Defines how much the image is blurred by the movement. Default value is equal to 1
  91. */
  92. this.motionStrength = 1;
  93. this._motionBlurSamples = 32;
  94. this._isObjectBased = true;
  95. this._forceGeometryBuffer = false;
  96. this._invViewProjection = null;
  97. this._previousViewProjection = null;
  98. this._forceGeometryBuffer = forceGeometryBuffer;
  99. // Set up assets
  100. if (this._forceGeometryBuffer) {
  101. scene.enableGeometryBufferRenderer();
  102. if (this._geometryBufferRenderer) {
  103. this._geometryBufferRenderer.enableVelocity = this._isObjectBased;
  104. }
  105. }
  106. else {
  107. scene.enablePrePassRenderer();
  108. if (this._prePassRenderer) {
  109. this._prePassRenderer.markAsDirty();
  110. this._prePassEffectConfiguration = new MotionBlurConfiguration();
  111. }
  112. }
  113. this._applyMode();
  114. }
  115. /**
  116. * Excludes the given skinned mesh from computing bones velocities.
  117. * Computing bones velocities can have a cost and that cost. The cost can be saved by calling this function and by passing the skinned mesh reference to ignore.
  118. * @param skinnedMesh The mesh containing the skeleton to ignore when computing the velocity map.
  119. */
  120. excludeSkinnedMesh(skinnedMesh) {
  121. if (skinnedMesh.skeleton) {
  122. let list;
  123. if (this._geometryBufferRenderer) {
  124. list = this._geometryBufferRenderer.excludedSkinnedMeshesFromVelocity;
  125. }
  126. else if (this._prePassRenderer) {
  127. list = this._prePassRenderer.excludedSkinnedMesh;
  128. }
  129. else {
  130. return;
  131. }
  132. list.push(skinnedMesh);
  133. }
  134. }
  135. /**
  136. * Removes the given skinned mesh from the excluded meshes to integrate bones velocities while rendering the velocity map.
  137. * @param skinnedMesh The mesh containing the skeleton that has been ignored previously.
  138. * @see excludeSkinnedMesh to exclude a skinned mesh from bones velocity computation.
  139. */
  140. removeExcludedSkinnedMesh(skinnedMesh) {
  141. if (skinnedMesh.skeleton) {
  142. let list;
  143. if (this._geometryBufferRenderer) {
  144. list = this._geometryBufferRenderer.excludedSkinnedMeshesFromVelocity;
  145. }
  146. else if (this._prePassRenderer) {
  147. list = this._prePassRenderer.excludedSkinnedMesh;
  148. }
  149. else {
  150. return;
  151. }
  152. const index = list.indexOf(skinnedMesh);
  153. if (index !== -1) {
  154. list.splice(index, 1);
  155. }
  156. }
  157. }
  158. /**
  159. * Disposes the post process.
  160. * @param camera The camera to dispose the post process on.
  161. */
  162. dispose(camera) {
  163. if (this._geometryBufferRenderer) {
  164. // Clear previous transformation matrices dictionary used to compute objects velocities
  165. this._geometryBufferRenderer._previousTransformationMatrices = {};
  166. this._geometryBufferRenderer._previousBonesTransformationMatrices = {};
  167. this._geometryBufferRenderer.excludedSkinnedMeshesFromVelocity = [];
  168. }
  169. super.dispose(camera);
  170. }
  171. /**
  172. * Called on the mode changed (object based or screen based).
  173. * @returns void
  174. */
  175. _applyMode() {
  176. if (!this._geometryBufferRenderer && !this._prePassRenderer) {
  177. // We can't get a velocity or depth texture. So, work as a passthrough.
  178. Logger.Warn("Multiple Render Target support needed to compute object based motion blur");
  179. return this.updateEffect();
  180. }
  181. if (this._geometryBufferRenderer) {
  182. this._geometryBufferRenderer.enableVelocity = this._isObjectBased;
  183. }
  184. this._updateEffect();
  185. this._invViewProjection = null;
  186. this._previousViewProjection = null;
  187. if (this.isObjectBased) {
  188. if (this._prePassRenderer && this._prePassEffectConfiguration) {
  189. this._prePassEffectConfiguration.texturesRequired[0] = 2;
  190. }
  191. this.onApply = (effect) => this._onApplyObjectBased(effect);
  192. }
  193. else {
  194. this._invViewProjection = Matrix.Identity();
  195. this._previousViewProjection = this._scene.getTransformMatrix().clone();
  196. if (this._prePassRenderer && this._prePassEffectConfiguration) {
  197. this._prePassEffectConfiguration.texturesRequired[0] = 5;
  198. }
  199. this.onApply = (effect) => this._onApplyScreenBased(effect);
  200. }
  201. }
  202. /**
  203. * Called on the effect is applied when the motion blur post-process is in object based mode.
  204. * @param effect
  205. */
  206. _onApplyObjectBased(effect) {
  207. effect.setVector2("screenSize", new Vector2(this.width, this.height));
  208. effect.setFloat("motionScale", this._scene.getAnimationRatio());
  209. effect.setFloat("motionStrength", this.motionStrength);
  210. if (this._geometryBufferRenderer) {
  211. const velocityIndex = this._geometryBufferRenderer.getTextureIndex(GeometryBufferRenderer.VELOCITY_TEXTURE_TYPE);
  212. effect.setTexture("velocitySampler", this._geometryBufferRenderer.getGBuffer().textures[velocityIndex]);
  213. }
  214. else if (this._prePassRenderer) {
  215. const velocityIndex = this._prePassRenderer.getIndex(2);
  216. effect.setTexture("velocitySampler", this._prePassRenderer.getRenderTarget().textures[velocityIndex]);
  217. }
  218. }
  219. /**
  220. * Called on the effect is applied when the motion blur post-process is in screen based mode.
  221. * @param effect
  222. */
  223. _onApplyScreenBased(effect) {
  224. const viewProjection = TmpVectors.Matrix[0];
  225. viewProjection.copyFrom(this._scene.getTransformMatrix());
  226. viewProjection.invertToRef(this._invViewProjection);
  227. effect.setMatrix("inverseViewProjection", this._invViewProjection);
  228. effect.setMatrix("prevViewProjection", this._previousViewProjection);
  229. this._previousViewProjection.copyFrom(viewProjection);
  230. effect.setMatrix("projection", this._scene.getProjectionMatrix());
  231. effect.setVector2("screenSize", new Vector2(this.width, this.height));
  232. effect.setFloat("motionScale", this._scene.getAnimationRatio());
  233. effect.setFloat("motionStrength", this.motionStrength);
  234. if (this._geometryBufferRenderer) {
  235. const depthIndex = this._geometryBufferRenderer.getTextureIndex(GeometryBufferRenderer.DEPTH_TEXTURE_TYPE);
  236. effect.setTexture("depthSampler", this._geometryBufferRenderer.getGBuffer().textures[depthIndex]);
  237. }
  238. else if (this._prePassRenderer) {
  239. const depthIndex = this._prePassRenderer.getIndex(5);
  240. effect.setTexture("depthSampler", this._prePassRenderer.getRenderTarget().textures[depthIndex]);
  241. }
  242. }
  243. /**
  244. * Called on the effect must be updated (changed mode, samples count, etc.).
  245. */
  246. _updateEffect() {
  247. if (this._geometryBufferRenderer || this._prePassRenderer) {
  248. const defines = [
  249. "#define GEOMETRY_SUPPORTED",
  250. "#define SAMPLES " + this._motionBlurSamples.toFixed(1),
  251. this._isObjectBased ? "#define OBJECT_BASED" : "#define SCREEN_BASED",
  252. ];
  253. this.updateEffect(defines.join("\n"));
  254. }
  255. }
  256. /**
  257. * @internal
  258. */
  259. static _Parse(parsedPostProcess, targetCamera, scene, rootUrl) {
  260. return SerializationHelper.Parse(() => {
  261. return new MotionBlurPostProcess(parsedPostProcess.name, scene, parsedPostProcess.options, targetCamera, parsedPostProcess.renderTargetSamplingMode, scene.getEngine(), parsedPostProcess.reusable, parsedPostProcess.textureType, false);
  262. }, parsedPostProcess, scene, rootUrl);
  263. }
  264. }
  265. __decorate([
  266. serialize()
  267. ], MotionBlurPostProcess.prototype, "motionStrength", void 0);
  268. __decorate([
  269. serialize()
  270. ], MotionBlurPostProcess.prototype, "motionBlurSamples", null);
  271. __decorate([
  272. serialize()
  273. ], MotionBlurPostProcess.prototype, "isObjectBased", null);
  274. RegisterClass("BABYLON.MotionBlurPostProcess", MotionBlurPostProcess);
  275. //# sourceMappingURL=motionBlurPostProcess.js.map