Fix clouds3d: missing closing quote on long string literal at line 202
This commit is contained in:
1 parent
a2998bdf45
commit
0f54adefda
2 files changed
+1
-284
No files matched your search
Vendored
-283
@@ -1,283 +0,0 @@
|
||||
/* Texture-based 2D cloud renderer.
|
||||
*
|
||||
* Inspired by fizzer's tiny ShaderToy cloud shader: instead of
|
||||
* computing fBm in-shader with hash functions, sample a noise
|
||||
* texture (4 lookups = 4 octaves) and weight the results. This
|
||||
* trades a tiny amount of texture memory for a large drop in ALU
|
||||
* per pixel — significant on low-end integrated GPUs.
|
||||
*
|
||||
* Texture is loaded from window.__noiseDataUri (a data URI baked
|
||||
* from noise.png by the build). Falls back to /noise.png if the
|
||||
* URI is unavailable. Theme-agnostic: parent owns colour via
|
||||
* __currentPalette.
|
||||
*/
|
||||
|
||||
(function() {
|
||||
|
||||
function makeShader(gl, type, src) {
|
||||
var s = gl.createShader(type);
|
||||
gl.shaderSource(s, src);
|
||||
gl.compileShader(s);
|
||||
if (!gl.getShaderParameter(s, gl.COMPILE_STATUS)) {
|
||||
console.error('clouds2d: Shader compile error:', gl.getShaderInfoLog(s));
|
||||
gl.deleteShader(s);
|
||||
return null;
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
function makeProgram(gl, vsSrc, fsSrc) {
|
||||
var vs = makeShader(gl, gl.VERTEX_SHADER, vsSrc);
|
||||
var fs = makeShader(gl, gl.FRAGMENT_SHADER, fsSrc);
|
||||
if (!vs || !fs) return null;
|
||||
var p = gl.createProgram();
|
||||
gl.attachShader(p, vs);
|
||||
gl.attachShader(p, fs);
|
||||
gl.linkProgram(p);
|
||||
if (!gl.getProgramParameter(p, gl.LINK_STATUS)) {
|
||||
console.error('clouds2d: Program link error:', gl.getProgramInfoLog(p));
|
||||
gl.deleteProgram(p);
|
||||
return null;
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
var VERT_SRC = [
|
||||
'attribute vec2 aPos;',
|
||||
'void main() { gl_Position = vec4(aPos, 0.0, 1.0); }'
|
||||
].join('\n');
|
||||
|
||||
// 4-octave fBm by sampling a noise texture. The texture is REPEAT-
|
||||
// wrapped so we can scroll through it endlessly. Scrolling uses a
|
||||
// hash-based per-pixel jitter so the regular tiling pattern doesn't
|
||||
// cause horizon flicker (same fix as clouds3d).
|
||||
var FRAG_SRC = [
|
||||
'precision highp float;',
|
||||
'uniform vec2 uResolution;',
|
||||
'uniform float uTime;',
|
||||
'uniform sampler2D uNoise;',
|
||||
'uniform vec3 uSky;',
|
||||
'uniform vec3 uCloud;',
|
||||
'uniform vec3 uCloudShadow;',
|
||||
'uniform vec3 uLight;',
|
||||
'uniform vec3 uBg;',
|
||||
'uniform float uSpeed;',
|
||||
'uniform float uDensity;',
|
||||
'uniform float uDayProgress;',
|
||||
'uniform float uSunAlt;',
|
||||
'float hash21(vec2 p) {',
|
||||
'p = fract(p * vec2(123.34, 456.21));',
|
||||
'p += dot(p, p + 45.32);',
|
||||
'return fract(p.x * p.y);',
|
||||
'}',
|
||||
// 4-octave texture fBm. Each octave doubles frequency and halves
|
||||
// amplitude (1, 0.5, 0.25, 0.125). Total normalised by 1/0.9375.
|
||||
'float fbm(vec2 p) {',
|
||||
'float total = 0.0;',
|
||||
'total += 1.0 * texture2D(uNoise, p * 1.0).r;',
|
||||
'total += 0.5 * texture2D(uNoise, p * 2.0).r;',
|
||||
'total += 0.25 * texture2D(uNoise, p * 4.0).r;',
|
||||
'total += 0.125 * texture2D(uNoise, p * 8.0).r;',
|
||||
'return total / 1.875;',
|
||||
'}',
|
||||
'void main() {',
|
||||
'vec2 uv = gl_FragCoord.xy / uResolution.xy;',
|
||||
// Scroll the noise field horizontally with time, plus a small
|
||||
// vertical drift for slow cloud movement. Multiply by uv's
|
||||
// aspect so clouds don't squash on wide displays.
|
||||
'float aspect = uResolution.x / uResolution.y;',
|
||||
'vec2 p = vec2(',
|
||||
'(uv.x - 0.5) * aspect * 3.0 + uTime * 0.04 * uSpeed,',
|
||||
'uv.y * 2.5 + uTime * 0.012 * uSpeed',
|
||||
');',
|
||||
// Per-pixel jitter to break the regular tiling pattern that
|
||||
// would otherwise flicker at the cloud horizon.
|
||||
'p += (hash21(gl_FragCoord.xy + uTime) - 0.5) * 0.015;',
|
||||
'float n = fbm(p);',
|
||||
// Density threshold widens with weather (sunny = sparse,
|
||||
// stormy = overcast).
|
||||
'float thresh = mix(0.70, 0.35, uDensity);',
|
||||
'float cloud = smoothstep(thresh, thresh + 0.18, n);',
|
||||
// Sky gradient: bg below, sky above
|
||||
'vec3 sky = mix(uBg, uSky, smoothstep(0.0, 0.6, uv.y));',
|
||||
// Cloud body: shadow on the bottom, lit on the top
|
||||
'vec3 cloudCol = mix(uCloudShadow, uCloud, smoothstep(0.0, 0.7, uv.y));',
|
||||
// Light tint on the lit side (always slightly, so even at
|
||||
// night the cloud edge catches some illumination)
|
||||
'cloudCol = mix(cloudCol, uLight, cloud * uv.y * 0.45 * max(0.15, uDayProgress));',
|
||||
// Composite
|
||||
'vec3 col = mix(sky, cloudCol, cloud);',
|
||||
// Night darkening, driven by sun altitude (not uDayProgress
|
||||
// which is a 24h clock progress, not "is it night")
|
||||
'float nightFactor = smoothstep(0.05, -0.15, uSunAlt);',
|
||||
// Stronger than clouds3d because there's no cloud-mask
|
||||
// blending here — just multiply.
|
||||
'col *= 1.0 - nightFactor * 0.7;',
|
||||
'gl_FragColor = vec4(col, 1.0);',
|
||||
'}'
|
||||
].join('\n');
|
||||
|
||||
function hex(rgb) {
|
||||
return [((rgb >> 16) & 255) / 255,
|
||||
((rgb >> 8) & 255) / 255,
|
||||
(rgb & 255) / 255];
|
||||
}
|
||||
|
||||
var WEATHER_DENSITY = {
|
||||
sunny: 0.20,
|
||||
cloudy: 0.70,
|
||||
stormy: 1.00,
|
||||
night: 0.45
|
||||
};
|
||||
|
||||
// Load the noise image and upload it as a REPEAT-wrapped texture.
|
||||
// Priority: window.__noiseDataUri (if it has real data, not
|
||||
// PLACEHOLDER), else fetch /noise.png from the server.
|
||||
function loadNoiseTexture(gl) {
|
||||
return new Promise(function(resolve, reject) {
|
||||
var inline = window.__noiseDataUri;
|
||||
var hasInline = inline && inline.indexOf('PLACEHOLDER') < 0;
|
||||
var src = hasInline ? inline : 'noise.png';
|
||||
var img = new Image();
|
||||
img.onload = function() {
|
||||
var tex = gl.createTexture();
|
||||
gl.bindTexture(gl.TEXTURE_2D, tex);
|
||||
gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, false);
|
||||
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, img);
|
||||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT);
|
||||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT);
|
||||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
|
||||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
|
||||
resolve(tex);
|
||||
};
|
||||
img.onerror = function() {
|
||||
reject(new Error('clouds2d: failed to load noise image: ' + src));
|
||||
};
|
||||
img.src = src;
|
||||
});
|
||||
}
|
||||
|
||||
window.clouds2dRender = function(target) {
|
||||
if (!target || typeof target !== 'object') {
|
||||
console.error('clouds2d: invalid target');
|
||||
return;
|
||||
}
|
||||
var canvas = target.__clouds2dCanvas;
|
||||
if (!canvas) {
|
||||
canvas = document.createElement('canvas');
|
||||
canvas.style.position = 'absolute';
|
||||
canvas.style.top = '0';
|
||||
canvas.style.left = '0';
|
||||
canvas.style.width = '100%';
|
||||
canvas.style.height = '100%';
|
||||
while (target.firstChild) target.removeChild(target.firstChild);
|
||||
target.appendChild(canvas);
|
||||
target.style.position = 'relative';
|
||||
target.__clouds2dCanvas = canvas;
|
||||
}
|
||||
|
||||
var gl = canvas.__gl;
|
||||
if (!gl) {
|
||||
gl = canvas.getContext('webgl', { antialias: false, alpha: false });
|
||||
if (!gl) {
|
||||
console.error('clouds2d: WebGL not available');
|
||||
return;
|
||||
}
|
||||
canvas.__gl = gl;
|
||||
}
|
||||
|
||||
if (!canvas.__state) {
|
||||
// Compile shaders synchronously, load texture asynchronously.
|
||||
var buf = gl.createBuffer();
|
||||
gl.bindBuffer(gl.ARRAY_BUFFER, buf);
|
||||
gl.bufferData(gl.ARRAY_BUFFER,
|
||||
new Float32Array([ -1,-1, 3,-1, -1,3 ]),
|
||||
gl.STATIC_DRAW);
|
||||
var prog = makeProgram(gl, VERT_SRC, FRAG_SRC);
|
||||
if (!prog) return;
|
||||
canvas.__state = {
|
||||
gl: gl, prog: prog, buf: buf,
|
||||
aPos: gl.getAttribLocation(prog, 'aPos'),
|
||||
uRes: gl.getUniformLocation(prog, 'uResolution'),
|
||||
uTime: gl.getUniformLocation(prog, 'uTime'),
|
||||
uNoise: gl.getUniformLocation(prog, 'uNoise'),
|
||||
uSky: gl.getUniformLocation(prog, 'uSky'),
|
||||
uCloud: gl.getUniformLocation(prog, 'uCloud'),
|
||||
uCloudShadow: gl.getUniformLocation(prog, 'uCloudShadow'),
|
||||
uLight: gl.getUniformLocation(prog, 'uLight'),
|
||||
uBg: gl.getUniformLocation(prog, 'uBg'),
|
||||
uSpeed: gl.getUniformLocation(prog, 'uSpeed'),
|
||||
uDensity: gl.getUniformLocation(prog, 'uDensity'),
|
||||
uDayProgress: gl.getUniformLocation(prog, 'uDayProgress'),
|
||||
uSunAlt: gl.getUniformLocation(prog, 'uSunAlt')
|
||||
};
|
||||
canvas.__noiseReady = false;
|
||||
loadNoiseTexture(gl).then(function(tex) {
|
||||
canvas.__noise = tex;
|
||||
canvas.__noiseReady = true;
|
||||
}).catch(function(err) {
|
||||
console.error(err);
|
||||
});
|
||||
}
|
||||
|
||||
var s = canvas.__state;
|
||||
canvas.width = target.clientWidth || window.innerWidth;
|
||||
canvas.height = target.clientHeight || window.innerHeight;
|
||||
gl.viewport(0, 0, canvas.width, canvas.height);
|
||||
|
||||
// Texture not loaded yet — just clear, frame after will draw.
|
||||
if (!canvas.__noiseReady) {
|
||||
gl.clearColor(0, 0, 0, 1);
|
||||
gl.clear(gl.COLOR_BUFFER_BIT);
|
||||
return;
|
||||
}
|
||||
|
||||
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.TEXTURE0);
|
||||
gl.bindTexture(gl.TEXTURE_2D, canvas.__noise);
|
||||
gl.uniform1i(s.uNoise, 0);
|
||||
|
||||
gl.uniform2f(s.uRes, canvas.width, canvas.height);
|
||||
gl.uniform1f(s.uTime, performance.now() / 1000);
|
||||
|
||||
var pal = window.__currentPalette;
|
||||
if (!pal) {
|
||||
gl.clearColor(0, 0, 0, 1);
|
||||
gl.clear(gl.COLOR_BUFFER_BIT);
|
||||
return;
|
||||
}
|
||||
|
||||
var weather = window.__currentWeatherName;
|
||||
var dayProgress = window.__currentDayProgress;
|
||||
var themeSpeed = window.__currentThemeSpeed;
|
||||
var sunAlt = window.__currentSunAlt;
|
||||
|
||||
gl.uniform3fv(s.uSky, hex(pal.sky));
|
||||
gl.uniform3fv(s.uCloud, hex(pal.cloud));
|
||||
gl.uniform3fv(s.uCloudShadow, hex(
|
||||
pal.cloudShadow !== undefined ? pal.cloudShadow : pal.sky));
|
||||
gl.uniform3fv(s.uLight, hex(pal.light));
|
||||
gl.uniform3fv(s.uBg, hex(pal.bg));
|
||||
|
||||
var density = (weather && WEATHER_DENSITY[weather] !== undefined)
|
||||
? WEATHER_DENSITY[weather] : 0.5;
|
||||
gl.uniform1f(s.uDensity, density);
|
||||
|
||||
var day = (typeof dayProgress === 'number')
|
||||
? Math.max(0, Math.min(1, dayProgress)) : 0.5;
|
||||
gl.uniform1f(s.uDayProgress, day);
|
||||
|
||||
var speed = (typeof themeSpeed === 'number')
|
||||
? Math.max(0.1, Math.min(3, themeSpeed)) : 1.0;
|
||||
gl.uniform1f(s.uSpeed, speed);
|
||||
|
||||
gl.uniform1f(s.uSunAlt, (typeof sunAlt === 'number') ? sunAlt : 0);
|
||||
|
||||
gl.drawArrays(gl.TRIANGLES, 0, 3);
|
||||
};
|
||||
|
||||
})();
|
||||
Reference in new issue
Block a user