forked from townforge/townforge
148 lines
5.4 KiB
GLSL
148 lines
5.4 KiB
GLSL
// @class ProcSky
|
|
// @brief Procedural Sky component for Urho3D
|
|
// @author carnalis <carnalis.j@gmail.com>
|
|
// @license MIT License
|
|
// @copyright
|
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
// of this software and associated documentation files (the "Software"), to deal
|
|
// in the Software without restriction, including without limitation the rights
|
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
// copies of the Software, and to permit persons to whom the Software is
|
|
// furnished to do so, subject to the following conditions:
|
|
//
|
|
// The above copyright notice and this permission notice shall be included in
|
|
// all copies or substantial portions of the Software.
|
|
//
|
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
// THE SOFTWARE.
|
|
|
|
#include "Uniforms.glsl"
|
|
#include "ScreenPos.glsl"
|
|
#include "Transform.glsl"
|
|
#include "Samplers.glsl"
|
|
#line 27
|
|
|
|
varying vec2 vTexCoord;
|
|
|
|
void VS() {
|
|
mat4 modelMatrix = iModelMatrix;
|
|
vec3 worldPos = GetWorldPos(modelMatrix);
|
|
gl_Position = GetClipPos(worldPos);
|
|
gl_Position.z = gl_Position.w;
|
|
vTexCoord = GetQuadTexCoord(gl_Position);
|
|
// Flip GLSL texture coord vertically.
|
|
vTexCoord.y = 1.0 - vTexCoord.y;
|
|
}
|
|
|
|
|
|
#if defined(COMPILEPS)
|
|
|
|
uniform mat4 cInvProj;
|
|
uniform mat3 cInvViewRot;
|
|
uniform vec3 cLightDir;
|
|
uniform vec3 cKr;
|
|
uniform float cRayleighBrightness, cMieBrightness, cSpotBrightness, cScatterStrength, cRayleighStrength, cMieStrength, cRayleighCollectionPower, cMieCollectionPower, cMieDistribution, cTimeOfDay;
|
|
uniform float cSnowfall;
|
|
|
|
float surfaceHeight = 0.99; // < 1
|
|
float intensity = 1.8;
|
|
const int stepCount = 16;
|
|
float interp = (atan(8 * sin(cTimeOfDay)) + 1.5) / 3;
|
|
const vec4 snowfallColor = vec4(0.7, 0.7, 0.7, 1.0);
|
|
|
|
vec3 GetWorldNormal() {
|
|
vec2 fragCoord = vTexCoord;
|
|
fragCoord = (fragCoord - 0.5) * 2.0;
|
|
vec4 deviceNormal = vec4(fragCoord, 0.0, 1.0);
|
|
vec3 eyeNormal = normalize((cInvProj * deviceNormal).xyz);
|
|
vec3 worldNormal = normalize(cInvViewRot * eyeNormal);
|
|
return worldNormal;
|
|
}
|
|
|
|
float AtmosphericDepth(vec3 pos, vec3 dir) {
|
|
float a = dot(dir, dir);
|
|
float b = 2.0 * dot(dir, pos);
|
|
float c = dot(pos, pos) - 1.0;
|
|
float det = b * b - 4.0 * a * c;
|
|
float detSqrt = sqrt(det);
|
|
float q = (-b - detSqrt) / 2.0;
|
|
float t1 = c / q;
|
|
return t1;
|
|
}
|
|
|
|
float Phase(float alpha, float g) {
|
|
float a = 3.0 * (1.0 - g * g);
|
|
float b = 2.0 * (2.0 + g * g);
|
|
float c = 1.0 + alpha * alpha;
|
|
float d = pow(1.0 + g * g - 2.0 * g * alpha, 1.5);
|
|
return (a / b) * (c / d);
|
|
}
|
|
|
|
float HorizonExtinction(vec3 pos, vec3 dir, float radius) {
|
|
if (dir.y < -0.1) {
|
|
return 1.0f;
|
|
}
|
|
float u = dot(dir, -pos);
|
|
if(u < 0.0) {
|
|
return 1.0;
|
|
}
|
|
vec3 near = pos + u * dir;
|
|
if(length(near) < radius) {
|
|
return 0.0;
|
|
} else {
|
|
vec3 v2 = normalize(near) * radius - pos;
|
|
float diff = acos(dot(normalize(v2), dir));
|
|
return smoothstep(0.0, 1.0, pow(diff * 2.0, 3.0));
|
|
}
|
|
}
|
|
|
|
vec3 Absorb(float dist, vec3 color, float factor) {
|
|
return color - color * pow(cKr, vec3(factor / dist));
|
|
}
|
|
|
|
#endif // defined(COMPILEPS)
|
|
|
|
void PS() {
|
|
vec3 lightDir = cLightDir;
|
|
lightDir.z *= -1.0; // Invert world Z for Urho.
|
|
vec3 eyeDir = GetWorldNormal();
|
|
float alpha = clamp(dot(eyeDir, lightDir), 0, 1);
|
|
//float rayleighFactor = Phase(alpha, -0.01) * cRayleighBrightness;
|
|
//mix function makes the rayleigh brightness lessen as the sun reaches higher in the sky.
|
|
float rayleighFactor = Phase(alpha, -0.01) * mix(cRayleighBrightness, 6.0, interp);
|
|
float mieFactor = Phase(alpha, cMieDistribution) * cMieBrightness;
|
|
float spot = smoothstep(0.0, 15.0, Phase(alpha, 0.9995)) * cSpotBrightness;
|
|
vec3 eyePos = vec3(0.0, surfaceHeight, 0.0);
|
|
float eyeDepth = AtmosphericDepth(eyePos, eyeDir);
|
|
float stepLength = eyeDepth / float(stepCount);
|
|
float eyeExtinction = HorizonExtinction(eyePos, eyeDir, surfaceHeight - 0.15);
|
|
|
|
vec3 rayleighCollected = vec3(0.0, 0.0, 0.0);
|
|
vec3 mieCollected = vec3(0.0, 0.0, 0.0);
|
|
|
|
for(int i = 0; i < stepCount; ++i) {
|
|
float sampleDistance = stepLength * float(i);
|
|
vec3 pos = eyePos + eyeDir * sampleDistance;
|
|
float extinction = HorizonExtinction(pos, lightDir, surfaceHeight - 0.35);
|
|
float sampleDepth = AtmosphericDepth(pos, lightDir);
|
|
vec3 influx = Absorb(sampleDepth, vec3(intensity), cScatterStrength) * extinction;
|
|
rayleighCollected += Absorb(sampleDistance, cKr * influx, cRayleighStrength);
|
|
mieCollected += Absorb(sampleDistance, influx, cMieStrength);
|
|
}
|
|
|
|
rayleighCollected = (rayleighCollected * eyeExtinction * pow(eyeDepth, cRayleighCollectionPower)) / float(stepCount);
|
|
mieCollected = (mieCollected * eyeExtinction * pow(eyeDepth, cMieCollectionPower)) / float(stepCount);
|
|
|
|
vec3 color = vec3(spot*mieCollected + mieFactor*mieCollected + rayleighFactor*rayleighCollected);
|
|
vec4 daySky = vec4(color, 1.0) + mix(vec4(0.0, 0.0, 0.0, 1.0), vec4(0.1, 0.2, 0.4, 1.0), interp);
|
|
vec4 nightSky = textureCube(sDiffCubeMap, eyeDir);
|
|
|
|
vec4 finalColor = mix(nightSky, daySky, interp);
|
|
gl_FragColor = mix(finalColor, snowfallColor * interp, cSnowfall);
|
|
}
|