Skip to content

Commit

Permalink
Fog style spec changes (#10590)
Browse files Browse the repository at this point in the history
* Clean up fog style properties

* Present a more reasonable example

* Fix fog tests and validations

* Improve haze wording
  • Loading branch information
rreusser authored Apr 16, 2021
1 parent e8f7d74 commit aa6c9fa
Show file tree
Hide file tree
Showing 20 changed files with 115 additions and 169 deletions.
78 changes: 38 additions & 40 deletions debug/fog.html
Original file line number Diff line number Diff line change
Expand Up @@ -33,13 +33,13 @@
this.enableSky = true;
this.terrainExaggeration = 1.5;
this.style = 'satellite-streets-v11';
this.fogRangeStart = 2;
this.fogRangeEnd = 12.0;
this.fogColor = [255, 255, 255];
this.fogHazeColor = [109, 123, 180];
this.fogHazeEnergy = 0.8;
this.fogStrength = 1.0;
this.fogSkyBlend = 0.1;
this.start = 2;
this.end = 12.0;
this.color = [255, 255, 255];
this.hazeColor = [109, 123, 180];
this.hazeOpacity = 0.5;
this.density = 1.0;
this.skyBlend = 0.1;
this.showTileBoundaries = false;
};

Expand Down Expand Up @@ -101,61 +101,60 @@
var enableFog = fog.add(guiParams, 'enableFog');
enableFog.onFinishChange((value) => {
map.setFog(value ? {
"range": [guiParams.fogRangeStart, guiParams.fogRangeEnd],
"color": 'rgba(' + guiParams.fogColor[0] + ', ' + guiParams.fogColor[1] + ', ' + guiParams.fogColor[2] + ', 1.0)',
"haze-color": 'rgba(' + guiParams.fogHazeColor[0] + ', ' + guiParams.fogHazeColor[1] + ', ' + guiParams.fogHazeColor[2] + ', 1.0)',
"haze-energy": guiParams.fogHazeEnergy,
"strength": guiParams.fogStrength,
"sky-blend": guiParams.fogSkyBlend
"range": [guiParams.start, guiParams.end],
"color": 'rgba(' + guiParams.color[0] + ', ' + guiParams.color[1] + ', ' + guiParams.color[2] + ', 1.0)',
"haze-color": 'rgba(' + guiParams.hazeColor[0] + ', ' + guiParams.hazeColor[1] + ', ' + guiParams.hazeColor[2] + ', ' + guiParams.hazeOpacity + ')',
"density": guiParams.density,
"sky-blend": guiParams.skyBlend
} : null);
});

