181 lines
5.3 KiB
JavaScript
181 lines
5.3 KiB
JavaScript
|
|
async function loadFile(url) {
|
|
const res = await fetch(url);
|
|
return res.text();
|
|
}
|
|
|
|
function compileShader(gl, source, type) {
|
|
const shader = gl.createShader(type);
|
|
gl.shaderSource(shader, source);
|
|
gl.compileShader(shader);
|
|
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
|
|
console.error('Shader compile error:', gl.getShaderInfoLog(shader));
|
|
gl.deleteShader(shader);
|
|
return null;
|
|
}
|
|
return shader;
|
|
}
|
|
|
|
function createTexture(gl) {
|
|
const tex = gl.createTexture();
|
|
gl.bindTexture(gl.TEXTURE_2D, tex);
|
|
// Set parameters so we can handle non-power-of-2 images if needed
|
|
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
|
|
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
|
|
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
|
|
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
|
|
return tex;
|
|
}
|
|
|
|
|
|
function updateCharTexture(gl, texture, text) {
|
|
const height = 256;
|
|
// Dynamic width based on text length, min 1
|
|
const chars = Math.max(1, text.length);
|
|
const width = 256 * chars;
|
|
|
|
const canvas = document.createElement('canvas');
|
|
canvas.width = width;
|
|
canvas.height = height;
|
|
const ctx = canvas.getContext('2d');
|
|
|
|
ctx.clearRect(0, 0, width, height);
|
|
ctx.fillStyle = 'white';
|
|
ctx.font = 'bold 200px monospace';
|
|
ctx.textAlign = 'center';
|
|
ctx.textBaseline = 'middle';
|
|
ctx.fillText(text, width / 2, height / 2);
|
|
|
|
gl.bindTexture(gl.TEXTURE_2D, texture);
|
|
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, canvas);
|
|
|
|
return chars; // Return aspect ratio (width/height)
|
|
}
|
|
|
|
async function main() {
|
|
const canvas = document.getElementById('glcanvas');
|
|
const gl = canvas.getContext('webgl');
|
|
|
|
if (!gl) {
|
|
alert('WebGL not supported');
|
|
return;
|
|
}
|
|
|
|
// Load Shaders
|
|
const vsSource = await loadFile('/static/shaders/vertex.glsl');
|
|
// Using simple Cache Busting for reloading shader during dev if needed, but not strictly necessary here
|
|
const fsSource = await loadFile('/static/shaders/fragment.glsl');
|
|
|
|
const vs = compileShader(gl, vsSource, gl.VERTEX_SHADER);
|
|
const fs = compileShader(gl, fsSource, gl.FRAGMENT_SHADER);
|
|
|
|
const program = gl.createProgram();
|
|
gl.attachShader(program, vs);
|
|
gl.attachShader(program, fs);
|
|
gl.linkProgram(program);
|
|
|
|
if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
|
|
console.error('Program link error:', gl.getProgramInfoLog(program));
|
|
return;
|
|
}
|
|
|
|
gl.useProgram(program);
|
|
|
|
// Quad Buffer
|
|
const verts = new Float32Array([
|
|
-1, -1,
|
|
1, -1,
|
|
-1, 1,
|
|
-1, 1,
|
|
1, -1,
|
|
1, 1,
|
|
]);
|
|
|
|
const buffer = gl.createBuffer();
|
|
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
|
|
gl.bufferData(gl.ARRAY_BUFFER, verts, gl.STATIC_DRAW);
|
|
|
|
const posLoc = gl.getAttribLocation(program, 'position');
|
|
gl.enableVertexAttribArray(posLoc);
|
|
gl.vertexAttribPointer(posLoc, 2, gl.FLOAT, false, 0, 0);
|
|
|
|
// Uniforms
|
|
const uTime = gl.getUniformLocation(program, 'u_time');
|
|
const uRes = gl.getUniformLocation(program, 'u_resolution');
|
|
const uCharPos = gl.getUniformLocation(program, 'u_charPos');
|
|
const uCharSize = gl.getUniformLocation(program, 'u_charSize');
|
|
// Texture uniform usually defaults to 0, which is fine
|
|
|
|
// State
|
|
const charTexture = createTexture(gl);
|
|
|
|
let charX = 0.5;
|
|
let charY = 0.5;
|
|
let charW = 0.5;
|
|
let charH = 0.5;
|
|
|
|
let currentAspect = 1.0;
|
|
|
|
// UI Handling
|
|
const input = document.getElementById('textInput');
|
|
const btn = document.getElementById('renderBtn');
|
|
|
|
function updateText() {
|
|
// Use placeholder or "A" if empty to avoid 0 size
|
|
let text = input.value.toUpperCase().substring(0, 12);
|
|
if (text.length === 0) return;
|
|
|
|
currentAspect = updateCharTexture(gl, charTexture, text);
|
|
resize(); // Recalculate size
|
|
}
|
|
|
|
btn.addEventListener('click', updateText);
|
|
|
|
// Initial Text
|
|
currentAspect = updateCharTexture(gl, charTexture, "HELLO");
|
|
|
|
function resize() {
|
|
// Look up container size
|
|
const container = canvas.parentElement;
|
|
canvas.width = container.clientWidth;
|
|
canvas.height = container.clientHeight;
|
|
gl.viewport(0, 0, canvas.width, canvas.height);
|
|
|
|
// Adjust aspect ratio
|
|
const screenAspect = canvas.width / canvas.height;
|
|
|
|
// Target: charW / charH = currentAspect / screenAspect
|
|
|
|
// Try filling 60% of height
|
|
charH = 0.6;
|
|
charW = charH * currentAspect / screenAspect;
|
|
|
|
// Constrain width to 95%
|
|
if (charW > 0.95) {
|
|
charW = 0.95;
|
|
charH = charW * screenAspect / currentAspect;
|
|
}
|
|
|
|
charX = 0.5 - charW / 2;
|
|
charY = 0.5 - charH / 2;
|
|
}
|
|
window.addEventListener('resize', resize);
|
|
resize();
|
|
|
|
function render(time) {
|
|
time *= 0.001; // Seconds
|
|
|
|
gl.uniform1f(uTime, time);
|
|
gl.uniform2f(uRes, canvas.width, canvas.height);
|
|
|
|
gl.uniform2f(uCharPos, charX, charY); // uniform needs bottom-left
|
|
gl.uniform2f(uCharSize, charW, charH);
|
|
|
|
gl.drawArrays(gl.TRIANGLES, 0, 6);
|
|
requestAnimationFrame(render);
|
|
}
|
|
requestAnimationFrame(render);
|
|
}
|
|
|
|
main();
|