Skip to content

Commit

Permalink
Offload terrain fog to vertex shader (#10549)
Browse files Browse the repository at this point in the history
* Offload terrain fog to vertex shader

* Don't set haze uniforms unless haze is present

* Remove haze varying unless haze is used

* Disable fog code path on the 2d case

* Remove redundant uniforms

* Simplify fog render logic

Co-authored-by: Karim Naaji <[email protected]>
  • Loading branch information
rreusser and karimnaaji committed Apr 16, 2021
1 parent b879867 commit 8783f40
Show file tree
Hide file tree
Showing 6 changed files with 104 additions and 30 deletions.
16 changes: 9 additions & 7 deletions src/render/fog.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,28 +3,30 @@
import Context from '../gl/context.js';
import type {UniformLocations} from './uniform_binding.js';

import {Uniform1f, Uniform2f, Uniform3f, UniformMatrix4f} from './uniform_binding.js';
import {Uniform1f, Uniform2f, Uniform3f, Uniform4f, UniformMatrix4f} from './uniform_binding.js';

export type FogUniformsType = {|
'u_fog_matrix': UniformMatrix4f,
'u_fog_range': Uniform2f,
'u_fog_color': Uniform3f,
'u_fog_exponent': Uniform1f,
'u_fog_opacity': Uniform1f,
'u_fog_sky_blend': Uniform1f,
'u_fog_temporal_offset': Uniform1f,
'u_haze_color_linear': Uniform3f,
'u_haze_energy': Uniform1f,
'u_haze_color_linear': Uniform4f,

// Precision may differ, so we must pass uniforms separately for use in a vertex shader
'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_exponent': new Uniform1f(context, locations.u_fog_exponent),
'u_fog_opacity': new Uniform1f(context, locations.u_fog_opacity),
'u_fog_sky_blend': new Uniform1f(context, locations.u_fog_sky_blend),
'u_fog_temporal_offset': new Uniform1f(context, locations.u_fog_temporal_offset),
'u_haze_color_linear': new Uniform3f(context, locations.u_haze_color_linear),
'u_haze_energy': new Uniform1f(context, locations.u_haze_energy),
'u_haze_color_linear': new Uniform4f(context, locations.u_haze_color_linear),

'u_fog_opacity': new Uniform1f(context, locations.u_fog_opacity),
});
24 changes: 17 additions & 7 deletions src/render/painter.js
Original file line number Diff line number Diff line change
Expand Up @@ -752,13 +752,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 haze = fog && fog.properties && fog.properties.get('haze-energy') > 0;

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 && !rtt) {
if (fog && fogOpacity !== 0.0 && !rtt) {
defines.push('FOG');
if (haze) defines.push('FOG_HAZE');
}
Expand Down Expand Up @@ -840,22 +841,31 @@ class Painter {

prepareDrawProgram(context: Context, program: Program<*>, tileID: ?UnwrappedTileID) {
const fog = this.style && this.style.fog;
if (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 hazeColor = fog.properties.get('haze-color');
const hazeColorLinear = [Math.pow(hazeColor.r, 2.2), Math.pow(hazeColor.g, 2.2), Math.pow(hazeColor.b, 2.2)];
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_opacity'] = fog.getFogPitchFactor(this.transform.pitch);
uniforms['u_fog_sky_blend'] = fog.properties.get('sky-blend');
uniforms['u_fog_temporal_offset'] = temporalOffset;
uniforms['u_haze_color_linear'] = hazeColorLinear;
uniforms['u_haze_energy'] = fog.properties.get('haze-energy');
uniforms['u_fog_opacity'] = fogOpacity;

if (fog.properties.get('haze-energy') > 0) {
const hazeColor = fog.properties.get('haze-color');
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')
];

uniforms['u_haze_color_linear'] = hazeColorLinear;
}

program.setFogUniformValues(context, uniforms);
}
Expand Down
38 changes: 26 additions & 12 deletions src/shaders/_prelude_fog.fragment.glsl
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
#ifdef FOG

uniform vec2 u_fog_range;
uniform vec3 u_fog_color;
uniform vec3 u_haze_color_linear;
uniform float u_haze_energy;
uniform float u_fog_opacity;
uniform float u_fog_sky_blend;
uniform float u_fog_temporal_offset;
uniform float u_fog_exponent;
uniform mediump vec2 u_fog_range;
uniform mediump float u_fog_opacity;
uniform mediump vec4 u_haze_color_linear;
uniform mediump float u_fog_exponent;

vec3 tonemap(vec3 color) {
// Use an exponential smoothmin between y=x and y=1 for tone-mapping
Expand All @@ -29,7 +28,7 @@ float fog_sky_blending(vec3 camera_dir) {
// by a smoothstep to a power to decrease the amount of fog relative to haze.
// - t: depth, rescaled to 0 at fogStart and 1 at fogEnd
// See: https://www.desmos.com/calculator/3taufutxid
// This function much match src/style/fog.js
// This function much match src/style/fog.js and _prelude_fog.vertex.glsl
float fog_opacity(float t) {
const float decay = 6.0;
float falloff = 1.0 - min(1.0, exp(-decay * t));
Expand All @@ -43,24 +42,24 @@ float fog_opacity(float t) {

// This function is only used in rare places like heatmap where opacity is used
// directly, outside the normal fog_apply method.
float fog_opacity (vec3 pos) {
float fog_opacity(vec3 pos) {
return fog_opacity((length(pos) - u_fog_range.x) / (u_fog_range.y - u_fog_range.x));
}

vec3 fog_apply(vec3 color, vec3 pos) {
// Map [near, far] to [0, 1]
float t = (length(pos) - u_fog_range.x) / (u_fog_range.y - u_fog_range.x);

float haze_opac = fog_opacity(pos);
float haze_opac = fog_opacity(t);
float fog_opac = haze_opac * pow(smoothstep(0.0, 1.0, t), u_fog_exponent);

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

// The smoothstep fades in tonemapping slightly before the fog layer. This causes
// 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_energy) * smoothstep(-0.5, 0.25, t);
// 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);
color = srgb_to_linear(color);
color = mix(color, tonemap(color + haze), tonemap_strength);
color = linear_to_srgb(color);
Expand All @@ -69,6 +68,21 @@ vec3 fog_apply(vec3 color, vec3 pos) {
return mix(color, u_fog_color, fog_opac);
}

// Apply fog and haze which were computed in the vertex shader
vec3 fog_apply_from_vert(vec3 color, float fog_opac
#ifdef FOG_HAZE
, vec4 haze
#endif
) {
#ifdef FOG_HAZE
color = srgb_to_linear(color);
color = mix(color, tonemap(color + haze.rgb), haze.a);
color = linear_to_srgb(color);
#endif

return mix(color, u_fog_color, fog_opac);
}

// Assumes z up
vec3 fog_apply_sky_gradient(vec3 camera_ray, vec3 sky_color) {
return mix(sky_color, u_fog_color, fog_sky_blending(normalize(camera_ray)));
Expand Down
34 changes: 34 additions & 0 deletions src/shaders/_prelude_fog.vertex.glsl
Original file line number Diff line number Diff line change
@@ -1,6 +1,18 @@
#ifdef FOG

uniform mat4 u_fog_matrix;
uniform mediump float u_fog_opacity;
uniform mediump float u_fog_exponent;
uniform mediump vec2 u_fog_range;
uniform mediump vec4 u_haze_color_linear;

// This function much match fog_opacity defined in _prelude_fog.fragment.glsl
float fog_opacity(float t) {
const float decay = 6.0;
float falloff = 1.0 - min(1.0, exp(-decay * t));
falloff *= falloff * falloff;
return u_fog_opacity * min(1.0, 1.00747 * falloff);
}

vec3 fog_position(vec3 pos) {
// The following function requires that u_fog_matrix be affine and result in
Expand All @@ -13,4 +25,26 @@ vec3 fog_position(vec2 pos) {
return fog_position(vec3(pos, 0));
}

void fog_haze(
vec3 pos, out float fog_opac
#ifdef FOG_HAZE
, out vec4 haze
#endif
) {
// Map [near, far] to [0, 1]
float t = (length(pos) - u_fog_range.x) / (u_fog_range.y - u_fog_range.x);

float haze_opac = fog_opacity(t);
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;

// 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);
#endif
}

#endif
11 changes: 9 additions & 2 deletions src/shaders/terrain_raster.fragment.glsl
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,20 @@ uniform sampler2D u_image0;
varying vec2 v_pos0;

#ifdef FOG
varying vec3 v_fog_pos;
varying float v_fog_opacity;
#ifdef FOG_HAZE
varying vec4 v_haze_color;
#endif
#endif

void main() {
vec4 color = texture2D(u_image0, v_pos0);
#ifdef FOG
color.rgb = fog_dither(fog_apply(color.rgb, v_fog_pos));
color.rgb = fog_dither(fog_apply_from_vert(color.rgb, v_fog_opacity
#ifdef FOG_HAZE
, v_haze_color
#endif
));
#endif
gl_FragColor = color;
#ifdef TERRAIN_WIREFRAME
Expand Down
11 changes: 9 additions & 2 deletions src/shaders/terrain_raster.vertex.glsl
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,10 @@ attribute vec2 a_texture_pos;
varying vec2 v_pos0;

#ifdef FOG
varying vec3 v_fog_pos;
varying float v_fog_opacity;
#ifdef FOG_HAZE
varying vec4 v_haze_color;
#endif
#endif

const float skirtOffset = 24575.0;
Expand All @@ -24,6 +27,10 @@ void main() {
gl_Position = u_matrix * vec4(decodedPos, elevation, 1.0);

#ifdef FOG
v_fog_pos = fog_position(vec3(decodedPos, elevation));
fog_haze(fog_position(vec3(decodedPos, elevation)), v_fog_opacity
#ifdef FOG_HAZE
, v_haze_color
#endif
);
#endif
}

0 comments on commit 8783f40

Please sign in to comment.