Skip to content

Commit

Permalink
Don't leave alpha of fog color unused (#10633)
Browse files Browse the repository at this point in the history
* Don't leave alpha of fog color unused

* Apply review comments: Simplify flow, reduce uniforms

* Fix naming

* Update docs

* Fix flow
  • Loading branch information
karimnaaji committed May 4, 2021
1 parent ad2ea60 commit 1e7e2e1
Show file tree
Hide file tree
Showing 21 changed files with 284 additions and 83 deletions.
14 changes: 11 additions & 3 deletions debug/fog.html
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
this.start = 200;
this.end = 1200;
this.color = [255, 255, 255];
this.opacity = 1.0;
this.horizonBlend = 0.1;
this.showTileBoundaries = false;
};
Expand Down Expand Up @@ -99,7 +100,7 @@
enableFog.onFinishChange((value) => {
map.setFog(value ? {
"range": [guiParams.start, guiParams.end],
"color": 'rgba(' + guiParams.color[0] + ', ' + guiParams.color[1] + ', ' + guiParams.color[2] + ', 1.0)',
"color": 'rgba(' + guiParams.color[0] + ', ' + guiParams.color[1] + ', ' + guiParams.color[2] + ', ' + guiParams.opacity + ')',
"horizon-blend": guiParams.horizonBlend
} : null);
});
Expand All @@ -125,10 +126,17 @@
});
});

var opacity = fog.add(guiParams, 'opacity', 0.0, 1.0);
opacity.onChange((value) => {
map.setFog({
"color": 'rgba(' + guiParams.color[0] + ', ' + guiParams.color[1] + ', ' + guiParams.color[2] + ', ' + value + ')'
});
});

var color = fog.addColor(guiParams, 'color');
color.onChange((value) => {
map.setFog({
"color": 'rgba(' + value[0] + ', ' + value[1] + ', ' + value[2] + ', 1.0)',
"color": 'rgba(' + value[0] + ', ' + value[1] + ', ' + value[2] + ', ' + guiParams.opacity + ')'
});
});
};
Expand Down Expand Up @@ -189,7 +197,7 @@

map.setFog(guiParams.enableFog ? {
"range": [guiParams.start, guiParams.end],
"color": 'rgba(' + guiParams.color[0] + ', ' + guiParams.color[1] + ', ' + guiParams.color[2] + ', 1.0)',
"color": 'rgba(' + guiParams.color[0] + ', ' + guiParams.color[1] + ', ' + guiParams.color[2] + ', ' + guiParams.opacity + ')',
"horizon-blend": guiParams.horizonBlend
} : null);

Expand Down
37 changes: 30 additions & 7 deletions src/render/fog.js
Original file line number Diff line number Diff line change
@@ -1,25 +1,48 @@
// @flow

import Context from '../gl/context.js';
import type {UniformLocations} from './uniform_binding.js';

import {Uniform1f, Uniform2f, Uniform3f, UniformMatrix4f} from './uniform_binding.js';
import type {UniformLocations, UniformValues} from './uniform_binding.js';
import type {UnwrappedTileID} from '../source/tile_id.js';
import Painter from './painter.js';
import Fog from '../style/fog.js';
import {Uniform1f, Uniform2f, Uniform4f, UniformMatrix4f} from './uniform_binding.js';

export type FogUniformsType = {|
'u_fog_matrix': UniformMatrix4f,
'u_fog_range': Uniform2f,
'u_fog_color': Uniform3f,
'u_fog_color': Uniform4f,
'u_fog_horizon_blend': Uniform1f,
'u_fog_temporal_offset': Uniform1f,
'u_fog_opacity': Uniform1f,

|};

export const fogUniforms = (context: Context, locations: UniformLocations): FogUniformsType => ({
'u_fog_matrix': new UniformMatrix4f(context, locations.u_fog_matrix),
'u_fog_range': new Uniform2f(context, locations.u_fog_range),
'u_fog_color': new Uniform3f(context, locations.u_fog_color),
'u_fog_color': new Uniform4f(context, locations.u_fog_color),
'u_fog_horizon_blend': new Uniform1f(context, locations.u_fog_horizon_blend),
'u_fog_temporal_offset': new Uniform1f(context, locations.u_fog_temporal_offset),
'u_fog_opacity': new Uniform1f(context, locations.u_fog_opacity),
});

export const fogUniformValues = (
painter: Painter,
fog: Fog,
tileID: ?UnwrappedTileID,
fogOpacity: number
): UniformValues<FogUniformsType> => {
const fogColor = fog.properties.get('color');
const temporalOffset = (painter.frameCounter / 1000.0) % 1;
const fogColorUnpremultiplied = [
fogColor.r / fogColor.a,
fogColor.g / fogColor.a,
fogColor.b / fogColor.a,
fogOpacity
];
return {
'u_fog_matrix': tileID ? painter.transform.calculateFogTileMatrix(tileID) : painter.identityMat,
'u_fog_range': fog.properties.get('range'),
'u_fog_color': fogColorUnpremultiplied,
'u_fog_horizon_blend': fog.properties.get('horizon-blend'),
'u_fog_temporal_offset': temporalOffset
};
};
41 changes: 14 additions & 27 deletions src/render/painter.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import shaders from '../shaders/shaders.js';
import Program from './program.js';
import {programUniforms} from './program/program_uniforms.js';
import Context from '../gl/context.js';
import {FOG_PITCH_END} from '../style/fog_helpers.js';
import {fogUniformValues} from '../render/fog.js';
import DepthMode from '../gl/depth_mode.js';
import StencilMode from '../gl/stencil_mode.js';
import ColorMode from '../gl/color_mode.js';
Expand Down Expand Up @@ -179,13 +179,13 @@ class Painter {
terrain.update(style, this.transform, cameraChanging);
}

