oceanPostProcess.ts 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249
  1. import { TargetCamera } from "babylonjs/Cameras/targetCamera";
  2. import { Texture } from "babylonjs/Materials/Textures/texture";
  3. import { MirrorTexture } from "babylonjs/Materials/Textures/mirrorTexture";
  4. import { RenderTargetTexture } from "babylonjs/Materials/Textures/renderTargetTexture";
  5. import { GeometryBufferRenderer } from "babylonjs/Rendering/geometryBufferRenderer";
  6. import { Effect } from "babylonjs/Materials/effect";
  7. import { PostProcess } from "babylonjs/PostProcesses/postProcess";
  8. import { Vector2, Vector3, Plane, Matrix, Epsilon } from "babylonjs/Maths/math";
  9. import { Nullable } from "babylonjs/types";
  10. import "./oceanPostProcess.fragment";
  11. /**
  12. * Option available in the Ocean Post Process.
  13. */
  14. export interface IOceanPostProcessOptions {
  15. /**
  16. * The size of the reflection RTT (number if square, or {width: number, height:number} or {ratio:} to define a ratio from the main scene)
  17. */
  18. reflectionSize?: number | { width: number; height: number } | { ratio: number };
  19. /**
  20. * The size of the refraction RTT (number if square, or {width: number, height:number} or {ratio:} to define a ratio from the main scene)
  21. */
  22. refractionSize?: number | { width: number; height: number } | { ratio: number };
  23. }
  24. /**
  25. * OceanPostProcess helps rendering an infinite ocean surface that can reflect and refract environment.
  26. *
  27. * Simmply add it to your scene and let the nerd that lives in you have fun.
  28. * Example usage:
  29. * var pp = new OceanPostProcess("myOcean", camera);
  30. * pp.reflectionEnabled = true;
  31. * pp.refractionEnabled = true;
  32. */
  33. export class OceanPostProcess extends PostProcess {
  34. /**
  35. * Gets a boolean indicating if the real-time reflection is enabled on the ocean.
  36. */
  37. public get reflectionEnabled(): boolean {
  38. return this._reflectionEnabled;
  39. }
  40. /**
  41. * Sets weither or not the real-time reflection is enabled on the ocean.
  42. * Is set to true, the reflection mirror texture will be used as reflection texture.
  43. */
  44. public set reflectionEnabled(enabled: boolean) {
  45. if (this._reflectionEnabled === enabled) {
  46. return;
  47. }
  48. this._reflectionEnabled = enabled;
  49. this.updateEffect(this._getDefines());
  50. // Remove or add custom render target
  51. const customRenderTargets = this.getCamera().getScene().customRenderTargets;
  52. if (!enabled) {
  53. const index = customRenderTargets.indexOf(this.reflectionTexture);
  54. if (index !== -1) {
  55. customRenderTargets.splice(index, 1);
  56. }
  57. } else {
  58. customRenderTargets.push(this.reflectionTexture);
  59. }
  60. }
  61. /**
  62. * Gets a boolean indicating if the real-time refraction is enabled on the ocean.
  63. */
  64. public get refractionEnabled(): boolean {
  65. return this._refractionEnabled;
  66. }
  67. /**
  68. * Sets weither or not the real-time refraction is enabled on the ocean.
  69. * Is set to true, the refraction render target texture will be used as refraction texture.
  70. */
  71. public set refractionEnabled(enabled: boolean) {
  72. if (this._refractionEnabled === enabled) {
  73. return;
  74. }
  75. this._refractionEnabled = enabled;
  76. this.updateEffect(this._getDefines());
  77. // Remove or add custom render target
  78. const customRenderTargets = this.getCamera().getScene().customRenderTargets;
  79. if (!enabled) {
  80. const index = customRenderTargets.indexOf(this.refractionTexture);
  81. if (index !== -1) {
  82. customRenderTargets.splice(index, 1);
  83. }
  84. } else {
  85. customRenderTargets.push(this.refractionTexture);
  86. }
  87. }
  88. /**
  89. * Gets wether or not the post-processes is supported by the running hardware.
  90. * This requires draw buffer supports.
  91. */
  92. public get isSupported(): boolean {
  93. return this._geometryRenderer !== null && this._geometryRenderer.isSupported;
  94. }
  95. /**
  96. * This is the reflection mirror texture used to display reflections on the ocean.
  97. * By default, render list is empty.
  98. */
  99. public reflectionTexture: MirrorTexture;
  100. /**
  101. * This is the refraction render target texture used to display refraction on the ocean.
  102. * By default, render list is empty.
  103. */
  104. public refractionTexture: RenderTargetTexture;
  105. private _time: number = 0;
  106. private _cameraRotation: Vector3 = Vector3.Zero();
  107. private _cameraViewMatrix: Matrix = Matrix.Identity();
  108. private _reflectionEnabled: boolean = false;
  109. private _refractionEnabled: boolean = false;
  110. private _geometryRenderer: Nullable<GeometryBufferRenderer>;
  111. /**
  112. * Instantiates a new Ocean Post Process.
  113. * @param name the name to give to the postprocess.
  114. * @camera the camera to apply the post process to.
  115. * @param options optional object following the IOceanPostProcessOptions format used to customize reflection and refraction render targets sizes.
  116. */
  117. constructor(name: string, camera: TargetCamera, options: IOceanPostProcessOptions = { }) {
  118. super(name,
  119. "oceanPostProcess",
  120. ["time", "resolution", "cameraPosition", "cameraRotation"],
  121. ["positionSampler", "reflectionSampler", "refractionSampler"],
  122. {
  123. width: camera.getEngine().getRenderWidth(),
  124. height: camera.getEngine().getRenderHeight()
  125. },
  126. camera,
  127. Texture.TRILINEAR_SAMPLINGMODE,
  128. camera.getEngine(),
  129. true);
  130. // Get geometry shader
  131. this._geometryRenderer = camera.getScene().enableGeometryBufferRenderer(1.0);
  132. if (this._geometryRenderer && this._geometryRenderer.isSupported) {
  133. // Eanble position buffer
  134. this._geometryRenderer.enablePosition = true;
  135. // Create mirror textures
  136. this.reflectionTexture = new MirrorTexture("oceanPostProcessReflection", options.reflectionSize || { width: 512, height: 512 }, camera.getScene());
  137. this.reflectionTexture.mirrorPlane = Plane.FromPositionAndNormal(Vector3.Zero(), new Vector3(0, -1, 0));
  138. this.refractionTexture = new RenderTargetTexture("oceanPostProcessRefraction", options.refractionSize || { width: 512, height: 512 }, camera.getScene());
  139. } else {
  140. this.updateEffect("#define NOT_SUPPORTED\n");
  141. }
  142. // On apply the post-process
  143. this.onApply = (effect: Effect) => {
  144. if (!this._geometryRenderer || !this._geometryRenderer.isSupported) {
  145. return;
  146. }
  147. const engine = camera.getEngine();
  148. const scene = camera.getScene();
  149. this._time += engine.getDeltaTime() * 0.001;
  150. effect.setFloat("time", this._time);
  151. effect.setVector2("resolution", new Vector2(engine.getRenderWidth(), engine.getRenderHeight()));
  152. if (scene) {
  153. // Position
  154. effect.setVector3("cameraPosition", camera.globalPosition);
  155. // Rotation
  156. this._computeCameraRotation(camera);
  157. effect.setVector3("cameraRotation", this._cameraRotation);
  158. // Samplers
  159. effect.setTexture("positionSampler", this._geometryRenderer.getGBuffer().textures[2]);
  160. if (this._reflectionEnabled) {
  161. effect.setTexture("reflectionSampler", this.reflectionTexture);
  162. }
  163. if (this._refractionEnabled) {
  164. effect.setTexture("refractionSampler", this.refractionTexture);
  165. }
  166. }
  167. };
  168. }
  169. /**
  170. * Returns the appropriate defines according to the current configuration.
  171. */
  172. private _getDefines(): string {
  173. const defines: string[] = [];
  174. if (this._reflectionEnabled) {
  175. defines.push("#define REFLECTION_ENABLED");
  176. }
  177. if (this._refractionEnabled) {
  178. defines.push("#define REFRACTION_ENABLED");
  179. }
  180. return defines.join("\n");
  181. }
  182. /**
  183. * Computes the current camera rotation as the shader requires a camera rotation.
  184. */
  185. private _computeCameraRotation(camera: TargetCamera): void {
  186. camera.upVector.normalize();
  187. const target = camera.getTarget();
  188. camera._initialFocalDistance = target.subtract(camera.position).length();
  189. if (camera.position.z === target.z) {
  190. camera.position.z += Epsilon;
  191. }
  192. const direction = target.subtract(camera.position);
  193. camera._viewMatrix.invertToRef(this._cameraViewMatrix);
  194. this._cameraRotation.x = Math.atan(this._cameraViewMatrix.m[6] / this._cameraViewMatrix.m[10]);
  195. if (direction.x >= 0.0) {
  196. this._cameraRotation.y = (-Math.atan(direction.z / direction.x) + Math.PI / 2.0);
  197. } else {
  198. this._cameraRotation.y = (-Math.atan(direction.z / direction.x) - Math.PI / 2.0);
  199. }
  200. this._cameraRotation.z = 0;
  201. if (isNaN(this._cameraRotation.x)) {
  202. this._cameraRotation.x = 0;
  203. }
  204. if (isNaN(this._cameraRotation.y)) {
  205. this._cameraRotation.y = 0;
  206. }
  207. if (isNaN(this._cameraRotation.z)) {
  208. this._cameraRotation.z = 0;
  209. }
  210. }
  211. }