maplibre / maplibre-gl-js

MapLibre GL JS - Interactive vector tile maps in the browser
https://maplibre.org/maplibre-gl-js/docs/
Other
6.72k stars 719 forks source link

Allow modifying farZ camera plane variable #5091

Open kamil-sienkiewicz-asi opened 1 day ago

kamil-sienkiewicz-asi commented 1 day ago

User Story

I want to add a simple skybox to globe view, instead of black screen behind it I want to add some stars etc. However,

globe_transform.ts

628 this._farZ = this.cameraToCenterDistance + globeRadiusPixels * 2.0; // just set the far plane far enough - we will calculate our own z in the vertex shader anyway

does not let me render things behind the globe: Image

I tried to modify projection matrix to change farZ value, however with no luck, changing 10th and 14th elements of the projection matrix were messing up rendering, also I'm not sure if having different projection matrices in one "scene" makes sense, I'm concerned about some unpredictable issues by such approach. Image

  render(gl: WebGL2RenderingContext, options: CustomRenderMethodInput): void {
    const {
      nearZ,
      farZ,
      projectionMatrix,
      defaultProjectionData: { mainMatrix },
    } = options;

    const projMatrix = new Matrix4().fromArray(projectionMatrix);
    const modelViewProjMatrix = new Matrix4().fromArray(mainMatrix);
    const projMatrixInverse = new Matrix4().copy(projMatrix).invert();
    const modelViewMatrix = new Matrix4().multiplyMatrices(modelViewProjMatrix, projMatrixInverse);
    const newProjMatrix = new Matrix4().copy(projMatrix);
    const n = nearZ;
    const f = farZ * 4.0; // editing this messes up scene
    newProjMatrix.elements[10] = -(f + n) / (f - n);
    newProjMatrix.elements[14] = (-2 * f * n) / (f - n);
    this.camera.projectionMatrix = modelViewMatrix.multiply(newProjMatrix);

after modifying farZ value, by simply multiplying it by 5.0, scene is looking as expected: Image

So in the end it seems like I would like to keep same projection matrix as maplibre uses, but I want to be able to modify and extend far plane of the camera, to render skybox/rockets/moon/satellites etc.

HarelM commented 1 day ago

I think i have the same concerns as I had for the following PR: https://github.com/maplibre/maplibre-gl-js/pull/4914

I'm not sure this world be the right API to expose. Is there a different way this can be solved?

bartosz-banachewicz-asi commented 22 hours ago

@HarelM Yes, by setting the default value to a less conservative one. (cc @kubapelc)

The depth buffer is a resource shared by everything rendering to the same frame in WebGL. In order to do any sort of 3D compositing, there needs to be a sufficient range of values usable to make everything fit. The way the current farZ is computed for the globe means that the sphere itself occupies virtually everything, spanning all possible depth values. Increasing this value by a factor of 2-5 would similarly increase the amount of "space" in the Z coordinate for skyboxes and objects in space or on orbits.

Other solutions to this issue that we've thought about/evaluated all seem to have significant complexity and downsides in comparison:

There are no significant downsides to setting the value to something like 4x the current one, other than 2 bits of precision in the depth testing lost in the range used by the sphere; however considering it's mostly a single mesh that gets back-culled anyway, I don't think it would be problematic for any other user. If there's concern about that, rather than exposing farZ directly, having two predefined values ("narrow" and "wide", for example) would both solve our issues in an elegant way and hopefully prevent API misuse.