webgl / index.html
victorqueiroz's picture
Add 3 files
18a13a8 verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>WebGL Text Rendering</title>
<script src="https://cdn.tailwindcss.com"></script>
<style>
#glCanvas {
display: block;
background-color: #1a202c;
border-radius: 0.5rem;
box-shadow: 0 10px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);
}
.control-panel {
background-color: #2d3748;
border-radius: 0.5rem;
}
.text-input {
background-color: #4a5568;
color: white;
border: none;
border-radius: 0.25rem;
padding: 0.5rem;
}
</style>
</head>
<body class="bg-gray-900 text-gray-200 min-h-screen p-8">
<div class="max-w-6xl mx-auto">
<header class="mb-8">
<h1 class="text-4xl font-bold text-blue-400 mb-2">WebGL Text Rendering</h1>
<p class="text-gray-400">Rendering text using pure WebGL</p>
</header>
<div class="grid grid-cols-1 lg:grid-cols-3 gap-8">
<div class="lg:col-span-2">
<canvas id="glCanvas" width="800" height="500"></canvas>
</div>
<div class="control-panel p-6">
<h2 class="text-xl font-semibold mb-4 text-blue-300">Controls</h2>
<div class="space-y-4">
<div>
<label class="block text-sm font-medium mb-1" for="textInput">Text Content</label>
<input type="text" id="textInput" value="WebGL" class="text-input w-full">
</div>
<div>
<label class="block text-sm font-medium mb-1" for="fontSize">Font Size</label>
<input type="range" id="fontSize" min="10" max="200" value="72" class="w-full">
<span id="fontSizeValue" class="text-sm">72px</span>
</div>
<div>
<label class="block text-sm font-medium mb-1" for="textColor">Text Color</label>
<input type="color" id="textColor" value="#4299e1" class="w-full h-10">
</div>
<button id="resetBtn" class="bg-blue-600 hover:bg-blue-700 text-white py-2 px-4 rounded transition-colors">
Reset Settings
</button>
</div>
</div>
</div>
</div>
<script>
document.addEventListener('DOMContentLoaded', function() {
const canvas = document.getElementById('glCanvas');
const gl = canvas.getContext('webgl') || canvas.getContext('experimental-webgl');
if (!gl) {
alert('WebGL not supported in your browser');
return;
}
// Set up controls
const textInput = document.getElementById('textInput');
const fontSize = document.getElementById('fontSize');
const fontSizeValue = document.getElementById('fontSizeValue');
const textColor = document.getElementById('textColor');
const resetBtn = document.getElementById('resetBtn');
fontSize.addEventListener('input', () => {
fontSizeValue.textContent = `${fontSize.value}px`;
updateText();
});
textInput.addEventListener('input', updateText);
textColor.addEventListener('input', updateUniforms);
resetBtn.addEventListener('click', () => {
textInput.value = 'WebGL';
fontSize.value = 72;
fontSizeValue.textContent = '72px';
textColor.value = '#4299e1';
updateText();
updateUniforms();
});
// Vertex shader
const vsSource = `
attribute vec2 a_position;
attribute vec2 a_texCoord;
varying vec2 v_texCoord;
void main() {
gl_Position = vec4(a_position, 0, 1);
v_texCoord = a_texCoord;
}
`;
// Fragment shader (simplified for basic text rendering)
const fsSource = `
precision mediump float;
uniform sampler2D u_texture;
uniform vec3 u_textColor;
varying vec2 v_texCoord;
void main() {
vec4 texColor = texture2D(u_texture, v_texCoord);
gl_FragColor = vec4(u_textColor, texColor.r);
}
`;
// Compile shader
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;
}
// Create shader program
const vertexShader = compileShader(gl, vsSource, gl.VERTEX_SHADER);
const fragmentShader = compileShader(gl, fsSource, gl.FRAGMENT_SHADER);
const program = gl.createProgram();
gl.attachShader(program, vertexShader);
gl.attachShader(program, fragmentShader);
gl.linkProgram(program);
if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
console.error('Program link error:', gl.getProgramInfoLog(program));
return;
}
gl.useProgram(program);
// Get attribute and uniform locations
const positionAttributeLocation = gl.getAttribLocation(program, 'a_position');
const texCoordAttributeLocation = gl.getAttribLocation(program, 'a_texCoord');
const textureUniformLocation = gl.getUniformLocation(program, 'u_texture');
const textColorUniformLocation = gl.getUniformLocation(program, 'u_textColor');
// Create a texture
const texture = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, texture);
// Set texture parameters
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);
// Create buffers for a fullscreen quad
const positionBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
// Positions for a fullscreen quad
const positions = [
-1.0, -1.0,
1.0, -1.0,
-1.0, 1.0,
1.0, 1.0,
];
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(positions), gl.STATIC_DRAW);
// Texture coordinates buffer
const texCoordBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, texCoordBuffer);
const texCoords = [
0.0, 0.0,
1.0, 0.0,
0.0, 1.0,
1.0, 1.0,
];
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(texCoords), gl.STATIC_DRAW);
// Create a temporary canvas for text rendering
const tempCanvas = document.createElement('canvas');
const tempCtx = tempCanvas.getContext('2d');
// Generate texture from text
function generateTextTexture(text, size) {
// Calculate canvas size based on text
tempCtx.font = `${size}px Arial`;
const metrics = tempCtx.measureText(text);
const width = Math.max(128, metrics.width + 20);
const height = size + 20;
tempCanvas.width = width;
tempCanvas.height = height;
// Clear and draw text
tempCtx.clearRect(0, 0, width, height);
tempCtx.font = `${size}px Arial`;
tempCtx.textAlign = 'center';
tempCtx.textBaseline = 'middle';
tempCtx.fillStyle = 'white';
tempCtx.fillText(text, width/2, height/2);
// Upload to WebGL texture
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, tempCanvas);
return {
width: width,
height: height
};
}
// Update uniforms
function updateUniforms() {
const color = hexToRgb(textColor.value);
gl.uniform3f(textColorUniformLocation, color.r, color.g, color.b);
render();
}
// Hex to RGB conversion
function hexToRgb(hex) {
const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
return result ? {
r: parseInt(result[1], 16) / 255,
g: parseInt(result[2], 16) / 255,
b: parseInt(result[3], 16) / 255
} : { r: 0, g: 0, b: 0 };
}
// Update text
function updateText() {
const text = textInput.value || ' ';
const size = parseInt(fontSize.value);
generateTextTexture(text, size);
updateUniforms();
}
// Render function
function render() {
gl.clearColor(0.1, 0.1, 0.1, 1.0);
gl.clear(gl.COLOR_BUFFER_BIT);
// Enable position attribute
gl.enableVertexAttribArray(positionAttributeLocation);
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
gl.vertexAttribPointer(positionAttributeLocation, 2, gl.FLOAT, false, 0, 0);
// Enable texture coordinate attribute
gl.enableVertexAttribArray(texCoordAttributeLocation);
gl.bindBuffer(gl.ARRAY_BUFFER, texCoordBuffer);
gl.vertexAttribPointer(texCoordAttributeLocation, 2, gl.FLOAT, false, 0, 0);
// Draw
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
}
// Initial render
updateText();
// Handle window resize
window.addEventListener('resize', function() {
const displayWidth = canvas.clientWidth;
const displayHeight = canvas.clientHeight;
if (canvas.width !== displayWidth || canvas.height !== displayHeight) {
canvas.width = displayWidth;
canvas.height = displayHeight;
gl.viewport(0, 0, canvas.width, canvas.height);
render();
}
});
});
</script>
<p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=victorqueiroz/webgl" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>
</html>