-
Notifications
You must be signed in to change notification settings - Fork 2.3k
Add a configurable Fog effect #10564
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
72916b7
to
e8f7d74
Compare
Marking this PR as ready for review. There are still a few benchmarking job runs that I'd like to run against this PR from the bench suite but we're not expecting any more major changes to happen other than spec details and review changes. The work from @arindam1993 on symbols will be easier to digest as a separate PR. |
src/style-spec/reference/v8.json
Outdated
"zoom" | ||
] | ||
}, | ||
"doc": "The near and far limits of the fog layer. Units are relative to the map height, so that 2.0 corresponds to twice the map height, in pixels.", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can this new unit type be named and defined so that it can be referenced uniformly across other style properties or in API docs as well
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we multiply by 100, it's CSS vh
units, but otherwise I'm not sure the best name for it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The near and far limits of the fog layer.
This suggests that fog only exists within the range. It might be good to document that this is the range that fog fades in over
673559b
to
7a5554a
Compare
I think we need to revisit the start/end range units. With the current units the appearance of fog changes very significantly when you:
Set the debug/fog.html page to these settings to see the problem. The problem is still visible with only haze and a less sharp cutoff but these settings make it easier to see in screenshots. Pitching exampleAs you pitch fog seems to appear and then recede. Areas that are covered at medium pitches are not covered if you pitch slightly more or slightly less. These all look like pretty different amounts of fog to me. Fog also looks significantly different with different map heights but I'm not sure what the behavior in that case should be. |
@ansis there's a possibility you're seeing something different, but to the best of my knowledge, a good share of what you're seeing results from us not knowing how far away the terrain is (alternatively, what the zoom level is). Near sea level, things work pretty well, e.g. http://localhost:9966/debug/fog.html#13.75/-44.65923/167.90228/-47.8/74. At high elevation though, we don't have a reliable way of telling whether what's in front of the camera is a passing hill which shouldn't affect the range, or whether it's the dominant terrain. Or to look at it differently, if you pan, the zoom level changes sporadically as hills pass in front of the camera. To avoid fog jumping all over the place, we shoot a ray to sea level. When you're at sea level, that's fine. When you're viewing high elevation terrain, that may be very far away, essentially making fog disappear. I tried to average the local minimum aabb elevation of the loaded tiles (with a kernel to favor what's near the camera). It helped. At high elevations, the fog was pulled 3-4x closer. I might consider resurrecting that. The downside is that as you pan, new tiles load, and it's a bit jumpy. It would probably need to be eased in time. Also, a single low-elevation pixel (of which there are some exceptionally bizarre ones), affect fog dramatically. |
Also, @ansis, here's what I see for resizing the viewport at sea level, which seems to me to work pretty well. resize.mp4It seems reasonable to me at high elevation as well. Maybe not quite as good (I think due to the above sea-level-ray issue, which is also the main culprit when you change the pitch) high-elevation.mp4If we just deal in pixel units (instead of pixel units divided by viewport height), the fog range has a strong dependence on map height: without-viewport-scaling.mp4 |
Yeah, it looks like that is a large part of it. This is something we need to fix or we need to approach this differently. Fog should look pretty consistent across different pitches and at different elevations. |
While I don't disagree, I don't think this is a blocker. Opening this up for improvements in subsequent work chunks is probably the best approach to prevent blocking this PR from merging. As fog is an opt-in feature that has convenient default at far distance (mitigating that specific pitch issue), it would be a fairly subtle change accomodating for the closer range case, that I don't foresee being the most common case. |
d965d76
to
7db9f61
Compare
69bfce9
to
798728e
Compare
* Continuously adjust the fog scale by raycasting * Remove unused function * Fix linter errors * Name thresholds for clarity * Hard-code sampling pattern * Rearrange conditional * Adjust sampling by horizon line * Replace arbitrary constant with epsilon * Remove stray import * Simplify logic * Only calc fog matrices on avg elevation change * Fix cloning of transform * Fix bug in state management * Fix linter errors * Remove debug code * Move samples points inside function * Fix accidental call to calcMatrices * We still love you, flow * Clarify code * Add tests for EasedVariable * Add one more test
Make sure to un/post multiply alpha before/after applying sky gradient contribution, as color ramps are premultiplied-alpha colors. This prevents washed out colors when using transparency.
* Account for FOV in fog * Fix a typo * Fix flow errors * Fix a factor of two to make tests pass * Fix bad merge * Fix tests * Shift fog range zero to the map center * Fix debug * Fix ranges to account for fov * Remove unused variable
when trying to reason about them. As the units of the range are representing 3d distances on a sphere centered around camera center point, they can hardly be reasoned about in terms of window height.
* Fix race condition in fog terrain sampling * Use frame start time stamp * Update render test expectation
* Remove fog vh units * Fix floating point errors in test expectations * Fix geo test
* Skip unnecessary uniform code path when terrain is rendering to texture On a typical frame with terrain this was called ~200 times, where only 20 were necessary (~10%) * Fix invalid transition evaluation When `delay` was undefined but not `now`, properties.begin and properties.end were assigned incorrect values, leading to an immediate transition interpolation. This was noticed when testing transitions on fog. * Fix style.hasTransition() when fog is transitioning values * Add a fog demo for release
29f0846
to
a0d6705
Compare
@@ -2426,6 +2486,17 @@ class Map extends Camera { | |||
this._canvas.style.height = `${height}px`; | |||
} | |||
|
|||
_addMarker(marker: Marker) { | |||
this._markers.push(marker); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It seems a bit messy to have some marker changes happen by the marker listening to map events and other changes to happen by the map calling methods directly on them.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But also don't have a clear suggestion here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A couple small things, but I don't think I'd consider any of my feedback blocking. 👏🌁
float fog_horizon_blending(vec3 camera_dir) { | ||
float t = max(0.0, camera_dir.z / u_fog_horizon_blend); | ||
// Factor of 3 chosen to roughly match smoothstep. | ||
// See: https://www.desmos.com/calculator/pub31lvshf |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I imagine these desmos links as being a nice convenience but not essential to understanding the code, but maybe it'd be a good idea for me to screenshot them and add them to the github PR before this work is all water under the bridge.
"horizon-blend": { | ||
"type": "number", | ||
"property-type": "data-constant", | ||
"default": 0.1, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Because of the fog horizon fix and just for aesthetics, it's advisable to increase this with a zoom expression as you increase the zoom. I hope that doesn't get lost by the time this makes its way to the designers.
need to expose it publicly
Co-authored-by: Ricky Reusser <rreusser@users.noreply.github.com>
Co-authored-by: Ricky Reusser <rreusser@users.noreply.github.com>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks amazing to me!
Will go ahead with merging in a bit, captured the last review comments as follow ups:
Thanks for the multiple reviews @ansis and nice work on this @rreusser and @arindam1993 . |
This PR adds configurable fog to the style specification. Mapbox-gl maps use draw order and hues as a way to prioritize categories of layers. In 3D (e.g. high pitch or with terrain), visual hierarchies may be lost due to arbitrary viewing angles. As a result, the position of objects on the vertical axis becomes our main visual hint to understanding where objects fall at the distance.
The fog effect helps to smooth the horizon line and gives a better understanding of the scene layout in 3D, it is an important visual clue as we are used to evaluating the distance of objects in a landscape by how blue they appear in relation to each other. In addition to visual features, fog can be used to reduce the perceived scene as it allows for a fog tile culling optimization that reduces tile count fetches, as the viewable bounds can be set closer to the viewer.
It is implemented with the following considerations:
The fog effect is applied at a very late stage of the shader processing pipeline for all layers except terrain (forward shading).
fill-extrusion
for this to work.background.mov
Markers use a few different opacity stops at which they evaluate their opacity against fog. As markers are not rendered natively, directly updating their opacity could result in style re-layout and a performance slow-down. The tradeoff is to have a slightly less accurate opacity for a faster marker update loop.
markers-stops.mov
Tile fog culling is implemented at a very late stage of the fog tile cover after the initial cover finished defining the visible tileset with frustum culling. It can be used as a mean to improve styling performance when visible distance is not a strict requirement of the style.
culling-range.1.mov
The fog is a root property and can be used with the following specification:
rgba
color, the alpha component is unused.rgba
color, the alpha component is unused.Symbols are automatically clipped on the CPU at a given fog opacity (currently 90%) to prevent showing labels without surrounding context. We are working on further improvements to labeling to have more granular control per feature for a given fog opacity. Doing it on the CPU has the advantage of preventing false positives compared to a GPU approach, as occluded labels would not hide other labels.
map.setFog({})
.Optimization that were not implemented in this PR and left for further investigation:
Since tiles may be drawn a null fog/haze contribution (basically running all of the fragment operations that results in no fog contribution at all) we could evaluate the fog opacity of the furthest tile corner CPU side and check if it's < fog start range. We would switch the shader variant to use a non-fog shader (we can do that easily with the define). This could reduce fragment ops as these tiles are close to the viewer and have a lot of screen area/fragments to work upon. The shader switches would only happen once as tiles are already sorted by distance to the viewer. The tiles that would be subject to this switch are represented in blue here:
cc @mapbox/gl-native, @mapbox/map-design-team
Launch Checklist
@mapbox/map-design-team
@mapbox/static-apis
if this PR includes style spec API or visual changes@mapbox/gl-native
if this PR includes shader changes or needs a native portmapbox-gl-js
changelog:<changelog>Add a configurable fog effect as a root style specification</changelog>