lensFlareSystem.js 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340
  1. import { Tools } from "../Misc/tools.js";
  2. import { Matrix, Vector3 } from "../Maths/math.vector.js";
  3. import { Scalar } from "../Maths/math.scalar.js";
  4. import { EngineStore } from "../Engines/engineStore.js";
  5. import { VertexBuffer } from "../Buffers/buffer.js";
  6. import { Ray } from "../Culling/ray.js";
  7. import { Material } from "../Materials/material.js";
  8. import { LensFlare } from "./lensFlare.js";
  9. import "../Shaders/lensFlare.fragment.js";
  10. import "../Shaders/lensFlare.vertex.js";
  11. import { _WarnImport } from "../Misc/devTools.js";
  12. import { Color3 } from "../Maths/math.color.js";
  13. /**
  14. * This represents a Lens Flare System or the shiny effect created by the light reflection on the camera lenses.
  15. * It is usually composed of several `lensFlare`.
  16. * @see https://doc.babylonjs.com/features/featuresDeepDive/environment/lenseFlare
  17. */
  18. export class LensFlareSystem {
  19. /** Gets the scene */
  20. get scene() {
  21. return this._scene;
  22. }
  23. /**
  24. * Instantiates a lens flare system.
  25. * This represents a Lens Flare System or the shiny effect created by the light reflection on the camera lenses.
  26. * It is usually composed of several `lensFlare`.
  27. * @see https://doc.babylonjs.com/features/featuresDeepDive/environment/lenseFlare
  28. * @param name Define the name of the lens flare system in the scene
  29. * @param emitter Define the source (the emitter) of the lens flares (it can be a camera, a light or a mesh).
  30. * @param scene Define the scene the lens flare system belongs to
  31. */
  32. constructor(
  33. /**
  34. * Define the name of the lens flare system
  35. */
  36. name, emitter, scene) {
  37. this.name = name;
  38. /**
  39. * List of lens flares used in this system.
  40. */
  41. this.lensFlares = [];
  42. /**
  43. * Define a limit from the border the lens flare can be visible.
  44. */
  45. this.borderLimit = 300;
  46. /**
  47. * Define a viewport border we do not want to see the lens flare in.
  48. */
  49. this.viewportBorder = 0;
  50. /**
  51. * Restricts the rendering of the effect to only the camera rendering this layer mask.
  52. */
  53. this.layerMask = 0x0fffffff;
  54. this._vertexBuffers = {};
  55. this._isEnabled = true;
  56. this._scene = scene || EngineStore.LastCreatedScene;
  57. LensFlareSystem._SceneComponentInitialization(this._scene);
  58. this._emitter = emitter;
  59. this.id = name;
  60. scene.lensFlareSystems.push(this);
  61. this.meshesSelectionPredicate = (m) => (scene.activeCamera && m.material && m.isVisible && m.isEnabled() && m.isBlocker && (m.layerMask & scene.activeCamera.layerMask) != 0);
  62. const engine = scene.getEngine();
  63. // VBO
  64. const vertices = [];
  65. vertices.push(1, 1);
  66. vertices.push(-1, 1);
  67. vertices.push(-1, -1);
  68. vertices.push(1, -1);
  69. this._vertexBuffers[VertexBuffer.PositionKind] = new VertexBuffer(engine, vertices, VertexBuffer.PositionKind, false, false, 2);
  70. // Indices
  71. this._createIndexBuffer();
  72. }
  73. _createIndexBuffer() {
  74. const indices = [];
  75. indices.push(0);
  76. indices.push(1);
  77. indices.push(2);
  78. indices.push(0);
  79. indices.push(2);
  80. indices.push(3);
  81. this._indexBuffer = this._scene.getEngine().createIndexBuffer(indices);
  82. }
  83. /**
  84. * Define if the lens flare system is enabled.
  85. */
  86. get isEnabled() {
  87. return this._isEnabled;
  88. }
  89. set isEnabled(value) {
  90. this._isEnabled = value;
  91. }
  92. /**
  93. * Get the scene the effects belongs to.
  94. * @returns the scene holding the lens flare system
  95. */
  96. getScene() {
  97. return this._scene;
  98. }
  99. /**
  100. * Get the emitter of the lens flare system.
  101. * It defines the source of the lens flares (it can be a camera, a light or a mesh).
  102. * @returns the emitter of the lens flare system
  103. */
  104. getEmitter() {
  105. return this._emitter;
  106. }
  107. /**
  108. * Set the emitter of the lens flare system.
  109. * It defines the source of the lens flares (it can be a camera, a light or a mesh).
  110. * @param newEmitter Define the new emitter of the system
  111. */
  112. setEmitter(newEmitter) {
  113. this._emitter = newEmitter;
  114. }
  115. /**
  116. * Get the lens flare system emitter position.
  117. * The emitter defines the source of the lens flares (it can be a camera, a light or a mesh).
  118. * @returns the position
  119. */
  120. getEmitterPosition() {
  121. return this._emitter.getAbsolutePosition ? this._emitter.getAbsolutePosition() : this._emitter.position;
  122. }
  123. /**
  124. * @internal
  125. */
  126. computeEffectivePosition(globalViewport) {
  127. let position = this.getEmitterPosition();
  128. position = Vector3.Project(position, Matrix.Identity(), this._scene.getTransformMatrix(), globalViewport);
  129. this._positionX = position.x;
  130. this._positionY = position.y;
  131. position = Vector3.TransformCoordinates(this.getEmitterPosition(), this._scene.getViewMatrix());
  132. if (this.viewportBorder > 0) {
  133. globalViewport.x -= this.viewportBorder;
  134. globalViewport.y -= this.viewportBorder;
  135. globalViewport.width += this.viewportBorder * 2;
  136. globalViewport.height += this.viewportBorder * 2;
  137. position.x += this.viewportBorder;
  138. position.y += this.viewportBorder;
  139. this._positionX += this.viewportBorder;
  140. this._positionY += this.viewportBorder;
  141. }
  142. const rhs = this._scene.useRightHandedSystem;
  143. const okZ = (position.z > 0 && !rhs) || (position.z < 0 && rhs);
  144. if (okZ) {
  145. if (this._positionX > globalViewport.x && this._positionX < globalViewport.x + globalViewport.width) {
  146. if (this._positionY > globalViewport.y && this._positionY < globalViewport.y + globalViewport.height) {
  147. return true;
  148. }
  149. }
  150. return true;
  151. }
  152. return false;
  153. }
  154. /** @internal */
  155. _isVisible() {
  156. if (!this._isEnabled || !this._scene.activeCamera) {
  157. return false;
  158. }
  159. const emitterPosition = this.getEmitterPosition();
  160. const direction = emitterPosition.subtract(this._scene.activeCamera.globalPosition);
  161. const distance = direction.length();
  162. direction.normalize();
  163. const ray = new Ray(this._scene.activeCamera.globalPosition, direction);
  164. const pickInfo = this._scene.pickWithRay(ray, this.meshesSelectionPredicate, true);
  165. return !pickInfo || !pickInfo.hit || pickInfo.distance > distance;
  166. }
  167. /**
  168. * @internal
  169. */
  170. render() {
  171. if (!this._scene.activeCamera) {
  172. return false;
  173. }
  174. const engine = this._scene.getEngine();
  175. const viewport = this._scene.activeCamera.viewport;
  176. const globalViewport = viewport.toGlobal(engine.getRenderWidth(true), engine.getRenderHeight(true));
  177. // Position
  178. if (!this.computeEffectivePosition(globalViewport)) {
  179. return false;
  180. }
  181. // Visibility
  182. if (!this._isVisible()) {
  183. return false;
  184. }
  185. // Intensity
  186. let awayX;
  187. let awayY;
  188. if (this._positionX < this.borderLimit + globalViewport.x) {
  189. awayX = this.borderLimit + globalViewport.x - this._positionX;
  190. }
  191. else if (this._positionX > globalViewport.x + globalViewport.width - this.borderLimit) {
  192. awayX = this._positionX - globalViewport.x - globalViewport.width + this.borderLimit;
  193. }
  194. else {
  195. awayX = 0;
  196. }
  197. if (this._positionY < this.borderLimit + globalViewport.y) {
  198. awayY = this.borderLimit + globalViewport.y - this._positionY;
  199. }
  200. else if (this._positionY > globalViewport.y + globalViewport.height - this.borderLimit) {
  201. awayY = this._positionY - globalViewport.y - globalViewport.height + this.borderLimit;
  202. }
  203. else {
  204. awayY = 0;
  205. }
  206. let away = awayX > awayY ? awayX : awayY;
  207. away -= this.viewportBorder;
  208. if (away > this.borderLimit) {
  209. away = this.borderLimit;
  210. }
  211. let intensity = 1.0 - Scalar.Clamp(away / this.borderLimit, 0, 1);
  212. if (intensity < 0) {
  213. return false;
  214. }
  215. if (intensity > 1.0) {
  216. intensity = 1.0;
  217. }
  218. if (this.viewportBorder > 0) {
  219. globalViewport.x += this.viewportBorder;
  220. globalViewport.y += this.viewportBorder;
  221. globalViewport.width -= this.viewportBorder * 2;
  222. globalViewport.height -= this.viewportBorder * 2;
  223. this._positionX -= this.viewportBorder;
  224. this._positionY -= this.viewportBorder;
  225. }
  226. // Position
  227. const centerX = globalViewport.x + globalViewport.width / 2;
  228. const centerY = globalViewport.y + globalViewport.height / 2;
  229. const distX = centerX - this._positionX;
  230. const distY = centerY - this._positionY;
  231. // Effects
  232. engine.setState(false);
  233. engine.setDepthBuffer(false);
  234. // Flares
  235. for (let index = 0; index < this.lensFlares.length; index++) {
  236. const flare = this.lensFlares[index];
  237. if (!flare._drawWrapper.effect.isReady() || (flare.texture && !flare.texture.isReady())) {
  238. continue;
  239. }
  240. engine.enableEffect(flare._drawWrapper);
  241. engine.bindBuffers(this._vertexBuffers, this._indexBuffer, flare._drawWrapper.effect);
  242. engine.setAlphaMode(flare.alphaMode);
  243. const x = centerX - distX * flare.position;
  244. const y = centerY - distY * flare.position;
  245. const cw = flare.size;
  246. const ch = flare.size * engine.getAspectRatio(this._scene.activeCamera, true);
  247. const cx = 2 * (x / (globalViewport.width + globalViewport.x * 2)) - 1.0;
  248. const cy = 1.0 - 2 * (y / (globalViewport.height + globalViewport.y * 2));
  249. const viewportMatrix = Matrix.FromValues(cw / 2, 0, 0, 0, 0, ch / 2, 0, 0, 0, 0, 1, 0, cx, cy, 0, 1);
  250. flare._drawWrapper.effect.setMatrix("viewportMatrix", viewportMatrix);
  251. // Texture
  252. flare._drawWrapper.effect.setTexture("textureSampler", flare.texture);
  253. // Color
  254. flare._drawWrapper.effect.setFloat4("color", flare.color.r * intensity, flare.color.g * intensity, flare.color.b * intensity, 1.0);
  255. // Draw order
  256. engine.drawElementsType(Material.TriangleFillMode, 0, 6);
  257. }
  258. engine.setDepthBuffer(true);
  259. engine.setAlphaMode(0);
  260. return true;
  261. }
  262. /**
  263. * Rebuilds the lens flare system
  264. */
  265. rebuild() {
  266. this._createIndexBuffer();
  267. for (const key in this._vertexBuffers) {
  268. this._vertexBuffers[key]?._rebuild();
  269. }
  270. }
  271. /**
  272. * Dispose and release the lens flare with its associated resources.
  273. */
  274. dispose() {
  275. const vertexBuffer = this._vertexBuffers[VertexBuffer.PositionKind];
  276. if (vertexBuffer) {
  277. vertexBuffer.dispose();
  278. this._vertexBuffers[VertexBuffer.PositionKind] = null;
  279. }
  280. if (this._indexBuffer) {
  281. this._scene.getEngine()._releaseBuffer(this._indexBuffer);
  282. this._indexBuffer = null;
  283. }
  284. while (this.lensFlares.length) {
  285. this.lensFlares[0].dispose();
  286. }
  287. // Remove from scene
  288. const index = this._scene.lensFlareSystems.indexOf(this);
  289. this._scene.lensFlareSystems.splice(index, 1);
  290. }
  291. /**
  292. * Parse a lens flare system from a JSON representation
  293. * @param parsedLensFlareSystem Define the JSON to parse
  294. * @param scene Define the scene the parsed system should be instantiated in
  295. * @param rootUrl Define the rootUrl of the load sequence to easily find a load relative dependencies such as textures
  296. * @returns the parsed system
  297. */
  298. static Parse(parsedLensFlareSystem, scene, rootUrl) {
  299. const emitter = scene.getLastEntryById(parsedLensFlareSystem.emitterId);
  300. const name = parsedLensFlareSystem.name || "lensFlareSystem#" + parsedLensFlareSystem.emitterId;
  301. const lensFlareSystem = new LensFlareSystem(name, emitter, scene);
  302. lensFlareSystem.id = parsedLensFlareSystem.id || name;
  303. lensFlareSystem.borderLimit = parsedLensFlareSystem.borderLimit;
  304. for (let index = 0; index < parsedLensFlareSystem.flares.length; index++) {
  305. const parsedFlare = parsedLensFlareSystem.flares[index];
  306. LensFlare.AddFlare(parsedFlare.size, parsedFlare.position, Color3.FromArray(parsedFlare.color), parsedFlare.textureName ? rootUrl + parsedFlare.textureName : "", lensFlareSystem);
  307. }
  308. return lensFlareSystem;
  309. }
  310. /**
  311. * Serialize the current Lens Flare System into a JSON representation.
  312. * @returns the serialized JSON
  313. */
  314. serialize() {
  315. const serializationObject = {};
  316. serializationObject.id = this.id;
  317. serializationObject.name = this.name;
  318. serializationObject.emitterId = this.getEmitter().id;
  319. serializationObject.borderLimit = this.borderLimit;
  320. serializationObject.flares = [];
  321. for (let index = 0; index < this.lensFlares.length; index++) {
  322. const flare = this.lensFlares[index];
  323. serializationObject.flares.push({
  324. size: flare.size,
  325. position: flare.position,
  326. color: flare.color.asArray(),
  327. textureName: Tools.GetFilename(flare.texture ? flare.texture.name : ""),
  328. });
  329. }
  330. return serializationObject;
  331. }
  332. }
  333. /**
  334. * @internal
  335. */
  336. LensFlareSystem._SceneComponentInitialization = (_) => {
  337. throw _WarnImport("LensFlareSystemSceneComponent");
  338. };
  339. //# sourceMappingURL=lensFlareSystem.js.map