fluidRenderingTextures.js 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261
  1. import { Texture } from "../../Materials/Textures/texture.js";
  2. import { Vector2 } from "../../Maths/math.vector.js";
  3. import { PostProcess } from "../../PostProcesses/postProcess.js";
  4. import { Observable } from "../../Misc/observable.js";
  5. /** @internal */
  6. export class FluidRenderingTextures {
  7. get blurNumIterations() {
  8. return this._blurNumIterations;
  9. }
  10. set blurNumIterations(numIterations) {
  11. if (this._blurNumIterations === numIterations) {
  12. return;
  13. }
  14. this._blurNumIterations = numIterations;
  15. if (this._blurPostProcesses !== null) {
  16. const blurX = this._blurPostProcesses[0];
  17. const blurY = this._blurPostProcesses[1];
  18. this._blurPostProcesses = [];
  19. for (let i = 0; i < this._blurNumIterations * 2; ++i) {
  20. this._blurPostProcesses[i] = i & 1 ? blurY : blurX;
  21. }
  22. }
  23. }
  24. get renderTarget() {
  25. return this._rt;
  26. }
  27. get renderTargetBlur() {
  28. return this._rtBlur;
  29. }
  30. get texture() {
  31. return this._texture;
  32. }
  33. get textureBlur() {
  34. return this._textureBlurred;
  35. }
  36. constructor(name, scene, width, height, blurTextureSizeX, blurTextureSizeY, textureType = 1, textureFormat = 6, blurTextureType = 1, blurTextureFormat = 6, useStandardBlur = false, camera = null, generateDepthBuffer = true, samples = 1) {
  37. this.enableBlur = true;
  38. this.blurSizeDivisor = 1;
  39. this.blurFilterSize = 7;
  40. this._blurNumIterations = 3;
  41. this.blurMaxFilterSize = 100;
  42. this.blurDepthScale = 10;
  43. this.particleSize = 0.02;
  44. this.onDisposeObservable = new Observable();
  45. this._name = name;
  46. this._scene = scene;
  47. this._camera = camera;
  48. this._engine = scene.getEngine();
  49. this._width = width;
  50. this._height = height;
  51. this._blurTextureSizeX = blurTextureSizeX;
  52. this._blurTextureSizeY = blurTextureSizeY;
  53. this._textureType = textureType;
  54. this._textureFormat = textureFormat;
  55. this._blurTextureType = blurTextureType;
  56. this._blurTextureFormat = blurTextureFormat;
  57. this._useStandardBlur = useStandardBlur;
  58. this._generateDepthBuffer = generateDepthBuffer;
  59. this._samples = samples;
  60. this._postProcessRunningIndex = 0;
  61. this.enableBlur = blurTextureSizeX !== 0 && blurTextureSizeY !== 0;
  62. this._rt = null;
  63. this._texture = null;
  64. this._rtBlur = null;
  65. this._textureBlurred = null;
  66. this._blurPostProcesses = null;
  67. }
  68. initialize() {
  69. this.dispose();
  70. this._createRenderTarget();
  71. if (this.enableBlur && this._texture) {
  72. const [rtBlur, textureBlurred, blurPostProcesses] = this._createBlurPostProcesses(this._texture, this._blurTextureType, this._blurTextureFormat, this.blurSizeDivisor, this._name, this._useStandardBlur);
  73. this._rtBlur = rtBlur;
  74. this._textureBlurred = textureBlurred;
  75. this._blurPostProcesses = blurPostProcesses;
  76. }
  77. }
  78. applyBlurPostProcesses() {
  79. if (this.enableBlur && this._blurPostProcesses) {
  80. this._postProcessRunningIndex = 0;
  81. this._scene.postProcessManager.directRender(this._blurPostProcesses, this._rtBlur, true);
  82. this._engine.unBindFramebuffer(this._rtBlur);
  83. }
  84. }
  85. _createRenderTarget() {
  86. this._rt = this._engine.createRenderTargetTexture({ width: this._width, height: this._height }, {
  87. generateMipMaps: false,
  88. type: this._textureType,
  89. format: this._textureFormat,
  90. samplingMode: 1,
  91. generateDepthBuffer: this._generateDepthBuffer,
  92. generateStencilBuffer: false,
  93. samples: this._samples,
  94. label: `FluidRenderingRTT-${this._name}`,
  95. });
  96. const renderTexture = this._rt.texture;
  97. renderTexture.incrementReferences();
  98. this._texture = new Texture(null, this._scene);
  99. this._texture.name = "rtt" + this._name;
  100. this._texture._texture = renderTexture;
  101. this._texture.wrapU = Texture.CLAMP_ADDRESSMODE;
  102. this._texture.wrapV = Texture.CLAMP_ADDRESSMODE;
  103. this._texture.anisotropicFilteringLevel = 1;
  104. }
  105. _createBlurPostProcesses(textureBlurSource, textureType, textureFormat, blurSizeDivisor, debugName, useStandardBlur = false) {
  106. const engine = this._scene.getEngine();
  107. const targetSize = new Vector2(Math.floor(this._blurTextureSizeX / blurSizeDivisor), Math.floor(this._blurTextureSizeY / blurSizeDivisor));
  108. const useBilinearFiltering = (textureType === 1 && engine.getCaps().textureFloatLinearFiltering) ||
  109. (textureType === 2 && engine.getCaps().textureHalfFloatLinearFiltering);
  110. const rtBlur = this._engine.createRenderTargetTexture({ width: targetSize.x, height: targetSize.y }, {
  111. generateMipMaps: false,
  112. type: textureType,
  113. format: textureFormat,
  114. samplingMode: useBilinearFiltering ? 2 : 1,
  115. generateDepthBuffer: false,
  116. generateStencilBuffer: false,
  117. samples: this._samples,
  118. label: `FluidRenderingRTTBlur-${debugName}`,
  119. });
  120. const renderTexture = rtBlur.texture;
  121. renderTexture.incrementReferences();
  122. const texture = new Texture(null, this._scene);
  123. texture.name = "rttBlurred" + debugName;
  124. texture._texture = renderTexture;
  125. texture.wrapU = Texture.CLAMP_ADDRESSMODE;
  126. texture.wrapV = Texture.CLAMP_ADDRESSMODE;
  127. texture.anisotropicFilteringLevel = 1;
  128. if (useStandardBlur) {
  129. const kernelBlurXPostprocess = new PostProcess("BilateralBlurX", "fluidRenderingStandardBlur", ["filterSize", "blurDir"], null, 1, null, 1, engine, true, null, textureType, undefined, undefined, undefined, textureFormat);
  130. kernelBlurXPostprocess.samples = this._samples;
  131. kernelBlurXPostprocess.externalTextureSamplerBinding = true;
  132. kernelBlurXPostprocess.onApplyObservable.add((effect) => {
  133. if (this._postProcessRunningIndex === 0) {
  134. effect.setTexture("textureSampler", textureBlurSource);
  135. }
  136. else {
  137. effect._bindTexture("textureSampler", kernelBlurXPostprocess.inputTexture.texture);
  138. }
  139. effect.setInt("filterSize", this.blurFilterSize);
  140. effect.setFloat2("blurDir", 1 / this._blurTextureSizeX, 0);
  141. this._postProcessRunningIndex++;
  142. });
  143. kernelBlurXPostprocess.onSizeChangedObservable.add(() => {
  144. kernelBlurXPostprocess._textures.forEach((rt) => {
  145. rt.texture.wrapU = Texture.CLAMP_ADDRESSMODE;
  146. rt.texture.wrapV = Texture.CLAMP_ADDRESSMODE;
  147. });
  148. });
  149. this._fixReusablePostProcess(kernelBlurXPostprocess);
  150. const kernelBlurYPostprocess = new PostProcess("BilateralBlurY", "fluidRenderingStandardBlur", ["filterSize", "blurDir"], null, 1, null, 1, engine, true, null, textureType, undefined, undefined, undefined, textureFormat);
  151. kernelBlurYPostprocess.samples = this._samples;
  152. kernelBlurYPostprocess.onApplyObservable.add((effect) => {
  153. effect.setInt("filterSize", this.blurFilterSize);
  154. effect.setFloat2("blurDir", 0, 1 / this._blurTextureSizeY);
  155. this._postProcessRunningIndex++;
  156. });
  157. kernelBlurYPostprocess.onSizeChangedObservable.add(() => {
  158. kernelBlurYPostprocess._textures.forEach((rt) => {
  159. rt.texture.wrapU = Texture.CLAMP_ADDRESSMODE;
  160. rt.texture.wrapV = Texture.CLAMP_ADDRESSMODE;
  161. });
  162. });
  163. this._fixReusablePostProcess(kernelBlurYPostprocess);
  164. kernelBlurXPostprocess.autoClear = false;
  165. kernelBlurYPostprocess.autoClear = false;
  166. const blurList = [];
  167. for (let i = 0; i < this._blurNumIterations * 2; ++i) {
  168. blurList[i] = i & 1 ? kernelBlurYPostprocess : kernelBlurXPostprocess;
  169. }
  170. return [rtBlur, texture, blurList];
  171. }
  172. else {
  173. const uniforms = ["maxFilterSize", "blurDir", "projectedParticleConstant", "depthThreshold"];
  174. const kernelBlurXPostprocess = new PostProcess("BilateralBlurX", "fluidRenderingBilateralBlur", uniforms, null, 1, null, 1, engine, true, null, textureType, undefined, undefined, undefined, textureFormat);
  175. kernelBlurXPostprocess.samples = this._samples;
  176. kernelBlurXPostprocess.externalTextureSamplerBinding = true;
  177. kernelBlurXPostprocess.onApplyObservable.add((effect) => {
  178. if (this._postProcessRunningIndex === 0) {
  179. effect.setTexture("textureSampler", textureBlurSource);
  180. }
  181. else {
  182. effect._bindTexture("textureSampler", kernelBlurXPostprocess.inputTexture.texture);
  183. }
  184. effect.setInt("maxFilterSize", this.blurMaxFilterSize);
  185. effect.setFloat2("blurDir", 1 / this._blurTextureSizeX, 0);
  186. effect.setFloat("projectedParticleConstant", this._getProjectedParticleConstant());
  187. effect.setFloat("depthThreshold", this._getDepthThreshold());
  188. this._postProcessRunningIndex++;
  189. });
  190. kernelBlurXPostprocess.onSizeChangedObservable.add(() => {
  191. kernelBlurXPostprocess._textures.forEach((rt) => {
  192. rt.texture.wrapU = Texture.CLAMP_ADDRESSMODE;
  193. rt.texture.wrapV = Texture.CLAMP_ADDRESSMODE;
  194. });
  195. });
  196. this._fixReusablePostProcess(kernelBlurXPostprocess);
  197. const kernelBlurYPostprocess = new PostProcess("BilateralBlurY", "fluidRenderingBilateralBlur", uniforms, null, 1, null, 1, engine, true, null, textureType, undefined, undefined, undefined, textureFormat);
  198. kernelBlurYPostprocess.samples = this._samples;
  199. kernelBlurYPostprocess.onApplyObservable.add((effect) => {
  200. effect.setInt("maxFilterSize", this.blurMaxFilterSize);
  201. effect.setFloat2("blurDir", 0, 1 / this._blurTextureSizeY);
  202. effect.setFloat("projectedParticleConstant", this._getProjectedParticleConstant());
  203. effect.setFloat("depthThreshold", this._getDepthThreshold());
  204. this._postProcessRunningIndex++;
  205. });
  206. kernelBlurYPostprocess.onSizeChangedObservable.add(() => {
  207. kernelBlurYPostprocess._textures.forEach((rt) => {
  208. rt.texture.wrapU = Texture.CLAMP_ADDRESSMODE;
  209. rt.texture.wrapV = Texture.CLAMP_ADDRESSMODE;
  210. });
  211. });
  212. this._fixReusablePostProcess(kernelBlurYPostprocess);
  213. kernelBlurXPostprocess.autoClear = false;
  214. kernelBlurYPostprocess.autoClear = false;
  215. const blurList = [];
  216. for (let i = 0; i < this._blurNumIterations * 2; ++i) {
  217. blurList[i] = i & 1 ? kernelBlurYPostprocess : kernelBlurXPostprocess;
  218. }
  219. return [rtBlur, texture, blurList];
  220. }
  221. }
  222. _fixReusablePostProcess(pp) {
  223. if (!pp.isReusable()) {
  224. return;
  225. }
  226. pp.onActivateObservable.add(() => {
  227. // undo what calling activate() does which will make sure we will retrieve the right texture when getting the input for the post process
  228. pp._currentRenderTextureInd = (pp._currentRenderTextureInd + 1) % 2;
  229. });
  230. pp.onApplyObservable.add(() => {
  231. // now we can advance to the next texture
  232. pp._currentRenderTextureInd = (pp._currentRenderTextureInd + 1) % 2;
  233. });
  234. }
  235. _getProjectedParticleConstant() {
  236. return (this.blurFilterSize * this.particleSize * 0.05 * (this._height / 2)) / Math.tan((this._camera?.fov ?? (45 * Math.PI) / 180) / 2);
  237. }
  238. _getDepthThreshold() {
  239. return (this.particleSize / 2) * this.blurDepthScale;
  240. }
  241. dispose() {
  242. if (this.onDisposeObservable.hasObservers()) {
  243. this.onDisposeObservable.notifyObservers(this);
  244. }
  245. this._rt?.dispose();
  246. this._rt = null;
  247. this._texture?.dispose();
  248. this._texture = null;
  249. this._rtBlur?.dispose();
  250. this._rtBlur = null;
  251. this._textureBlurred?.dispose();
  252. this._textureBlurred = null;
  253. if (this._blurPostProcesses) {
  254. this._blurPostProcesses[0].dispose();
  255. this._blurPostProcesses[1].dispose();
  256. }
  257. this._blurPostProcesses = null;
  258. }
  259. }
  260. //# sourceMappingURL=fluidRenderingTextures.js.map