update
This commit is contained in:
180
app/static/js/noise_letter.js
Normal file
180
app/static/js/noise_letter.js
Normal file
@@ -0,0 +1,180 @@
|
||||
|
||||
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();
|
||||
Reference in New Issue
Block a user