mirrorTexture.ts 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282
  1. import { Observer } from "../../Misc/observable";
  2. import { Nullable } from "../../types";
  3. import { Camera } from "../../Cameras/camera";
  4. import { Scene } from "../../scene";
  5. import { Matrix, Vector3, Vector2, Plane } from "../../Maths/math";
  6. import { Texture } from "../../Materials/Textures/texture";
  7. import { RenderTargetTexture } from "../../Materials/Textures/renderTargetTexture";
  8. import { ImageProcessingConfiguration } from "../../Materials/imageProcessingConfiguration";
  9. import { BlurPostProcess } from "../../PostProcesses/blurPostProcess";
  10. import { Constants } from "../../Engines/constants";
  11. /**
  12. * Mirror texture can be used to simulate the view from a mirror in a scene.
  13. * It will dynamically be rendered every frame to adapt to the camera point of view.
  14. * You can then easily use it as a reflectionTexture on a flat surface.
  15. * In case the surface is not a plane, please consider relying on reflection probes.
  16. * @see https://doc.babylonjs.com/how_to/reflect#mirrors
  17. */
  18. export class MirrorTexture extends RenderTargetTexture {
  19. /**
  20. * Define the reflection plane we want to use. The mirrorPlane is usually set to the constructed reflector.
  21. * It is possible to directly set the mirrorPlane by directly using a Plane(a, b, c, d) where a, b and c give the plane normal vector (a, b, c) and d is a scalar displacement from the mirrorPlane to the origin. However in all but the very simplest of situations it is more straight forward to set it to the reflector as stated in the doc.
  22. * @see https://doc.babylonjs.com/how_to/reflect#mirrors
  23. */
  24. public mirrorPlane = new Plane(0, 1, 0, 1);
  25. /**
  26. * Define the blur ratio used to blur the reflection if needed.
  27. */
  28. public set blurRatio(value: number) {
  29. if (this._blurRatio === value) {
  30. return;
  31. }
  32. this._blurRatio = value;
  33. this._preparePostProcesses();
  34. }
  35. public get blurRatio(): number {
  36. return this._blurRatio;
  37. }
  38. /**
  39. * Define the adaptive blur kernel used to blur the reflection if needed.
  40. * This will autocompute the closest best match for the `blurKernel`
  41. */
  42. public set adaptiveBlurKernel(value: number) {
  43. this._adaptiveBlurKernel = value;
  44. this._autoComputeBlurKernel();
  45. }
  46. /**
  47. * Define the blur kernel used to blur the reflection if needed.
  48. * Please consider using `adaptiveBlurKernel` as it could find the closest best value for you.
  49. */
  50. public set blurKernel(value: number) {
  51. this.blurKernelX = value;
  52. this.blurKernelY = value;
  53. }
  54. /**
  55. * Define the blur kernel on the X Axis used to blur the reflection if needed.
  56. * Please consider using `adaptiveBlurKernel` as it could find the closest best value for you.
  57. */
  58. public set blurKernelX(value: number) {
  59. if (this._blurKernelX === value) {
  60. return;
  61. }
  62. this._blurKernelX = value;
  63. this._preparePostProcesses();
  64. }
  65. public get blurKernelX(): number {
  66. return this._blurKernelX;
  67. }
  68. /**
  69. * Define the blur kernel on the Y Axis used to blur the reflection if needed.
  70. * Please consider using `adaptiveBlurKernel` as it could find the closest best value for you.
  71. */
  72. public set blurKernelY(value: number) {
  73. if (this._blurKernelY === value) {
  74. return;
  75. }
  76. this._blurKernelY = value;
  77. this._preparePostProcesses();
  78. }
  79. public get blurKernelY(): number {
  80. return this._blurKernelY;
  81. }
  82. private _autoComputeBlurKernel(): void {
  83. let engine = this.getScene()!.getEngine();
  84. let dw = this.getRenderWidth() / engine.getRenderWidth();
  85. let dh = this.getRenderHeight() / engine.getRenderHeight();
  86. this.blurKernelX = this._adaptiveBlurKernel * dw;
  87. this.blurKernelY = this._adaptiveBlurKernel * dh;
  88. }
  89. protected _onRatioRescale(): void {
  90. if (this._sizeRatio) {
  91. this.resize(this._initialSizeParameter);
  92. if (!this._adaptiveBlurKernel) {
  93. this._preparePostProcesses();
  94. }
  95. }
  96. if (this._adaptiveBlurKernel) {
  97. this._autoComputeBlurKernel();
  98. }
  99. }
  100. private _updateGammaSpace() {
  101. this.gammaSpace = !this.scene.imageProcessingConfiguration.isEnabled || !this.scene.imageProcessingConfiguration.applyByPostProcess;
  102. }
  103. private _imageProcessingConfigChangeObserver: Nullable<Observer<ImageProcessingConfiguration>>;
  104. private _transformMatrix = Matrix.Zero();
  105. private _mirrorMatrix = Matrix.Zero();
  106. private _savedViewMatrix: Matrix;
  107. private _blurX: Nullable<BlurPostProcess>;
  108. private _blurY: Nullable<BlurPostProcess>;
  109. private _adaptiveBlurKernel = 0;
  110. private _blurKernelX = 0;
  111. private _blurKernelY = 0;
  112. private _blurRatio = 1.0;
  113. /**
  114. * Instantiates a Mirror Texture.
  115. * Mirror texture can be used to simulate the view from a mirror in a scene.
  116. * It will dynamically be rendered every frame to adapt to the camera point of view.
  117. * You can then easily use it as a reflectionTexture on a flat surface.
  118. * In case the surface is not a plane, please consider relying on reflection probes.
  119. * @see https://doc.babylonjs.com/how_to/reflect#mirrors
  120. * @param name
  121. * @param size
  122. * @param scene
  123. * @param generateMipMaps
  124. * @param type
  125. * @param samplingMode
  126. * @param generateDepthBuffer
  127. */
  128. constructor(name: string, size: number | { width: number, height: number } | { ratio: number }, private scene: Scene, generateMipMaps?: boolean, type: number = Constants.TEXTURETYPE_UNSIGNED_INT, samplingMode = Texture.BILINEAR_SAMPLINGMODE, generateDepthBuffer = true) {
  129. super(name, size, scene, generateMipMaps, true, type, false, samplingMode, generateDepthBuffer);
  130. this.ignoreCameraViewport = true;
  131. this._updateGammaSpace();
  132. this._imageProcessingConfigChangeObserver = scene.imageProcessingConfiguration.onUpdateParameters.add(() => {
  133. this._updateGammaSpace;
  134. });
  135. this.onBeforeRenderObservable.add(() => {
  136. Matrix.ReflectionToRef(this.mirrorPlane, this._mirrorMatrix);
  137. this._savedViewMatrix = scene.getViewMatrix();
  138. this._mirrorMatrix.multiplyToRef(this._savedViewMatrix, this._transformMatrix);
  139. scene.setTransformMatrix(this._transformMatrix, scene.getProjectionMatrix());
  140. scene.clipPlane = this.mirrorPlane;
  141. scene.getEngine().cullBackFaces = false;
  142. scene._mirroredCameraPosition = Vector3.TransformCoordinates((<Camera>scene.activeCamera).globalPosition, this._mirrorMatrix);
  143. });
  144. this.onAfterRenderObservable.add(() => {
  145. scene.setTransformMatrix(this._savedViewMatrix, scene.getProjectionMatrix());
  146. scene.getEngine().cullBackFaces = true;
  147. scene._mirroredCameraPosition = null;
  148. scene.clipPlane = null;
  149. });
  150. }
  151. private _preparePostProcesses(): void {
  152. this.clearPostProcesses(true);
  153. if (this._blurKernelX && this._blurKernelY) {
  154. var engine = (<Scene>this.getScene()).getEngine();
  155. var textureType = engine.getCaps().textureFloatRender ? Constants.TEXTURETYPE_FLOAT : Constants.TEXTURETYPE_HALF_FLOAT;
  156. this._blurX = new BlurPostProcess("horizontal blur", new Vector2(1.0, 0), this._blurKernelX, this._blurRatio, null, Texture.BILINEAR_SAMPLINGMODE, engine, false, textureType);
  157. this._blurX.autoClear = false;
  158. if (this._blurRatio === 1 && this.samples < 2 && this._texture) {
  159. this._blurX.inputTexture = this._texture;
  160. } else {
  161. this._blurX.alwaysForcePOT = true;
  162. }
  163. this._blurY = new BlurPostProcess("vertical blur", new Vector2(0, 1.0), this._blurKernelY, this._blurRatio, null, Texture.BILINEAR_SAMPLINGMODE, engine, false, textureType);
  164. this._blurY.autoClear = false;
  165. this._blurY.alwaysForcePOT = this._blurRatio !== 1;
  166. this.addPostProcess(this._blurX);
  167. this.addPostProcess(this._blurY);
  168. }
  169. else {
  170. if (this._blurY) {
  171. this.removePostProcess(this._blurY);
  172. this._blurY.dispose();
  173. this._blurY = null;
  174. }
  175. if (this._blurX) {
  176. this.removePostProcess(this._blurX);
  177. this._blurX.dispose();
  178. this._blurX = null;
  179. }
  180. }
  181. }
  182. /**
  183. * Clone the mirror texture.
  184. * @returns the cloned texture
  185. */
  186. public clone(): MirrorTexture {
  187. let scene = this.getScene();
  188. if (!scene) {
  189. return this;
  190. }
  191. var textureSize = this.getSize();
  192. var newTexture = new MirrorTexture(
  193. this.name,
  194. textureSize.width,
  195. scene,
  196. this._renderTargetOptions.generateMipMaps,
  197. this._renderTargetOptions.type,
  198. this._renderTargetOptions.samplingMode,
  199. this._renderTargetOptions.generateDepthBuffer
  200. );
  201. // Base texture
  202. newTexture.hasAlpha = this.hasAlpha;
  203. newTexture.level = this.level;
  204. // Mirror Texture
  205. newTexture.mirrorPlane = this.mirrorPlane.clone();
  206. if (this.renderList) {
  207. newTexture.renderList = this.renderList.slice(0);
  208. }
  209. return newTexture;
  210. }
  211. /**
  212. * Serialize the texture to a JSON representation you could use in Parse later on
  213. * @returns the serialized JSON representation
  214. */
  215. public serialize(): any {
  216. if (!this.name) {
  217. return null;
  218. }
  219. var serializationObject = super.serialize();
  220. serializationObject.mirrorPlane = this.mirrorPlane.asArray();
  221. return serializationObject;
  222. }
  223. /**
  224. * Dispose the texture and release its associated resources.
  225. */
  226. public dispose() {
  227. super.dispose();
  228. this.scene.imageProcessingConfiguration.onUpdateParameters.remove(this._imageProcessingConfigChangeObserver);
  229. }
  230. }
  231. Texture._CreateMirror = (name: string, renderTargetSize: number, scene: Scene, generateMipMaps: boolean): MirrorTexture => {
  232. return new MirrorTexture(name, renderTargetSize, scene, generateMipMaps);
  233. };