overte-HifiExperiments/libraries/render-utils/src/Haze.slh

200 lines
7.3 KiB
Text

<!
// Haze.slh
//
// Created by Nissim Hadar on 9/13/2017
// Copyright 2013 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
!>
<@if not HAZE_SLH@>
<@def HAZE_SLH@>
const int HAZE_MODE_IS_ACTIVE = 1 << 0;
const int HAZE_MODE_IS_ALTITUDE_BASED = 1 << 1;
const int HAZE_MODE_IS_KEYLIGHT_ATTENUATED = 1 << 2;
const int HAZE_MODE_IS_MODULATE_COLOR = 1 << 3;
const int HAZE_MODE_IS_ENABLE_LIGHT_BLEND = 1 << 4;
struct HazeParams {
vec3 hazeColor;
float hazeGlareBlend;
vec3 hazeGlareColor;
float hazeBaseReference;
vec3 colorModulationFactor;
int hazeMode;
mat4 transform;
float backgroundBlend;
float hazeRangeFactor;
float hazeHeightFactor;
float hazeKeyLightRangeFactor;
float hazeKeyLightAltitudeFactor;
};
layout(std140) uniform hazeBuffer {
HazeParams hazeParams;
};
// Input:
// color - fragment original color
// lightDirectionWS - parameters of the keylight
// fragPositionWS - fragment position in world coordinates
// Output:
// fragment colour after haze effect
//
// General algorithm taken from http://www.iquilezles.org/www/articles/fog/fog.htm, with permission
//
vec3 computeHazeColorKeyLightAttenuation(vec3 color, vec3 lightDirectionWS, vec3 fragPositionWS) {
// Directional light attenuation is simulated by assuming the light source is at a fixed height above the
// fragment. This height is where the haze density is reduced by 95% from the haze at the fragment's height
//
// The distance is computed from the height and the directional light orientation
// The distance is limited to height * 1,000, which gives an angle of ~0.057 degrees
// Height at which haze density is reduced by 95% (default set to 2000.0 for safety ,this should never happen)
float height_95p = 2000.0;
const float log_p_005 = log(0.05);
if (hazeParams.hazeKeyLightAltitudeFactor > 0.0f) {
height_95p = -log_p_005 / hazeParams.hazeKeyLightAltitudeFactor;
}
// Note that we need the sine to be positive
float sin_pitch = abs(lightDirectionWS.y);
float distance;
const float minimumSinPitch = 0.001;
if (sin_pitch < minimumSinPitch) {
distance = height_95p / minimumSinPitch;
} else {
distance = height_95p / sin_pitch;
}
// Integration is from the fragment towards the light source
// Note that the haze base reference affects only the haze density as function of altitude
float hazeDensityDistribution =
hazeParams.hazeKeyLightRangeFactor *
exp(-hazeParams.hazeKeyLightAltitudeFactor * (fragPositionWS.y - hazeParams.hazeBaseReference));
float hazeIntegral = hazeDensityDistribution * distance;
// Note that t is constant and equal to -log(0.05)
// float t = hazeParams.hazeAltitudeFactor * height_95p;
// hazeIntegral *= (1.0 - exp (-t)) / t;
hazeIntegral *= 0.3171178;
return color * exp(-hazeIntegral);
}
// Input:
// fragColor - fragment original color
// fragPositionES - fragment position in eye coordinates
// fragPositionWS - fragment position in world coordinates
// eyePositionWS - eye position in world coordinates
// Output:
// fragment colour after haze effect
//
// General algorithm taken from http://www.iquilezles.org/www/articles/fog/fog.htm, with permission
//
vec4 computeHazeColor(vec4 fragColor, vec3 fragPositionES, vec3 fragPositionWS, vec3 eyePositionWS, vec3 lightDirectionWS) {
// Distance to fragment
float distance = length(fragPositionES);
float eyeWorldHeight = eyePositionWS.y;
// Convert haze colour from uniform into a vec4
vec4 hazeColor = vec4(hazeParams.hazeColor, 1.0);
// Use the haze colour for the glare colour, if blend is not enabled
vec4 blendedHazeColor;
if ((hazeParams.hazeMode & HAZE_MODE_IS_ENABLE_LIGHT_BLEND) == HAZE_MODE_IS_ENABLE_LIGHT_BLEND) {
// Directional light component is a function of the angle from the eye, between the fragment and the sun
vec3 fragToEyeDirWS = normalize(fragPositionWS - eyePositionWS);
float glareComponent = max(0.0, dot(fragToEyeDirWS, -lightDirectionWS));
float power = min(1.0, pow(glareComponent, hazeParams.hazeGlareBlend));
vec4 glareColor = vec4(hazeParams.hazeGlareColor, 1.0);
blendedHazeColor = mix(hazeColor, glareColor, power);
} else {
blendedHazeColor = hazeColor;
}
vec4 potentialFragColor;
if ((hazeParams.hazeMode & HAZE_MODE_IS_MODULATE_COLOR) == HAZE_MODE_IS_MODULATE_COLOR) {
// Compute separately for each colour
// Haze is based on both range and altitude
// Taken from www.crytek.com/download/GDC2007_RealtimeAtmoFxInGamesRev.ppt
// Note that the haze base reference affects only the haze density as function of altitude
vec3 hazeDensityDistribution =
hazeParams.colorModulationFactor *
exp(-hazeParams.hazeHeightFactor * (eyeWorldHeight - hazeParams.hazeBaseReference));
vec3 hazeIntegral = hazeDensityDistribution * distance;
const float slopeThreshold = 0.01;
float deltaHeight = fragPositionWS.y - eyeWorldHeight;
if (abs(deltaHeight) > slopeThreshold) {
float t = hazeParams.hazeHeightFactor * deltaHeight;
hazeIntegral *= (1.0 - exp (-t)) / t;
}
vec3 hazeAmount = 1.0 - exp(-hazeIntegral);
// Compute color after haze effect
potentialFragColor = mix(fragColor, vec4(1.0, 1.0, 1.0, 1.0), vec4(hazeAmount, 1.0));
} else if ((hazeParams.hazeMode & HAZE_MODE_IS_ALTITUDE_BASED) != HAZE_MODE_IS_ALTITUDE_BASED) {
// Haze is based only on range
float hazeAmount = 1.0 - exp(-distance * hazeParams.hazeRangeFactor);
// Compute color after haze effect
potentialFragColor = mix(fragColor, blendedHazeColor, hazeAmount);
} else {
// Haze is based on both range and altitude
// Taken from www.crytek.com/download/GDC2007_RealtimeAtmoFxInGamesRev.ppt
// Note that the haze base reference affects only the haze density as function of altitude
float hazeDensityDistribution =
hazeParams.hazeRangeFactor *
exp(-hazeParams.hazeHeightFactor * (eyeWorldHeight - hazeParams.hazeBaseReference));
float hazeIntegral = hazeDensityDistribution * distance;
const float slopeThreshold = 0.01;
float deltaHeight = fragPositionWS.y - eyeWorldHeight;
if (abs(deltaHeight) > slopeThreshold) {
float t = hazeParams.hazeHeightFactor * deltaHeight;
// Protect from wild values
const float EPSILON = 0.0000001f;
if (abs(t) > EPSILON) {
hazeIntegral *= (1.0 - exp (-t)) / t;
}
}
float hazeAmount = 1.0 - exp(-hazeIntegral);
// Compute color after haze effect
potentialFragColor = mix(fragColor, blendedHazeColor, hazeAmount);
}
// Mix with background at far range
const float BLEND_DISTANCE = 27000.0f;
vec4 outFragColor;
if (distance > BLEND_DISTANCE) {
outFragColor = mix(potentialFragColor, fragColor, hazeParams.backgroundBlend);
} else {
outFragColor = potentialFragColor;
}
return outFragColor;
}
<@endif@>