clouds: add new spherical cloud pass using terrain distance occlusion
This commit is contained in:
1 parent
b9abacbdda
commit
52ececa917
2 files changed
+112
-3
No files matched your search
@@ -143,6 +143,7 @@ window.CONFIG = {
|
|||||||
// World units (1 unit ~ 12.9 km). Keep 'top' below the lowest flight
|
// World units (1 unit ~ 12.9 km). Keep 'top' below the lowest flight
|
||||||
// altitude so aircraft fly above the deck.
|
// altitude so aircraft fly above the deck.
|
||||||
clouds: {
|
clouds: {
|
||||||
|
enabled: true,
|
||||||
top: 1.0, // ~12.9 km
|
top: 1.0, // ~12.9 km
|
||||||
bottom: -0.2, // ~-2.6 km (deck base slightly below terrain)
|
bottom: -0.2, // ~-2.6 km (deck base slightly below terrain)
|
||||||
// Coverage per weather level, 0 = clear, 1 = overcast. Only the
|
// Coverage per weather level, 0 = clear, 1 = overcast. Only the
|
||||||
|
|||||||
Vendored
+111
-3
@@ -117,6 +117,7 @@ var FRAG_SRC = [
|
|||||||
'uniform float uAirHazeMaxAlpha;',
|
'uniform float uAirHazeMaxAlpha;',
|
||||||
'uniform vec3 uAirHazeColor;',
|
'uniform vec3 uAirHazeColor;',
|
||||||
'uniform sampler2D uTerrainDistTex;',
|
'uniform sampler2D uTerrainDistTex;',
|
||||||
|
'uniform float uCloudsOn;',
|
||||||
'uniform float uFrontHazeOn;',
|
'uniform float uFrontHazeOn;',
|
||||||
'uniform float uFrontHazeDensity;',
|
'uniform float uFrontHazeDensity;',
|
||||||
'uniform float uFrontHazeScaleHeightUnits;',
|
'uniform float uFrontHazeScaleHeightUnits;',
|
||||||
@@ -362,6 +363,28 @@ var FRAG_SRC = [
|
|||||||
'}',
|
'}',
|
||||||
'return column;',
|
'return column;',
|
||||||
'}',
|
'}',
|
||||||
|
'vec2 cloudShellRoots(float alt, vec3 oc, vec3 rd) {',
|
||||||
|
'float R = max(1.0, uEarthRadius);',
|
||||||
|
'float r = R + max(0.0, alt);',
|
||||||
|
'float b = 2.0 * dot(oc, rd);',
|
||||||
|
'float c = dot(oc, oc) - r * r;',
|
||||||
|
'float disc = b * b - 4.0 * c;',
|
||||||
|
'if (disc < 0.0) return vec2(-1.0, -1.0);',
|
||||||
|
'float sq = sqrt(disc);',
|
||||||
|
'return vec2((-b - sq) * 0.5, (-b + sq) * 0.5);',
|
||||||
|
'}',
|
||||||
|
'float cloudDensitySpherical(vec3 p, float alt, float baseThresh, float soft) {',
|
||||||
|
'vec3 q = vec3(p.x, alt, p.z);',
|
||||||
|
'float n = fbm3(q * 0.22 + vec3(0.0, 0.0, uTime * 0.05));',
|
||||||
|
'if (soft < 0.75) {',
|
||||||
|
'float d = fbm3(q * 1.80 + vec3(uTime * 0.045, 7.3, uTime * 0.035));',
|
||||||
|
'n = n + d * mix(0.22, 0.0, soft / 0.75);',
|
||||||
|
'}',
|
||||||
|
'float h = clamp((alt - uCloudBot) / max(0.01, uCloudTop - uCloudBot), 0.0, 1.0);',
|
||||||
|
'float profile = 4.0 * h * (1.0 - h);',
|
||||||
|
'profile = max(profile, 0.0);',
|
||||||
|
'return smoothstep(baseThresh, baseThresh + 0.34 + soft * 0.28, n) * profile;',
|
||||||
|
'}',
|
||||||
'float hazeColumnToDistance(vec3 rd, float dist) {',
|
'float hazeColumnToDistance(vec3 rd, float dist) {',
|
||||||
'if (dist <= 0.0) return 0.0;',
|
'if (dist <= 0.0) return 0.0;',
|
||||||
'if (uCurvatureOn < 0.5) return dist;',
|
'if (uCurvatureOn < 0.5) return dist;',
|
||||||
@@ -603,7 +626,72 @@ var FRAG_SRC = [
|
|||||||
'return;',
|
'return;',
|
||||||
'}',
|
'}',
|
||||||
|
|
||||||
'if (uMode == 1) { gl_FragColor = vec4(0.0); return; }',
|
'if (uMode == 1) {',
|
||||||
|
'if (uCloudsOn < 0.5) { gl_FragColor = vec4(0.0); return; }',
|
||||||
|
'float R = max(1.0, uEarthRadius);',
|
||||||
|
'vec3 C = vec3(uCamPos.x, -R, uCamPos.z);',
|
||||||
|
'vec3 oc = uCamPos - C;',
|
||||||
|
'float camAlt = max(0.0, length(oc) - R);',
|
||||||
|
'vec2 top = cloudShellRoots(uCloudTop, oc, rd);',
|
||||||
|
'vec2 bot = cloudShellRoots(uCloudBot, oc, rd);',
|
||||||
|
'float tEnter = 1e9;',
|
||||||
|
'float tExit = -1e9;',
|
||||||
|
'if (camAlt >= uCloudBot && camAlt <= uCloudTop) {',
|
||||||
|
'tEnter = 0.0;',
|
||||||
|
'tExit = max(maxPositiveRoot(top), maxPositiveRoot(bot));',
|
||||||
|
'} else if (camAlt > uCloudTop) {',
|
||||||
|
'tEnter = minPositiveRoot(top);',
|
||||||
|
'tExit = maxPositiveRoot(bot);',
|
||||||
|
'if (tExit < 0.0) tExit = maxPositiveRoot(top);',
|
||||||
|
'} else {',
|
||||||
|
'tEnter = minPositiveRoot(bot);',
|
||||||
|
'tExit = maxPositiveRoot(top);',
|
||||||
|
'if (tExit < 0.0) tExit = maxPositiveRoot(bot);',
|
||||||
|
'}',
|
||||||
|
'if (tEnter > 1e8 || tExit <= tEnter) { gl_FragColor = vec4(0.0); return; }',
|
||||||
|
'vec2 uv = gl_FragCoord.xy / uResolution;',
|
||||||
|
'vec4 distTex = texture2D(uTerrainDistTex, uv);',
|
||||||
|
'if (distTex.a > 0.5) {',
|
||||||
|
'float distNorm = (distTex.r * 65536.0 + distTex.g * 256.0 + distTex.b) / 16777215.0;',
|
||||||
|
'tExit = min(tExit, distNorm * max(1.0, uFarPlane));',
|
||||||
|
'}',
|
||||||
|
'tExit = min(tExit, tEnter + 30.0);',
|
||||||
|
'if (tExit <= tEnter) { gl_FragColor = vec4(0.0); return; }',
|
||||||
|
'float stepSize = (tExit - tEnter) / 24.0;',
|
||||||
|
'float t = tEnter + stepSize * 0.5;',
|
||||||
|
'float baseThresh = mix(0.72, 0.40, uDensity);',
|
||||||
|
'float sunUp = smoothstep(-0.20, 0.05, uSunAlt);',
|
||||||
|
'float cosT = dot(rd, uSunDir);',
|
||||||
|
'float phase = 0.30 + 0.70 * pow(max(0.0, cosT), 2.5);',
|
||||||
|
'float trans = 1.0;',
|
||||||
|
'vec3 cloudSum = vec3(0.0);',
|
||||||
|
'for (int i = 0; i < 24; i++) {',
|
||||||
|
'vec3 p = uCamPos + rd * t;',
|
||||||
|
'p += rd * (ign(gl_FragCoord.xy + vec2(float(i) * 7.13, float(i) * 3.71)) - 0.5) * stepSize * 0.25;',
|
||||||
|
'float alt = max(0.0, length(p - C) - R);',
|
||||||
|
'if (alt > uCloudTop + 0.1 || alt < uCloudBot - 0.1) break;',
|
||||||
|
'float soft = smoothstep(4.0, 20.0, t);',
|
||||||
|
'float d = cloudDensitySpherical(p, alt, baseThresh, soft);',
|
||||||
|
'if (d > 0.02) {',
|
||||||
|
'float lit = clamp((alt - uCloudBot) / max(0.01, uCloudTop - uCloudBot), 0.0, 1.0);',
|
||||||
|
'vec3 albC = uCloudAlbedo * mix(0.78, 1.0, lit);',
|
||||||
|
'float selfLit = clamp(1.0 - d * 0.8, 0.18, 1.0);',
|
||||||
|
'vec3 direct = albC * uSunColor * uSunInt * phase * sunUp * selfLit;',
|
||||||
|
'vec3 ambC = albC * max(vec3(uSkyAmb), uStarAmb * vec3(0.7, 0.85, 1.0)) * 1.7;',
|
||||||
|
'vec3 moonC = albC * uMoonColor * (0.35 + 0.65 * max(0.0, dot(rd, uMoonDir))) * uMoonInt * 3.0;',
|
||||||
|
'vec3 cloudCol = direct + ambC + moonC;',
|
||||||
|
'float alpha = clamp(d * stepSize * 1.30, 0.0, 1.0);',
|
||||||
|
'cloudSum += cloudCol * alpha * trans;',
|
||||||
|
'trans *= (1.0 - alpha);',
|
||||||
|
'if (trans < 0.04) break;',
|
||||||
|
'}',
|
||||||
|
't += stepSize;',
|
||||||
|
'}',
|
||||||
|
'float cloudAlpha = 1.0 - trans;',
|
||||||
|
'vec3 cloudColor = (cloudAlpha > 0.001) ? cloudSum / cloudAlpha : vec3(0.0);',
|
||||||
|
'gl_FragColor = vec4(cloudColor, clamp(cloudAlpha, 0.0, 1.0));',
|
||||||
|
'return;',
|
||||||
|
'}',
|
||||||
'}'
|
'}'
|
||||||
].join('\n');
|
].join('\n');
|
||||||
|
|
||||||
@@ -1040,6 +1128,7 @@ function init(canvas) {
|
|||||||
var uAirHazeMaxAlpha = gl.getUniformLocation(prog, 'uAirHazeMaxAlpha');
|
var uAirHazeMaxAlpha = gl.getUniformLocation(prog, 'uAirHazeMaxAlpha');
|
||||||
var uAirHazeColor = gl.getUniformLocation(prog, 'uAirHazeColor');
|
var uAirHazeColor = gl.getUniformLocation(prog, 'uAirHazeColor');
|
||||||
var uTerrainDistTex = gl.getUniformLocation(prog, 'uTerrainDistTex');
|
var uTerrainDistTex = gl.getUniformLocation(prog, 'uTerrainDistTex');
|
||||||
|
var uCloudsOn = gl.getUniformLocation(prog, 'uCloudsOn');
|
||||||
var uFrontHazeOn = gl.getUniformLocation(prog, 'uFrontHazeOn');
|
var uFrontHazeOn = gl.getUniformLocation(prog, 'uFrontHazeOn');
|
||||||
var uFrontHazeDensity = gl.getUniformLocation(prog, 'uFrontHazeDensity');
|
var uFrontHazeDensity = gl.getUniformLocation(prog, 'uFrontHazeDensity');
|
||||||
var uFrontHazeScaleHeightUnits = gl.getUniformLocation(prog, 'uFrontHazeScaleHeightUnits');
|
var uFrontHazeScaleHeightUnits = gl.getUniformLocation(prog, 'uFrontHazeScaleHeightUnits');
|
||||||
@@ -1160,6 +1249,7 @@ function init(canvas) {
|
|||||||
uAirHazeGain: uAirHazeGain, uAirHazeMaxAlpha: uAirHazeMaxAlpha,
|
uAirHazeGain: uAirHazeGain, uAirHazeMaxAlpha: uAirHazeMaxAlpha,
|
||||||
uAirHazeColor: uAirHazeColor,
|
uAirHazeColor: uAirHazeColor,
|
||||||
uTerrainDistTex: uTerrainDistTex,
|
uTerrainDistTex: uTerrainDistTex,
|
||||||
|
uCloudsOn: uCloudsOn,
|
||||||
uFrontHazeOn: uFrontHazeOn, uFrontHazeDensity: uFrontHazeDensity,
|
uFrontHazeOn: uFrontHazeOn, uFrontHazeDensity: uFrontHazeDensity,
|
||||||
uFrontHazeScaleHeightUnits: uFrontHazeScaleHeightUnits,
|
uFrontHazeScaleHeightUnits: uFrontHazeScaleHeightUnits,
|
||||||
uFrontHazeGain: uFrontHazeGain, uFrontHazeMaxAlpha: uFrontHazeMaxAlpha,
|
uFrontHazeGain: uFrontHazeGain, uFrontHazeMaxAlpha: uFrontHazeMaxAlpha,
|
||||||
@@ -1315,6 +1405,7 @@ if (window.CONFIG && window.CONFIG.clouds) {
|
|||||||
}
|
}
|
||||||
CLOUD_TOP = Math.max(0.2, Math.min(CLOUD_TOP, 2.0));
|
CLOUD_TOP = Math.max(0.2, Math.min(CLOUD_TOP, 2.0));
|
||||||
CLOUD_BOT = Math.min(CLOUD_BOT, CLOUD_TOP - 0.4);
|
CLOUD_BOT = Math.min(CLOUD_BOT, CLOUD_TOP - 0.4);
|
||||||
|
var CLOUDS_ON = !(window.CONFIG && window.CONFIG.clouds && window.CONFIG.clouds.enabled === false);
|
||||||
|
|
||||||
// Terrain elevation grid (CONFIG.terrain). Heights come from Mapzen/
|
// Terrain elevation grid (CONFIG.terrain). Heights come from Mapzen/
|
||||||
// TILEZEN "terrarium" tiles (Copernicus DEM) at ELEV_Z, fetched once
|
// TILEZEN "terrarium" tiles (Copernicus DEM) at ELEV_Z, fetched once
|
||||||
@@ -2269,7 +2360,7 @@ var terrainDistW = 0;
|
|||||||
var terrainDistH = 0;
|
var terrainDistH = 0;
|
||||||
var terrainDistOk = false;
|
var terrainDistOk = false;
|
||||||
function ensureTerrainDistFbo(gl, w, h) {
|
function ensureTerrainDistFbo(gl, w, h) {
|
||||||
if (!FRONT_HAZE_ON || w <= 0 || h <= 0) { terrainDistOk = false; return false; }
|
if ((!FRONT_HAZE_ON && !CLOUDS_ON) || w <= 0 || h <= 0) { terrainDistOk = false; return false; }
|
||||||
if (terrainDistFbo && terrainDistW === w && terrainDistH === h) return terrainDistOk;
|
if (terrainDistFbo && terrainDistW === w && terrainDistH === h) return terrainDistOk;
|
||||||
if (terrainDistFbo) {
|
if (terrainDistFbo) {
|
||||||
gl.deleteFramebuffer(terrainDistFbo);
|
gl.deleteFramebuffer(terrainDistFbo);
|
||||||
@@ -5840,6 +5931,7 @@ window.clouds3dRender = function(target) {
|
|||||||
gl.uniform1f(s.uAirHazeMaxAlpha, AIR_HAZE_MAX_ALPHA);
|
gl.uniform1f(s.uAirHazeMaxAlpha, AIR_HAZE_MAX_ALPHA);
|
||||||
gl.uniform3f(s.uAirHazeColor, _airHazeColor[0], _airHazeColor[1], _airHazeColor[2]);
|
gl.uniform3f(s.uAirHazeColor, _airHazeColor[0], _airHazeColor[1], _airHazeColor[2]);
|
||||||
gl.uniform1i(s.uTerrainDistTex, 7);
|
gl.uniform1i(s.uTerrainDistTex, 7);
|
||||||
|
gl.uniform1f(s.uCloudsOn, CLOUDS_ON ? 1.0 : 0.0);
|
||||||
gl.uniform1f(s.uFrontHazeOn, FRONT_HAZE_ON ? 1.0 : 0.0);
|
gl.uniform1f(s.uFrontHazeOn, FRONT_HAZE_ON ? 1.0 : 0.0);
|
||||||
gl.uniform1f(s.uFrontHazeDensity, FRONT_HAZE_DENSITY);
|
gl.uniform1f(s.uFrontHazeDensity, FRONT_HAZE_DENSITY);
|
||||||
gl.uniform1f(s.uFrontHazeScaleHeightUnits, FRONT_HAZE_SCALE_HEIGHT_UNITS);
|
gl.uniform1f(s.uFrontHazeScaleHeightUnits, FRONT_HAZE_SCALE_HEIGHT_UNITS);
|
||||||
@@ -6162,7 +6254,7 @@ window.clouds3dRender = function(target) {
|
|||||||
gl.enableVertexAttribArray(terr.aColor);
|
gl.enableVertexAttribArray(terr.aColor);
|
||||||
gl.vertexAttribPointer(terr.aColor, 3, gl.FLOAT, false, STRIDE, 32);
|
gl.vertexAttribPointer(terr.aColor, 3, gl.FLOAT, false, STRIDE, 32);
|
||||||
}
|
}
|
||||||
var distPassOk = FRONT_HAZE_ON && ensureTerrainDistFbo(gl, canvas.width, canvas.height);
|
var distPassOk = (FRONT_HAZE_ON || CLOUDS_ON) && ensureTerrainDistFbo(gl, canvas.width, canvas.height);
|
||||||
if (distPassOk) {
|
if (distPassOk) {
|
||||||
gl.bindFramebuffer(gl.FRAMEBUFFER, terrainDistFbo);
|
gl.bindFramebuffer(gl.FRAMEBUFFER, terrainDistFbo);
|
||||||
gl.viewport(0, 0, terrainDistW, terrainDistH);
|
gl.viewport(0, 0, terrainDistW, terrainDistH);
|
||||||
@@ -6715,6 +6807,22 @@ window.clouds3dRender = function(target) {
|
|||||||
gl.drawArrays(gl.TRIANGLES, 0, 3);
|
gl.drawArrays(gl.TRIANGLES, 0, 3);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (CLOUDS_ON && terrainDistOk && terrainDistTex) {
|
||||||
|
gl.useProgram(s.prog);
|
||||||
|
gl.bindBuffer(gl.ARRAY_BUFFER, s.buf);
|
||||||
|
gl.enableVertexAttribArray(s.aPos);
|
||||||
|
gl.vertexAttribPointer(s.aPos, 2, gl.FLOAT, false, 0, 0);
|
||||||
|
gl.activeTexture(gl.TEXTURE7);
|
||||||
|
gl.bindTexture(gl.TEXTURE_2D, terrainDistTex);
|
||||||
|
gl.uniform1i(s.uTerrainDistTex, 7);
|
||||||
|
gl.disable(gl.DEPTH_TEST);
|
||||||
|
gl.depthMask(false);
|
||||||
|
gl.enable(gl.BLEND);
|
||||||
|
gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
|
||||||
|
gl.uniform1i(s.uMode, 1);
|
||||||
|
gl.drawArrays(gl.TRIANGLES, 0, 3);
|
||||||
|
}
|
||||||
|
|
||||||
if (FRONT_HAZE_ON && terrainDistOk && terrainDistTex) {
|
if (FRONT_HAZE_ON && terrainDistOk && terrainDistTex) {
|
||||||
gl.useProgram(s.prog);
|
gl.useProgram(s.prog);
|
||||||
gl.bindBuffer(gl.ARRAY_BUFFER, s.buf);
|
gl.bindBuffer(gl.ARRAY_BUFFER, s.buf);
|
||||||
|
|||||||
Reference in new issue
Block a user