mirrorTexture.ts 10 KB

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