Skip to content

Commit

Permalink
Simplify haze evaluation (#10606)
Browse files Browse the repository at this point in the history
* Simplify fog evaluation

* Switch to approximate gamma conversion

* Factor out haze application

* Document the haze_apply function

* Remove backticks

* Update src/shaders/_prelude_fog.fragment.glsl
  • Loading branch information
rreusser authored and karimnaaji committed May 4, 2021
1 parent 8a6ed00 commit 4558549
Show file tree
Hide file tree
Showing 7 changed files with 34 additions and 54 deletions.
6 changes: 3 additions & 3 deletions src/render/fog.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import Context from '../gl/context.js';
import type {UniformLocations} from './uniform_binding.js';

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

export type FogUniformsType = {|
'u_fog_matrix': UniformMatrix4f,
Expand All @@ -12,7 +12,7 @@ export type FogUniformsType = {|
'u_fog_exponent': Uniform1f,
'u_fog_horizon_blend': Uniform1f,
'u_fog_temporal_offset': Uniform1f,
'u_haze_color_linear': Uniform4f,
'u_haze_color_linear': Uniform3f,

// Precision may differ, so we must pass uniforms separately for use in a vertex shader
'u_fog_opacity': Uniform1f,
Expand All @@ -26,7 +26,7 @@ export const fogUniforms = (context: Context, locations: UniformLocations): FogU
'u_fog_exponent': new Uniform1f(context, locations.u_fog_exponent),
'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_haze_color_linear': new Uniform4f(context, locations.u_haze_color_linear),
'u_haze_color_linear': new Uniform3f(context, locations.u_haze_color_linear),

'u_fog_opacity': new Uniform1f(context, locations.u_fog_opacity),
});
16 changes: 7 additions & 9 deletions src/render/painter.js
Original file line number Diff line number Diff line change
Expand Up @@ -865,16 +865,14 @@ class Painter {
if (fog.properties.get('haze-color').a > 0) {
const hazeColor = fog.properties.get('haze-color');
const hazeBaseAmpl = 5;
const hazeColorLinear = [
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)
// Since there's no significant difference in visual effect, we use approximate
// sRGB -> linear RGB conversion with a power of 2 instead of 2.2 in order to
// avoid pow() functions in the shader.
uniforms['u_haze_color_linear'] = [
hazeBaseAmpl * Math.pow(hazeColor.r, 2),
hazeBaseAmpl * Math.pow(hazeColor.g, 2),
hazeBaseAmpl * Math.pow(hazeColor.b, 2),
];

uniforms['u_haze_color_linear'] = hazeColorLinear;
}

program.setFogUniformValues(context, uniforms);
Expand Down
9 changes: 0 additions & 9 deletions src/shaders/_prelude.fragment.glsl
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,3 @@ vec3 dither(vec3 color, highp vec2 seed) {
vec3 rnd = hash(seed) + hash(seed + 0.59374) - 0.5;
return color + rnd / 255.0;
}

vec3 linear_to_srgb(vec3 color) {
return pow(color, vec3(1.0 / 2.2));
}

vec3 srgb_to_linear(vec3 color) {
return pow(color, vec3(2.2));
}

38 changes: 17 additions & 21 deletions src/shaders/_prelude_fog.fragment.glsl
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,9 @@ uniform float u_fog_temporal_offset;
uniform mediump float u_fog_horizon_blend;
uniform mediump vec2 u_fog_range;
uniform mediump float u_fog_opacity;
uniform mediump vec4 u_haze_color_linear;
uniform mediump vec3 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
// See: https://www.desmos.com/calculator/h8odggcnd0
const float k = 8.0;
return max(vec3(0), log2(exp2(-k * color) + exp2(-k)) * (-1.0 / k));
}

// Assumes z up and camera_dir *normalized* (to avoid computing its length multiple
// times for different functions).
// Must match definitions in:
Expand Down Expand Up @@ -49,6 +42,19 @@ float fog_opacity(vec3 pos) {
return fog_opacity((length(pos) - u_fog_range.x) / (u_fog_range.y - u_fog_range.x));
}

// This function applies haze to an input color using an approximation of the following algorithm:
// 1. convert color from sRGB to linear RGB
// 2. add haze (presuming haze is in linear RGB)
// 3. tone-map the output
// 4. convert the result back to sRGB
// The equation below is based on a curve fit of the above algorithm, with the additional approximation
// during linear-srgb conversion that gamma=2, in order to avoid transcendental function evaluations
// which don't affect the visual quality.
vec3 haze_apply(vec3 color, vec3 haze) {
vec3 color2 = color * color;
return sqrt((color2 + haze) / (1.0 + color2 * color * haze));
}

vec3 fog_apply(vec3 color, vec3 pos) {
// Map [near, far] to [0, 1]
float depth = length(pos);
Expand All @@ -59,26 +65,16 @@ vec3 fog_apply(vec3 color, vec3 pos) {
fog_opac *= fog_horizon_blending(pos / depth);

#ifdef FOG_HAZE
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 * 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);
color = haze_apply(color, haze_opac * u_haze_color_linear);
#endif

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, vec4 haze) {
vec3 fog_apply_from_vert(vec3 color, float fog_opac, vec3 haze) {
#ifdef FOG_HAZE
color = srgb_to_linear(color);
color = mix(color, tonemap(color + haze.rgb), haze.a);
color = linear_to_srgb(color);
color = haze_apply(color, haze);
#endif

return mix(color, u_fog_color, fog_opac);
Expand Down
11 changes: 3 additions & 8 deletions src/shaders/_prelude_fog.vertex.glsl
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ uniform mediump float u_fog_horizon_blend;
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;
uniform mediump vec3 u_haze_color_linear;

// This function much match fog_opacity defined in _prelude_fog.fragment.glsl
float fog_opacity(float t) {
Expand All @@ -32,7 +32,7 @@ vec3 fog_position(vec2 pos) {
return fog_position(vec3(pos, 0));
}

void fog_haze(vec3 pos, out float fog_opac, out vec4 haze) {
void fog_haze(vec3 pos, out float fog_opac, out vec3 haze) {
// Map [near, far] to [0, 1]
float depth = length(pos);
float t = (depth - u_fog_range.x) / (u_fog_range.y - u_fog_range.x);
Expand All @@ -42,12 +42,7 @@ void fog_haze(vec3 pos, out float fog_opac, out vec4 haze) {
fog_opac *= fog_horizon_blending(pos / depth);

#ifdef FOG_HAZE
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 * u_haze_color_linear.a * smoothstep(-0.5, 0.25, t);
haze.rgb = haze_opac * u_haze_color_linear;
#endif
}

Expand Down
4 changes: 2 additions & 2 deletions src/shaders/terrain_raster.fragment.glsl
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ varying vec2 v_pos0;
#ifdef FOG
varying float v_fog_opacity;
#ifdef FOG_HAZE
varying vec4 v_haze_color;
varying vec3 v_haze_color;
#endif
#endif

Expand All @@ -14,7 +14,7 @@ void main() {
#ifdef FOG_HAZE
color.rgb = fog_dither(fog_apply_from_vert(color.rgb, v_fog_opacity, v_haze_color));
#else
vec4 unused;
vec3 unused;
color.rgb = fog_dither(fog_apply_from_vert(color.rgb, v_fog_opacity, unused));
#endif
#endif
Expand Down
4 changes: 2 additions & 2 deletions src/shaders/terrain_raster.vertex.glsl
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ varying vec2 v_pos0;
#ifdef FOG
varying float v_fog_opacity;
#ifdef FOG_HAZE
varying vec4 v_haze_color;
varying vec3 v_haze_color;
#endif
#endif

Expand All @@ -30,7 +30,7 @@ void main() {
#ifdef FOG_HAZE
fog_haze(fog_position(vec3(decodedPos, elevation)), v_fog_opacity, v_haze_color);
#else
vec4 unused;
vec3 unused;
fog_haze(fog_position(vec3(decodedPos, elevation)), v_fog_opacity, unused);
#endif
#endif
Expand Down

0 comments on commit 4558549

Please sign in to comment.