This commit is contained in:
Daniel
2026-04-02 10:07:24 +08:00
parent 198d8534ad
commit a5bf2adad9
2 changed files with 82 additions and 14 deletions

48
app.js
View File

@@ -149,12 +149,25 @@ uniform float iTime;
uniform float iTimeDelta; uniform float iTimeDelta;
uniform int iFrame; uniform int iFrame;
uniform vec4 iMouse; uniform vec4 iMouse;
uniform vec4 iDate;
${userCode} ${userCode}
void main() { void main() {
mainImage(outColor, gl_FragCoord.xy); mainImage(outColor, gl_FragCoord.xy);
}`; }`;
} }
function setIDateUniform(gl, loc) {
if (loc == null) return;
const d = new Date();
gl.uniform4f(
loc,
d.getFullYear(),
d.getMonth() + 1,
d.getDate(),
d.getHours() * 3600 + d.getMinutes() * 60 + d.getSeconds() + d.getMilliseconds() * 0.001
);
}
function createQuad(gl) { function createQuad(gl) {
const quad = new Float32Array([-1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, 1]); const quad = new Float32Array([-1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, 1]);
const vao = gl.createVertexArray(); const vao = gl.createVertexArray();
@@ -169,20 +182,30 @@ function createQuad(gl) {
} }
function compileProgram(gl, fragmentShaderSource) { function compileProgram(gl, fragmentShaderSource) {
function compile(type, source) { function compile(type, label, source) {
if (gl.isContextLost?.()) {
throw new Error(`${label}: WebGL context lost`);
}
const shader = gl.createShader(type); const shader = gl.createShader(type);
gl.shaderSource(shader, source); gl.shaderSource(shader, source);
gl.compileShader(shader); gl.compileShader(shader);
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) { if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
const info = gl.getShaderInfoLog(shader); let info = (gl.getShaderInfoLog(shader) || "").trim();
if (!info) {
const err = gl.getError();
info =
err && err !== gl.NO_ERROR
? `WebGL getError 0x${err.toString(16)} (driver returned no compile log)`
: "No compile log (often: shader too complex, loop/unroll limits, or driver bug).";
}
gl.deleteShader(shader); gl.deleteShader(shader);
throw new Error(info || "unknown compile error"); throw new Error(`${label} shader:\n${info}`);
} }
return shader; return shader;
} }
const vs = compile(gl.VERTEX_SHADER, vertexShaderSource); const vs = compile(gl.VERTEX_SHADER, "Vertex", vertexShaderSource);
const fs = compile(gl.FRAGMENT_SHADER, fragmentShaderSource); const fs = compile(gl.FRAGMENT_SHADER, "Fragment", fragmentShaderSource);
const program = gl.createProgram(); const program = gl.createProgram();
gl.attachShader(program, vs); gl.attachShader(program, vs);
gl.attachShader(program, fs); gl.attachShader(program, fs);
@@ -190,9 +213,16 @@ function compileProgram(gl, fragmentShaderSource) {
gl.deleteShader(vs); gl.deleteShader(vs);
gl.deleteShader(fs); gl.deleteShader(fs);
if (!gl.getProgramParameter(program, gl.LINK_STATUS)) { if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
const info = gl.getProgramInfoLog(program); let info = (gl.getProgramInfoLog(program) || "").trim();
if (!info) {
const err = gl.getError();
info =
err && err !== gl.NO_ERROR
? `WebGL getError 0x${err.toString(16)} (no link log)`
: "No link log (vertex/fragment interface mismatch or resource limits).";
}
gl.deleteProgram(program); gl.deleteProgram(program);
throw new Error(info || "unknown link error"); throw new Error(`Program link:\n${info}`);
} }
return program; return program;
} }
@@ -260,6 +290,7 @@ function createPreviewCard(name, code) {
iTimeDelta: gl.getUniformLocation(program, "iTimeDelta"), iTimeDelta: gl.getUniformLocation(program, "iTimeDelta"),
iFrame: gl.getUniformLocation(program, "iFrame"), iFrame: gl.getUniformLocation(program, "iFrame"),
iMouse: gl.getUniformLocation(program, "iMouse"), iMouse: gl.getUniformLocation(program, "iMouse"),
iDate: gl.getUniformLocation(program, "iDate"),
}; };
const state = { const state = {
@@ -366,6 +397,7 @@ function openDetail(previewState) {
iTimeDelta: gl.getUniformLocation(program, "iTimeDelta"), iTimeDelta: gl.getUniformLocation(program, "iTimeDelta"),
iFrame: gl.getUniformLocation(program, "iFrame"), iFrame: gl.getUniformLocation(program, "iFrame"),
iMouse: gl.getUniformLocation(program, "iMouse"), iMouse: gl.getUniformLocation(program, "iMouse"),
iDate: gl.getUniformLocation(program, "iDate"),
}; };
detailRuntime = { detailRuntime = {
@@ -424,6 +456,7 @@ function renderAll(ts) {
gl.uniform1f(uniforms.iTime, preview.localTime); gl.uniform1f(uniforms.iTime, preview.localTime);
gl.uniform1f(uniforms.iTimeDelta, preview.isPlaying && !paused ? localDt : 0); gl.uniform1f(uniforms.iTimeDelta, preview.isPlaying && !paused ? localDt : 0);
gl.uniform1i(uniforms.iFrame, preview.localFrame); gl.uniform1i(uniforms.iFrame, preview.localFrame);
setIDateUniform(gl, uniforms.iDate);
gl.uniform4f( gl.uniform4f(
uniforms.iMouse, uniforms.iMouse,
mouse.x, mouse.x,
@@ -452,6 +485,7 @@ function renderAll(ts) {
gl.uniform1f(uniforms.iTime, elapsed); gl.uniform1f(uniforms.iTime, elapsed);
gl.uniform1f(uniforms.iTimeDelta, dt); gl.uniform1f(uniforms.iTimeDelta, dt);
gl.uniform1i(uniforms.iFrame, frame); gl.uniform1i(uniforms.iFrame, frame);
setIDateUniform(gl, uniforms.iDate);
gl.uniform4f( gl.uniform4f(
uniforms.iMouse, uniforms.iMouse,
mouse.x, mouse.x,

View File

@@ -106,6 +106,7 @@ uniform float iTime;
uniform float iTimeDelta; uniform float iTimeDelta;
uniform int iFrame; uniform int iFrame;
uniform vec4 iMouse; uniform vec4 iMouse;
uniform vec4 iDate;
uniform sampler2D iChannel0; uniform sampler2D iChannel0;
uniform sampler2D iChannel1; uniform sampler2D iChannel1;
uniform sampler2D iChannel2; uniform sampler2D iChannel2;
@@ -114,6 +115,18 @@ ${userCode}
void main() { mainImage(outColor, gl_FragCoord.xy); }`; void main() { mainImage(outColor, gl_FragCoord.xy); }`;
} }
function setIDateUniform(gl, loc) {
if (loc == null) return;
const d = new Date();
gl.uniform4f(
loc,
d.getFullYear(),
d.getMonth() + 1,
d.getDate(),
d.getHours() * 3600 + d.getMinutes() * 60 + d.getSeconds() + d.getMilliseconds() * 0.001
);
}
function createQuad(gl) { function createQuad(gl) {
const quad = new Float32Array([-1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, 1]); const quad = new Float32Array([-1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, 1]);
const vao = gl.createVertexArray(); const vao = gl.createVertexArray();
@@ -128,19 +141,29 @@ function createQuad(gl) {
} }
function compileProgram(gl, fragmentShaderSource) { function compileProgram(gl, fragmentShaderSource) {
function compile(type, source) { function compile(type, label, source) {
if (gl.isContextLost?.()) {
throw new Error(`${label}: WebGL context lost`);
}
const shader = gl.createShader(type); const shader = gl.createShader(type);
gl.shaderSource(shader, source); gl.shaderSource(shader, source);
gl.compileShader(shader); gl.compileShader(shader);
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) { if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
const info = gl.getShaderInfoLog(shader); let info = (gl.getShaderInfoLog(shader) || "").trim();
if (!info) {
const err = gl.getError();
info =
err && err !== gl.NO_ERROR
? `WebGL getError 0x${err.toString(16)} (driver returned no compile log)`
: "No compile log (often: shader too complex, loop/unroll limits, or driver bug). Try a simpler shader.";
}
gl.deleteShader(shader); gl.deleteShader(shader);
throw new Error(info || "unknown compile error"); throw new Error(`${label} shader:\n${info}`);
} }
return shader; return shader;
} }
const vs = compile(gl.VERTEX_SHADER, vertexShaderSource); const vs = compile(gl.VERTEX_SHADER, "Vertex", vertexShaderSource);
const fs = compile(gl.FRAGMENT_SHADER, fragmentShaderSource); const fs = compile(gl.FRAGMENT_SHADER, "Fragment", fragmentShaderSource);
const program = gl.createProgram(); const program = gl.createProgram();
gl.attachShader(program, vs); gl.attachShader(program, vs);
gl.attachShader(program, fs); gl.attachShader(program, fs);
@@ -148,9 +171,16 @@ function compileProgram(gl, fragmentShaderSource) {
gl.deleteShader(vs); gl.deleteShader(vs);
gl.deleteShader(fs); gl.deleteShader(fs);
if (!gl.getProgramParameter(program, gl.LINK_STATUS)) { if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
const info = gl.getProgramInfoLog(program); let info = (gl.getProgramInfoLog(program) || "").trim();
if (!info) {
const err = gl.getError();
info =
err && err !== gl.NO_ERROR
? `WebGL getError 0x${err.toString(16)} (no link log)`
: "No link log (vertex/fragment interface mismatch or resource limits).";
}
gl.deleteProgram(program); gl.deleteProgram(program);
throw new Error(info || "unknown link error"); throw new Error(`Program link:\n${info}`);
} }
return program; return program;
} }
@@ -349,6 +379,7 @@ function ensurePreviewWebGL(state) {
iTimeDelta: gl.getUniformLocation(program, "iTimeDelta"), iTimeDelta: gl.getUniformLocation(program, "iTimeDelta"),
iFrame: gl.getUniformLocation(program, "iFrame"), iFrame: gl.getUniformLocation(program, "iFrame"),
iMouse: gl.getUniformLocation(program, "iMouse"), iMouse: gl.getUniformLocation(program, "iMouse"),
iDate: gl.getUniformLocation(program, "iDate"),
}; };
state.gl = gl; state.gl = gl;
state.program = program; state.program = program;
@@ -611,6 +642,7 @@ function openDetail(previewState) {
iTimeDelta: gl.getUniformLocation(program, "iTimeDelta"), iTimeDelta: gl.getUniformLocation(program, "iTimeDelta"),
iFrame: gl.getUniformLocation(program, "iFrame"), iFrame: gl.getUniformLocation(program, "iFrame"),
iMouse: gl.getUniformLocation(program, "iMouse"), iMouse: gl.getUniformLocation(program, "iMouse"),
iDate: gl.getUniformLocation(program, "iDate"),
}, },
mouse: { x: 0, y: 0, downX: 0, downY: 0, down: false }, mouse: { x: 0, y: 0, downX: 0, downY: 0, down: false },
_layoutW: 0, _layoutW: 0,
@@ -741,6 +773,7 @@ function renderAll(ts) {
gl.uniform1f(uniforms.iTimeDelta, 0); gl.uniform1f(uniforms.iTimeDelta, 0);
gl.uniform1i(uniforms.iFrame, 0); gl.uniform1i(uniforms.iFrame, 0);
} }
setIDateUniform(gl, uniforms.iDate);
gl.uniform4f( gl.uniform4f(
uniforms.iMouse, uniforms.iMouse,
mouse.x, mouse.x,
@@ -776,6 +809,7 @@ function renderAll(ts) {
gl.uniform1f(uniforms.iTime, elapsed); gl.uniform1f(uniforms.iTime, elapsed);
gl.uniform1f(uniforms.iTimeDelta, dt); gl.uniform1f(uniforms.iTimeDelta, dt);
gl.uniform1i(uniforms.iFrame, frame); gl.uniform1i(uniforms.iFrame, frame);
setIDateUniform(gl, uniforms.iDate);
gl.uniform4f( gl.uniform4f(
uniforms.iMouse, uniforms.iMouse,
mouse.x, mouse.x,