var fogRangeStart = fog.add(guiParams, 'fogRangeStart', 0.0, 15.0);
fogRangeStart.onChange((value) => {
var start = fog.add(guiParams, 'start', 0.0, 15.0);
start.onChange((value) => {
map.setFog({
"range": [value, guiParams.fogRangeEnd],
"range": [value, guiParams.end],
});
});

var fogRangeEnd = fog.add(guiParams, 'fogRangeEnd', 0.0, 15.0);
fogRangeEnd.onChange((value) => {
var end = fog.add(guiParams, 'end', 0.0, 15.0);
end.onChange((value) => {
map.setFog({
"range": [guiParams.fogRangeStart, value],
"range": [guiParams.start, value],
});
});

var fogStrength = fog.add(guiParams, 'fogStrength', 0, 1);
fogStrength.onChange((value) => {
var density = fog.add(guiParams, 'density', 0, 1);
density.onChange((value) => {
map.setFog({
"strength": value,
"density": value,
});
});

var fogHazeEnergy = fog.add(guiParams, 'fogHazeEnergy', 0, 2);
fogHazeEnergy.onChange((value) => {
var skyBlend = fog.add(guiParams, 'skyBlend', 0.0, 1.0);
skyBlend.onChange((value) => {
map.setFog({
"haze-energy": value,
"sky-blend": value,
});
});

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

var fogColor = fog.addColor(guiParams, 'fogColor');
fogColor.onChange((value) => {
var hazeColor = fog.addColor(guiParams, 'hazeColor');
hazeColor.onChange((value) => {
map.setFog({
"color": 'rgba(' + value[0] + ', ' + value[1] + ', ' + value[2] + ', 1.0)',
"haze-color": 'rgba(' + value[0] + ', ' + value[1] + ', ' + value[2] + ', ' + guiParams.hazeOpacity + ')',
});
});

var fogHazeColor = fog.addColor(guiParams, 'fogHazeColor');
fogHazeColor.onChange((value) => {
var hazeOpacity = fog.add(guiParams, 'hazeOpacity', 0, 1);
hazeOpacity.onChange((value) => {
map.setFog({
"haze-color": 'rgba(' + value[0] + ', ' + value[1] + ', ' + value[2] + ', 1.0)',
"haze-color": 'rgba(' + guiParams.hazeColor[0] + ', ' + guiParams.hazeColor[1] + ', ' + guiParams.hazeColor[2] + ', ' + guiParams.hazeOpacity + ')',
});
});
};
Expand Down Expand Up @@ -215,12 +214,11 @@
});

map.setFog(guiParams.enableFog ? {
"range": [guiParams.fogRangeStart, guiParams.fogRangeEnd],
"color": 'rgba(' + guiParams.fogColor[0] + ', ' + guiParams.fogColor[1] + ', ' + guiParams.fogColor[2] + ', 1.0)',
"haze-color": 'rgba(' + guiParams.fogHazeColor[0] + ', ' + guiParams.fogHazeColor[1] + ', ' + guiParams.fogHazeColor[2] + ', 1.0)',
"strength": guiParams.fogStrength,
"haze-energy": guiParams.fogHazeEnergy,
"sky-blend": guiParams.fogSkyBlend
"range": [guiParams.start, guiParams.end],
"color": 'rgba(' + guiParams.color[0] + ', ' + guiParams.color[1] + ', ' + guiParams.color[2] + ', 1.0)',
"haze-color": 'rgba(' + guiParams.hazeColor[0] + ', ' + guiParams.hazeColor[1] + ', ' + guiParams.hazeColor[2] + ', ' + guiParams.hazeOpacity + ')',
"density": guiParams.density,
"sky-blend": guiParams.skyBlend
} : null);

map.setTerrain(guiParams.enableTerrain ? {
Expand Down
28 changes: 18 additions & 10 deletions src/render/painter.js
Original file line number Diff line number Diff line change
Expand Up @@ -188,11 +188,11 @@ class Painter {
const fog = this.style.fog;
const fogStart = fog.properties.get('range')[0];
const fogEnd = fog.properties.get('range')[1];
const fogStrength = fog.properties.get('strength');
const fogDensity = fog.properties.get('density');
// We start culling where the fog opacity function hits 98%, leaving
// a non-noticeable opacity change threshold. We use an arbitrary function
// which bounds the true answer. See: https://www.desmos.com/calculator/lw03ldsuhy
const fogBoundFraction = 1 - 0.22 * Math.exp(4 * (fogStrength - 1));
const fogBoundFraction = 1 - 0.22 * Math.exp(4 * (fogDensity - 1));
const fogCullDist = fogStart + (fogEnd - fogStart) * fogBoundFraction;

this.transform.fogCullDistSq = fogCullDist * fogCullDist;
Expand Down Expand Up @@ -757,7 +757,7 @@ class Painter {
const rtt = this.terrain && this.terrain.renderingToTexture;
const fog = this.style && this.style.fog;
const fogOpacity = fog && fog.getFogPitchFactor(this.transform.pitch);
const haze = fog && fog.properties && fog.properties.get('haze-energy') > 0;
const haze = fog && fog.properties && fog.properties.get('haze-color').a > 0;

const defines = [];
if (terrain) defines.push('TERRAIN');
Expand Down Expand Up @@ -849,23 +849,31 @@ class Painter {
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'] = [fogColor.r, fogColor.g, fogColor.b];
uniforms['u_fog_exponent'] = Math.max(1e-3, 12 * Math.pow(1 - fog.properties.get('strength'), 2));
uniforms['u_fog_color'] = fogColorUnpremultiplied;
uniforms['u_fog_exponent'] = Math.max(1e-3, 12 * Math.pow(1 - fog.properties.get('density'), 2));
uniforms['u_fog_sky_blend'] = fog.properties.get('sky-blend');
uniforms['u_fog_temporal_offset'] = temporalOffset;
uniforms['u_fog_opacity'] = fogOpacity;

if (fog.properties.get('haze-energy') > 0) {
if (fog.properties.get('haze-color').a > 0) {
const hazeColor = fog.properties.get('haze-color');
const hazeBaseAmpl = 5;
const hazeColorLinear = [
Math.pow(hazeColor.r, 2.2),
Math.pow(hazeColor.g, 2.2),
Math.pow(hazeColor.b, 2.2),
fog.properties.get('haze-energy')
hazeBaseAmpl * Math.pow(hazeColor.r, 2.2),
hazeBaseAmpl * Math.pow(hazeColor.g, 2.2),
hazeBaseAmpl * Math.pow(hazeColor.b, 2.2),
// Alpha is already premultiplied into the color, so we use haze color alpha to
// specify how much tone mapping to apply, reaching full strength after opacity=0.5.
Math.min(1, 2.0 * hazeColor.a)
];

uniforms['u_haze_color_linear'] = hazeColorLinear;
Expand Down
4 changes: 2 additions & 2 deletions src/shaders/_prelude_fog.fragment.glsl
Original file line number Diff line number Diff line change
Expand Up @@ -54,12 +54,12 @@ vec3 fog_apply(vec3 color, vec3 pos) {
float fog_opac = haze_opac * pow(smoothstep(0.0, 1.0, t), u_fog_exponent);

#ifdef FOG_HAZE
vec3 haze = (haze_opac * u_haze_color_linear.a) * u_haze_color_linear.rgb;
vec3 haze = haze_opac * u_haze_color_linear.rgb;

// The smoothstep fades in tonemapping slightly before the fog layer. This violates
// the principle that fog should not have an effect outside the fog layer, but the
// effect is hardly noticeable except on pure white glaciers.
float tonemap_strength = u_fog_opacity * min(1.0, u_haze_color_linear.a) * smoothstep(-0.5, 0.25, t);
float tonemap_strength = u_fog_opacity * u_haze_color_linear.a * smoothstep(-0.5, 0.25, t);
color = srgb_to_linear(color);
color = mix(color, tonemap(color + haze), tonemap_strength);
color = linear_to_srgb(color);
Expand Down
4 changes: 2 additions & 2 deletions src/shaders/_prelude_fog.vertex.glsl
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,12 @@ void fog_haze(vec3 pos, out float fog_opac, out vec4 haze) {
fog_opac = haze_opac * pow(smoothstep(0.0, 1.0, t), u_fog_exponent);

#ifdef FOG_HAZE
haze.rgb = (haze_opac * u_haze_color_linear.a) * u_haze_color_linear.rgb;
haze.rgb = haze_opac * u_haze_color_linear.rgb;

// The smoothstep fades in tonemapping slightly before the fog layer. This violates
// the principle that fog should not have an effect outside the fog layer, but the
// effect is hardly noticeable except on pure white glaciers.
haze.a = u_fog_opacity * min(1.0, u_haze_color_linear.a) * smoothstep(-0.5, 0.25, t);
haze.a = u_fog_opacity * u_haze_color_linear.a * smoothstep(-0.5, 0.25, t);
#endif
}

Expand Down
33 changes: 7 additions & 26 deletions src/style-spec/reference/v8.json
Original file line number Diff line number Diff line change
Expand Up @@ -3718,32 +3718,14 @@
]
},
"transition": true,
"doc": "The color of the fog. If provided as an `rgba` color, the alpha component is unused.",
"doc": "The color of the fog. If provided as an `rgba` color, the alpha component must be nonzero, but is otherwise unused.",
"sdk-support": {
"basic functionality": {
"js": "2.3.0"
}
}
},
"haze-color": {
"type": "color",
"property-type": "data-constant",
"default": "#7287d5",
"expression": {
"interpolated": true,
"parameters": [
"zoom"
]
},
"transition": true,
"doc": "The color of the haze component of the fog, used only when haze energy is nonzero. If provided as an `rgba` color, the alpha component is unused.",
"sdk-support": {
"basic functionality": {
"js": "2.3.0"
}
}
},
"strength": {
"density": {
"type": "number",
"property-type": "data-constant",
"default": 1.0,
Expand All @@ -3755,26 +3737,25 @@
]
},
"transition": true,
"doc": "Controls the strength of the fog effect. Reducing the fog strength pushes the fog farther toward the horizon, leaving more visible space for haze, if enabled. When haze energy is zero, this will usually be 1.0.",
"doc": "Controls the strength of fog within the fog range. Reducing the density pushes the fog farther toward the horizon, leaving more visible space for haze, if enabled. When haze opacity is zero, density will usually be 1.0.",
"sdk-support": {
"basic functionality": {
"js": "2.3.0"
}
}
},
"haze-energy": {
"type": "number",
"haze-color": {
"type": "color",
"property-type": "data-constant",
"default": 0.0,
"minimum": 0.0,
"default": "rgba(114,135,213,0)",
"expression": {
"interpolated": true,
"parameters": [
"zoom"
]
},
"transition": true,
"doc": "Controls the strength of haze in the fog layer. Haze is an additive effect which approximates the look of an atmosphere. Increasing this strengthens the haze. Reducing the fog strength pushes fog toward the horizon, making haze visible.",
"doc": "The color of the haze component of fog. Alpha adjusts the amount of haze. Note that haze is disabled by default (zero alpha); increase this value to enable the effect.",
"sdk-support": {
"basic functionality": {
"js": "2.3.0"
Expand Down
3 changes: 1 addition & 2 deletions src/style-spec/types.js
Original file line number Diff line number Diff line change
Expand Up @@ -90,9 +90,8 @@ export type TerrainSpecification = {|
export type FogSpecification = {|
"range"?: PropertyValueSpecification<[number, number]>,
"color"?: PropertyValueSpecification<ColorSpecification>,
"density"?: PropertyValueSpecification<number>,
"haze-color"?: PropertyValueSpecification<ColorSpecification>,
"strength"?: PropertyValueSpecification<number>,
"haze-energy"?: PropertyValueSpecification<number>,
"sky-blend"?: PropertyValueSpecification<number>
|}

Expand Down
8 changes: 8 additions & 0 deletions src/style-spec/validate/validate_fog.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import ValidationError from '../error/validation_error.js';
import validate from './validate.js';
import getType from '../util/get_type.js';
import {parseCSSColor} from 'csscolorparser';

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

if (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 && 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: 3 additions & 5 deletions src/style/fog.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,18 +17,16 @@ import type {FogState} from './fog_helpers.js';
type Props = {|
"range": DataConstantProperty<[number, number]>,
"color": DataConstantProperty<Color>,
"density": DataConstantProperty<number>,
"haze-color": DataConstantProperty<Color>,
"haze-energy": DataConstantProperty<number>,
"strength": DataConstantProperty<number>,
"sky-blend": DataConstantProperty<number>,
|};

const fogProperties: Properties<Props> = new Properties({
"range": new DataConstantProperty(styleSpec.fog.range),
"color": new DataConstantProperty(styleSpec.fog.color),
"density": new DataConstantProperty(styleSpec.fog["density"]),
"haze-color": new DataConstantProperty(styleSpec.fog["haze-color"]),
"haze-energy": new DataConstantProperty(styleSpec.fog["haze-energy"]),
"strength": new DataConstantProperty(styleSpec.fog["strength"]),
"sky-blend": new DataConstantProperty(styleSpec.fog["sky-blend"]),
});

Expand All @@ -49,7 +47,7 @@ class Fog extends Evented {
get state(): FogState {
return {
range: this.properties.get('range'),
strength: this.properties.get('strength')
density: this.properties.get('density')
};
}

Expand Down
8 changes: 4 additions & 4 deletions src/style/fog_helpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,14 @@ export const FOG_SYMBOL_CLIPPING_THRESHOLD = 0.9;

export type FogState = {
range: [number, number],
strength: number
density: number
};

// As defined in _prelude_fog.fragment.glsl#fog_opacity
export function getFogOpacity(state: FogState, depth: number, pitch: number): number {
const fogOpacity = smoothstep(FOG_PITCH_START, FOG_PITCH_END, pitch);
const [start, end] = state.range;
const fogStrength = state.strength;
const fogDensity = state.density;

// The fog is not physically accurate, so we seek an expression which satisfies a
// couple basic constraints:
Expand All @@ -42,9 +42,9 @@ export function getFogOpacity(state: FogState, depth: number, pitch: number): nu
falloff = Math.min(1.0, 1.00747 * falloff);

// From src/render/painter.js via fog uniforms:
const fogExponent = 12 * Math.pow(1 - fogStrength, 2);
const fogExponent = 12 * Math.pow(1 - fogDensity, 2);

// Account for fog strength
// Account for fog density
falloff *= Math.pow(smoothstep(0, 1, t), fogExponent);

return falloff * fogOpacity;
Expand Down
5 changes: 2 additions & 3 deletions src/ui/map.js
Original file line number Diff line number Diff line change
Expand Up @@ -2183,9 +2183,8 @@ class Map extends Camera {
* map.setFog({
* "range": [1.0, 12.0],
* "color": 'white',
* "haze-color": 'rgba(109, 123, 180, 1.0)',
* "strength": 1.0,
* "haze-energy": 1.0,
* "density": 1.0,
* "haze-color": 'rgba(109, 123, 180, 0.5)',
* "sky-blend": 0.1
* });
*/
Expand Down
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 aa6c9fa

Please sign in to comment.