summaryrefslogtreecommitdiff
path: root/fractiform.js
diff options
context:
space:
mode:
authorpommicket <pommicket@gmail.com>2023-06-14 15:58:11 +0100
committerpommicket <pommicket@gmail.com>2023-06-14 15:58:11 +0100
commit7432c0b941df5db8b7e38351188c27d06e60d006 (patch)
treefe997e2dc629366f3808e785b76bfd013608ff58 /fractiform.js
parent5d84ef35cef2959ef4901b85f3469735d51c9555 (diff)
a fractal ! hoo ray!
Diffstat (limited to 'fractiform.js')
-rw-r--r--fractiform.js163
1 files changed, 119 insertions, 44 deletions
diff --git a/fractiform.js b/fractiform.js
index c63751d..7e0edd7 100644
--- a/fractiform.js
+++ b/fractiform.js
@@ -7,29 +7,50 @@ let vertex_buffer_rect;
let canvas;
let framebuffer;
let framebuffer_color_texture;
-let prev_width = -1, prev_height = -1;
+let sampler_texture;
+let width = 1920, height = 1920;
window.addEventListener('load', startup);
+function handle_key_press(e) {
+ let code = e.keyCode;
+ switch (code) {
+ case 32:
+ perform_step();
+ }
+}
+
function startup() {
canvas = document.getElementById('canvas');
+ window.addEventListener('keydown', handle_key_press);
+
gl = canvas.getContext('webgl');
+ if (gl === null) {
+ show_error('your browser doesnt support webgl.\noh well.');
+ return;
+ }
program_main = compile_program('main', `
attribute vec2 v_pos;
uniform vec2 u_scale;
+uniform vec2 u_offset;
+varying vec2 uv;
void main() {
- gl_Position = vec4(v_pos * u_scale, 0.0, 1.0);
+ uv = v_pos * 0.5 + 0.5;
+ gl_Position = vec4(v_pos * u_scale + u_offset, 0.0, 1.0);
}
`, `
#ifdef GL_ES
precision highp float;
#endif
+uniform sampler2D u_texture;
uniform vec4 u_color;
+uniform float u_color_mix;
+varying vec2 uv;
void main() {
- gl_FragColor = u_color;
+ gl_FragColor = mix(texture2D(u_texture, uv), u_color, u_color_mix);
}
`);
if (program_main === null) {
@@ -64,6 +85,10 @@ void main() {
-1.0, -1.0, 1.0, 1.0, -1.0, 1.0
]), gl.STATIC_DRAW);
+ framebuffer_color_texture = gl.createTexture();
+ sampler_texture = gl.createTexture();
+
+ set_up_framebuffer();
frame(0.0);
}
@@ -71,61 +96,86 @@ void main() {
function frame(time) {
time *= 1e-3;
- let width = canvas.offsetWidth;
- let height = canvas.offsetHeight;
- canvas.width = width;
- canvas.height = height;
-
- if (width !== prev_width || height !== prev_height) {
- console.log('new framebuffer');
- prev_width = width;
- prev_height = height;
- framebuffer = gl.createFramebuffer();
- framebuffer_color_texture = gl.createTexture();
- gl.bindTexture(gl.TEXTURE_2D, framebuffer_color_texture);
- gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, width, height, 0,
- gl.RGBA, gl.UNSIGNED_BYTE, null);
- gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
- gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
- 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.bindFramebuffer(gl.FRAMEBUFFER, framebuffer);
- gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, framebuffer_color_texture, 0);
- let status = gl.checkFramebufferStatus(gl.FRAMEBUFFER);
- if (status !== gl.FRAMEBUFFER_COMPLETE) {
- show_error('Error: framebuffer incomplete (status ' + status + ')');
- return;
- }
+ let canvas_width = canvas.offsetWidth;
+ let canvas_height = canvas.offsetHeight;
+ canvas.width = canvas_width;
+ canvas.height = canvas_height;
+
+ let step = true;
+
+ if (step) {
+ perform_step();
+ }
+
+ gl.bindFramebuffer(gl.FRAMEBUFFER, null);
+ gl.viewport(0, 0, canvas_width, canvas_height);
+ gl.clearColor(0.0, 0.0, 0.0, 1.0);
+ gl.clear(gl.COLOR_BUFFER_BIT);
+
+ let aspect_ratio = width / height;
+ if (canvas_width / aspect_ratio < canvas_height) {
+ // landscape mode
+ let viewport_height = Math.floor(canvas_width / aspect_ratio);
+ gl.viewport(0, Math.floor((canvas_height - viewport_height) * 0.5), canvas_width, viewport_height);
+ } else {
+ // portrait mode
+ let viewport_width = Math.floor(canvas_height * aspect_ratio);
+ gl.viewport(Math.floor((canvas_width - viewport_width) * 0.5), 0, viewport_width, canvas_height);
+ }
+
+ gl.useProgram(program_post);
+ gl.activeTexture(gl.TEXTURE0);
+ gl.bindTexture(gl.TEXTURE_2D, sampler_texture);
+ gl.uniform1i(gl.getUniformLocation(program_post, 'u_texture'), 0);
+ let v_pos = gl.getAttribLocation(program_post, 'v_pos');
+ gl.enableVertexAttribArray(v_pos);
+ gl.vertexAttribPointer(v_pos, 2, gl.FLOAT, false, 0, 0);
+ gl.drawArrays(gl.TRIANGLES, 0, 6);
+
+ if (requestAnimationFrame == null) {
+ show_error('your browser doesnt support requestAnimationFrame.\noh well.');
+ return;
+ }
+ requestAnimationFrame(frame);
+}
+
+function perform_step() {
+ if (width === -1) {
+ // not properly loaded yet
+ return;
}
gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer);
gl.viewport(0, 0, width, height);
- gl.clearColor(Math.sin(time) ** 2, 0.9, 1.0, 1.0);
+ gl.clearColor(0.5, 0.0, 0.5, 1.0);
gl.clear(gl.COLOR_BUFFER_BIT);
gl.useProgram(program_main);
-
- gl.uniform4fv(gl.getUniformLocation(program_main, 'u_color'), [0.1, 0.7, 0.2, 1.0]);
- gl.uniform2fv(gl.getUniformLocation(program_main, 'u_scale'), [0.5, 0.5]);
+ gl.activeTexture(gl.TEXTURE0);
+ gl.bindTexture(gl.TEXTURE_2D, sampler_texture);
+ gl.uniform4fv(gl.getUniformLocation(program_main, 'u_color'), [1.0, 1.0, 1.0, 1.0]);
+ gl.uniform1f(gl.getUniformLocation(program_main, 'u_color_mix'), 0.1);
+ gl.uniform1i(gl.getUniformLocation(program_main, 'u_sampler_texture'), 0);
gl.bindBuffer(gl.ARRAY_BUFFER, vertex_buffer_rect);
-
let v_pos = gl.getAttribLocation(program_main, 'v_pos');
gl.enableVertexAttribArray(v_pos);
gl.vertexAttribPointer(v_pos, 2, gl.FLOAT, false, 0, 0);
- gl.drawArrays(gl.TRIANGLES, 0, 6);
- gl.useProgram(program_post);
- gl.bindFramebuffer(gl.FRAMEBUFFER, null);
- gl.activeTexture(gl.TEXTURE0);
- gl.bindTexture(gl.TEXTURE_2D, framebuffer_color_texture);
- gl.uniform1i(gl.getUniformLocation(program_post, 'u_texture'), 0);
- v_pos = gl.getAttribLocation(program_post, 'v_pos');
- gl.enableVertexAttribArray(v_pos);
- gl.vertexAttribPointer(v_pos, 2, gl.FLOAT, false, 0, 0);
- gl.drawArrays(gl.TRIANGLES, 0, 6);
+ for (let y = 0; y < 3; ++y) {
+ for (let x = 0; x < 3; ++x) {
+ let k = 1.0 / 3.0;
+ if (x == 1 && y == 1) {
+ continue;
+ }
+ gl.uniform2fv(gl.getUniformLocation(program_main, 'u_scale'), [k, k]);
+ gl.uniform2fv(gl.getUniformLocation(program_main, 'u_offset'), [x * 2.0 / 3.0 - 2.0 / 3.0, y * 2.0 / 3.0 - 2.0 / 3.0]);
+ gl.drawArrays(gl.TRIANGLES, 0, 6);
+ }
+ }
- requestAnimationFrame(frame);
+ gl.bindTexture(gl.TEXTURE_2D, sampler_texture);
+ gl.copyTexImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 0, 0, width, height, 0);
}
function compile_program(name, vertex_source, fragment_source) {
@@ -145,6 +195,31 @@ function compile_program(name, vertex_source, fragment_source) {
return program;
}
+function set_up_framebuffer() {
+ framebuffer = gl.createFramebuffer();
+ let sampler_pixels = new Uint8Array(width * height * 4);
+ sampler_pixels.fill(255);
+ set_up_rgba_texture(sampler_texture, width, height, sampler_pixels);
+ set_up_rgba_texture(framebuffer_color_texture, width, height, null);
+ gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer);
+ gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, framebuffer_color_texture, 0);
+ let status = gl.checkFramebufferStatus(gl.FRAMEBUFFER);
+ if (status !== gl.FRAMEBUFFER_COMPLETE) {
+ show_error('Error: framebuffer incomplete (status ' + status + ')');
+ return;
+ }
+}
+
+function set_up_rgba_texture(texture, width, height, pixels) {
+ gl.bindTexture(gl.TEXTURE_2D, texture);
+ gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, width, height, 0,
+ gl.RGBA, gl.UNSIGNED_BYTE, pixels);
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
+ 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);
+}
+
function compile_shader(name, type, source) {
let shader = gl.createShader(type);
gl.shaderSource(shader, source);