mapbox / mapbox-gl-js

Interactive, thoroughly customizable maps in the browser, powered by vector tiles and WebGL
https://docs.mapbox.com/mapbox-gl-js/
Other
11.06k stars 2.21k forks source link

Dynamic hillshade with terrain doesn't update lighting smoothly #13238

Open matteiben-onx opened 1 month ago

matteiben-onx commented 1 month ago

mapbox-gl-js version: 2.15.0

browser: Chrome

Description

In v2.15.0, when using dynamic hillshade with 3D terrain applied, lighting does not update smoothly. There is one edgecase where it does seem to update smoothly, as noted below.

When in 2D in this example https://docs.mapbox.com/mapbox-gl-js/example/hillshade/, the dynamic hillshade lighting updates smoothly.

In v3+, dynamic hillshade lighting with 3D terrain applied continues to not update smoothly, although the behavior is slightly different than this demo using v2.15.0.

Also worth noting is the mobile SDKs update lighting smoothly in the same scenarios.

Steps to Trigger Behavior

  1. Spin up code snippet below locally, note lighting rendering smoothly
  2. Comment out either of the noted lines
  3. Note lighting no longer renders smoothly

Demo Video: https://github.com/user-attachments/assets/f3f3c43a-e129-49cb-b805-cb1553eaed98

Note at first, the dynamic hillshade lighting updates smoothly. Once I comment other either of the two lines, it will start updating only once the camera has stopped and on mouseup.

Link to Demonstration

Doesn't seem to render correctly in jsbin, so pasting code here

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>Dynamic Hillshade</title>
    <meta name="viewport" content="initial-scale=1,maximum-scale=1,user-scalable=no">
    <link href="https://api.mapbox.com/mapbox-gl-js/v2.15.0/mapbox-gl.css" rel="stylesheet">
    <script src="https://api.mapbox.com/mapbox-gl-js/v2.15.0/mapbox-gl.js"></script>
    <style>
      body { margin: 0; padding: 0; }
      #map { position: absolute; top: 0; bottom: 0; width: 100%; }
    </style>
  </head>
  <body>
    <div id="map"></div>
    <script>
      mapboxgl.accessToken = "pk.eyJ1IjoiZXhhbXBsZXMiLCJhIjoiY2p0MG01MXRqMW45cjQzb2R6b2ptc3J4MSJ9.zA2W0IkI0c6KaAhJfk9bWg"

      function initializeMap(stylesheet) {
        const demSource = {
          maxzoom: 14,
          type: "raster-dem",
          url: "mapbox://mapbox.mapbox-terrain-dem-v1",
          encoding: "mapbox",
          tileSize: 512,
        }

        stylesheet.sources["dem"] = Object.assign({}, demSource)
        stylesheet.sources["hillshadeDem"] = Object.assign({}, demSource)
        stylesheet.terrain = { source: "dem" }

        const hillshadeIdx = stylesheet.layers.findIndex((l) => l.id === "hillshade")

        // Replace static hillshade layer with dynamic one
        stylesheet.layers.splice(hillshadeIdx, 1, {
          id: "hillshadeDem",
          source: "hillshadeDem",
          type: "hillshade",
        })

        const map = new mapboxgl.Map({
          container: "map",
          style: stylesheet,
          center: [-106.28042, 39.74775],
          zoom: 14.65,
          bearing: 67,
          pitch: 56,
          hash: true,
          // Comment this out
          optimizeForTerrain: false,
        })

        map.once('load', () => {
          // Comment this out
          // Force a symbol layer near the bottom of the stylesheet.
          // This is out of "intended render order" when in 3D, which triggers the edge case, resulting in
          // the dynamic hillshade lighting updating smoothly.
          map.moveLayer("contour-label", "contour-line")
        })
      }

      fetch(`https://api.mapbox.com/styles/v1/mapbox/outdoors-v12?access_token=${mapboxgl.accessToken}`)
        .then((res) => res.json())
        .then(initializeMap)
        .catch((err) => console.error(err))
    </script>
  </body>
</html>

Expected Behavior

In this example, dynamic hillshade lighting updates smoothly. I would expect it to work this way when 3D terrain is added to the map as well.

Actual Behavior

In v2.15, the hillshade only updates smoothly in 3D under a very specific scenario. Under all other scenarios, the lighting updates on camera stop and mouse up.

In v3+, the lighting behavior is slightly different, but still does not update smoothly.

stepankuzmin commented 1 month ago

Hi @matteiben-onx,

Could you please elaborate more on the expected behavior? Starting with v3.x, GL JS no longer supports the optimizeForTerrain map option. Layer rendering on the globe and terrain is always optimized now.

matteiben-onx commented 1 month ago

@stepankuzmin Sure thing

What I'd expect is shown in seconds 2 - 7 in this video https://github.com/user-attachments/assets/f3f3c43a-e129-49cb-b805-cb1553eaed98

Notice as the camera pans, the lighting updates smoothly. As pointed out above, the smoothly updating lighting is not the norm, even on v2.15.0. I had to get the map into a very specific scenario to result in the smooth lighting updates.

Using v3.4 (with optimizeForTerrain removed), the gif below shows a behavior where the lighting only updates once the camera has stopped and on mouseup.

hillshade-v3 4

Let me know if I can provide more details.

rob-g-d commented 1 month ago

Hi Team Mapbox - appreciate the sync this afternoon. After reviewing our upcoming roadmap intentions, we would appreciate the MB team being able to prioritize looking at this since it's impactful to the customer experience of an upcoming release we are planning.