_updateFog() {
if (this.transform.pitch <= FOG_PITCH_END || !(this.style && this.style.fog)) {
_updateFog(style: Style) {
const fog = style.fog;
if (!fog || (fog && fog.getFogOpacity(this.transform.pitch) !== 1.0)) {
this.transform.fogCullDistSq = null;
return;
}

const fog = this.style.fog;
const fogStart = fog.properties.get('range')[0];
const fogEnd = fog.properties.get('range')[1];

Expand Down Expand Up @@ -753,13 +753,14 @@ class Painter {
const terrain = this.terrain && !this.terrain.renderingToTexture; // Enables elevation sampling in vertex shader.
const rtt = this.terrain && this.terrain.renderingToTexture;
const fog = this.style && this.style.fog;
const fogOpacity = fog && fog.getFogPitchFactor(this.transform.pitch);

const defines = [];

if (terrain) defines.push('TERRAIN');
// When terrain is active, fog is rendered as part of draping, not as part of tile
// rendering. Removing the fog flag during tile rendering avoids additional defines.
if (fog && fogOpacity !== 0.0 && !rtt) defines.push('FOG');
if (fog && !rtt && fog.getFogOpacity(this.transform.pitch) !== 0.0) {
defines.push('FOG');
}
if (rtt) defines.push('RENDER_TO_TEXTURE');
if (this._showOverdrawInspector) defines.push('OVERDRAW_INSPECTOR');
return defines;
Expand Down Expand Up @@ -837,26 +838,12 @@ class Painter {
}

prepareDrawProgram(context: Context, program: Program<*>, tileID: ?UnwrappedTileID) {
const fog = this.style && this.style.fog;
const fogOpacity = (fog && fog.getFogPitchFactor(this.transform.pitch)) || 0.0;
if (fog && fogOpacity !== 0.0) {
const temporalOffset = (this.frameCounter / 1000.0) % 1;
const fogColor = fog.properties.get('color');
const fogColorUnpremultiplied = fogColor.a === 0 ? [0, 0, 0] : [
fogColor.r / fogColor.a,
fogColor.g / fogColor.a,
fogColor.b / fogColor.a
];
const uniforms = {};

uniforms['u_fog_matrix'] = tileID ? this.transform.calculateFogTileMatrix(tileID) : this.identityMat;
uniforms['u_fog_range'] = fog.properties.get('range');
uniforms['u_fog_color'] = fogColorUnpremultiplied;
uniforms['u_fog_horizon_blend'] = fog.properties.get('horizon-blend');
uniforms['u_fog_temporal_offset'] = temporalOffset;
uniforms['u_fog_opacity'] = fogOpacity;

program.setFogUniformValues(context, uniforms);
const fog = this.style.fog;
if (fog) {
const fogOpacity = fog.getFogOpacity(this.transform.pitch);
if (fogOpacity !== 0.0) {
program.setFogUniformValues(context, fogUniformValues(this, fog, tileID, fogOpacity));
}
}
}

Expand Down
6 changes: 3 additions & 3 deletions src/shaders/_prelude.glsl
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

#ifdef FOG

uniform mediump float u_fog_opacity;
uniform mediump vec4 u_fog_color;
uniform mediump vec2 u_fog_range;
uniform mediump float u_fog_horizon_blend;

Expand All @@ -24,7 +24,7 @@ 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
return u_fog_opacity * exp(-3.0 * t * t);
return u_fog_color.a * exp(-3.0 * t * t);
}

// Compute a ramp for fog opacity
Expand All @@ -38,7 +38,7 @@ float fog_opacity(float t) {
falloff *= falloff * falloff;

// Scale and clip to 1 at the far limit
return u_fog_opacity * min(1.0, 1.00747 * falloff);
return u_fog_color.a * min(1.0, 1.00747 * falloff);
}

#endif
7 changes: 3 additions & 4 deletions src/shaders/_prelude_fog.fragment.glsl
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
#ifdef FOG

uniform vec3 u_fog_color;
uniform float u_fog_temporal_offset;

// This function is only used in rare places like heatmap where opacity is used
Expand All @@ -14,18 +13,18 @@ vec3 fog_apply(vec3 color, vec3 pos) {
float depth = length(pos);
float opacity = fog_opacity(fog_range(depth));
opacity *= fog_horizon_blending(pos / depth);
return mix(color, u_fog_color, opacity);
return mix(color, u_fog_color.rgb, opacity);
}

// Apply fog computed in the vertex shader
vec3 fog_apply_from_vert(vec3 color, float fog_opac) {
return mix(color, u_fog_color, fog_opac);
return mix(color, u_fog_color.rgb, fog_opac);
}

// Assumes z up
vec3 fog_apply_sky_gradient(vec3 camera_ray, vec3 sky_color) {
float horizon_blend = fog_horizon_blending(normalize(camera_ray));
return mix(sky_color, u_fog_color, horizon_blend);
return mix(sky_color, u_fog_color.rgb, horizon_blend);
}

// Un-premultiply the alpha, then blend fog, then re-premultiply alpha.
Expand Down
2 changes: 1 addition & 1 deletion src/style-spec/reference/v8.json
Original file line number Diff line number Diff line change
Expand Up @@ -3719,7 +3719,7 @@
]
},
"transition": true,
"doc": "The color of the fog. If provided as an `rgba` color, the alpha component must be nonzero, but is otherwise unused.",
"doc": "The color of the fog. Using opacity is recommended only for smoothly transitioning fog on/off as anything less than 100% opacity results in more tiles loaded and drawn.",
"sdk-support": {
"basic functionality": {
"js": "2.3.0"
Expand Down
8 changes: 0 additions & 8 deletions src/style-spec/validate/validate_fog.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import validate from './validate.js';
import getType from '../util/get_type.js';
import {isExpression} from '../expression/index.js';
import {deepUnbundle} from '../util/unbundle_jsonlint.js';
import {parseCSSColor} from 'csscolorparser';

export default function validateFog(options) {
const fog = options.value;
Expand All @@ -21,13 +20,6 @@ export default function validateFog(options) {
return errors;
}

if (fog.color && !isExpression(deepUnbundle(fog.color))) {
const fogColor = parseCSSColor(fog.color);
if (fogColor && fogColor[3] === 0) {
errors = errors.concat([new ValidationError('fog', fog, 'fog.color alpha must be nonzero.')]);
}
}

if (fog.range && !isExpression(deepUnbundle(fog.range)) && fog.range[0] >= fog.range[1]) {
errors = errors.concat([new ValidationError('fog', fog, 'fog.range[0] can\'t be greater than or equal to fog.range[1]')]);
}
Expand Down
8 changes: 7 additions & 1 deletion src/style/fog.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,8 @@ class Fog extends Evented {
get state(): FogState {
return {
range: this.properties.get('range'),
horizonBlend: this.properties.get('horizon-blend')
horizonBlend: this.properties.get('horizon-blend'),
alpha: this.properties.get('color').a
};
}

Expand All @@ -70,6 +71,11 @@ class Fog extends Evented {
return smoothstep(FOG_PITCH_START, FOG_PITCH_END, pitch);
}

getFogOpacity(pitch: number): number {
const fogColor = (this.properties && this.properties.get('color')) || 1.0;
return this.getFogPitchFactor(pitch) * fogColor.a;
}

getOpacityAtLatLng(lngLat: LngLat, transform: Transform): number {
return getFogOpacityAtLngLat(this.state, lngLat, transform);
}
Expand Down
5 changes: 3 additions & 2 deletions src/style/fog_helpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ export const FOG_SYMBOL_CLIPPING_THRESHOLD = 0.9;

export type FogState = {
range: [number, number],
horizonBlend: number
horizonBlend: number,
alpha: number
};

// As defined in _prelude_fog.fragment.glsl#fog_opacity
Expand All @@ -30,7 +31,7 @@ export function getFogOpacity(state: FogState, pos: Array<number>, pitch: number
falloff *= falloff * falloff;
falloff = Math.min(1.0, 1.00747 * falloff);

return falloff * fogPitchOpacity;
return falloff * fogPitchOpacity * state.alpha;
}

export function getFogOpacityAtTileCoord(state: FogState, x: number, y: number, z: number, tileId: UnwrappedTileID, transform: Transform): number {
Expand Down
2 changes: 1 addition & 1 deletion src/ui/map.js
Original file line number Diff line number Diff line change
Expand Up @@ -2658,7 +2658,7 @@ class Map extends Camera {
// need for the current transform
if (this.style && this._sourcesDirty) {
this._sourcesDirty = false;
this.painter._updateFog();
this.painter._updateFog(this.style);
this._updateTerrain(); // Terrain DEM source updates here and skips update in style._updateSources.
this.style._updateSources(this.transform);
}
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
29 changes: 29 additions & 0 deletions test/integration/render-tests/fog/color-opacity/style.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
{
"version": 8,
"metadata": {
"test": {
"height": 256,
"allowed": 0.0005
}
},
"center": [
52.499167,
13.418056
],
"zoom": 16,
"pitch": 70,
"fog": {
"range": [1, 4],
"color": "rgba(255, 30, 35, 0.8)"
},
"sources": {},
"layers": [
{
"id": "background",
"type": "background",
"paint": {
"background-color": "beige"
}
}
]
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading

0 comments on commit 1e7e2e1

Please sign in to comment.