Created
December 5, 2023 21:55
-
-
Save kcinnu/47e7d380dc483194a326d9ac8b44ea24 to your computer and use it in GitHub Desktop.
procedural binary tree pixelart
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html> | |
<head> | |
<style> | |
html { | |
background-color: black; | |
} | |
canvas { | |
image-rendering: pixelated; | |
} | |
</style> | |
</head> | |
<body> | |
<canvas id="c"></canvas> | |
<script src="main.js"></script> | |
</body> | |
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const c = document.getElementById("c"); | |
const gl = c.getContext("webgl2", { antialias: false }); | |
function createShader(type) { | |
return src => { | |
const shd = gl.createShader(type); | |
gl.shaderSource(shd, src); | |
gl.compileShader(shd); | |
if (gl.getShaderParameter(shd, gl.COMPILE_STATUS)) { | |
return shd; | |
} | |
throw new Error("couldnt compile shader:\n" + gl.getShaderInfoLog(shd)); | |
}; | |
} | |
const fragShd = createShader(gl.FRAGMENT_SHADER); | |
const vertShd = createShader(gl.VERTEX_SHADER); | |
function createProgram(unifs, attrs, vert, frag) { | |
const prog = gl.createProgram(); | |
gl.attachShader(prog, vert); | |
gl.attachShader(prog, frag); | |
gl.linkProgram(prog); | |
if (gl.getProgramParameter(prog, gl.LINK_STATUS)) { | |
let obj = { prog, u: {}, a: {} }; | |
for (const u of unifs.split(' ')) { | |
obj.u[u] = gl.getUniformLocation(prog, u); | |
} | |
for (const a of attrs.split(' ')) { | |
obj.a[a] = gl.getAttribLocation(prog, a); | |
} | |
return obj; | |
} | |
throw new Error("couldnt link shaders:\n" + gl.getProgramInfoLog(prog)); | |
} | |
function add([ax, ay], [bx, by]) { | |
return [ax + bx, ay + by]; | |
} | |
function sub([ax, ay], [bx, by]) { | |
return [ax - bx, ay - by]; | |
} | |
function mul([ax, ay], [bx, by]) { | |
return [ax * bx, ay * by]; | |
} | |
function div([ax, ay], [bx, by]) { | |
return [ax / bx, ay / by]; | |
} | |
function scale([x, y], s) { | |
return [x * s, y * s]; | |
} | |
function rot90([x, y]) { | |
return [-y, x]; | |
} | |
function rot([x, y], a) { | |
const c = Math.cos(a); | |
const s = Math.sin(a); | |
return [x * c - y * s, y * c + x * s]; | |
} | |
function round([x, y]) { | |
return [Math.round(x), Math.round(y)]; | |
} | |
const d_tree = createProgram("adj", "pos col", vertShd`#version 300 es | |
precision highp float; | |
in vec2 pos; | |
in vec3 col; | |
out vec3 col_; | |
uniform vec4 adj; | |
void main() { | |
gl_Position = vec4(pos * adj.xy + adj.zw, 0, 1); | |
col_ = col; | |
} | |
`, fragShd`#version 300 es | |
precision highp float; | |
in vec3 col_; | |
out vec4 col; | |
void main() { | |
//col = vec4(.5,.3,0,1); | |
col = vec4(mix(col_, vec3(.5,.3,0), .7), 1); | |
} | |
`); | |
const d_stars = createProgram("", "pos uv bright", vertShd`#version 300 es | |
precision highp float; | |
in vec4 pos; | |
in vec2 uv; | |
out vec2 uv_; | |
in float bright; | |
out float bright_; | |
void main() { | |
gl_Position = pos; | |
uv_ = uv; | |
bright_ = bright; | |
} | |
`, fragShd`#version 300 es | |
precision highp float; | |
in vec2 uv_; | |
out vec4 col; | |
in float bright_; | |
void main() { | |
vec2 uv = abs(uv_); | |
uv = vec2(min(uv.x, uv.y), max(uv.x, uv.y)); | |
if (uv.x > (1.-uv.y) * .5) discard; | |
col = vec2(bright_ * exp2(-uv.x*2.),0).xxxy; | |
} | |
`); | |
const d_sky = createProgram("", "pos", vertShd`#version 300 es | |
precision highp float; | |
in vec4 pos; | |
out vec2 uv; | |
void main() { | |
gl_Position = pos; | |
uv = pos.xy; | |
} | |
`, fragShd`#version 300 es | |
precision highp float; | |
in vec2 uv; | |
out vec4 col; | |
void main() { | |
float x = .5-uv.y*.5; | |
col = vec4(vec3(x*x*x*x*x*x*.9,x*x*.4,.3*x+.1)*.5, 1); | |
} | |
`); | |
c.width = 300; | |
c.height = 128 * 1.5; | |
c.style.width = c.width / devicePixelRatio * 3 + 'px'; | |
c.style.height = c.height / devicePixelRatio * 3 + 'px'; | |
let lines = []; | |
function cr_tree(lvl, pos, dir) { | |
const npos = add(pos, dir); | |
lines.push({ | |
a: pos, | |
b: npos, | |
}); | |
if (lvl === 10) return; | |
if (lvl <= 4 || Math.random() < .8) cr_tree(lvl + 1, npos, scale(rot(dir, 0.4 + (Math.random() - .5) * .7), .8)); | |
if (lvl <= 4 || Math.random() < .8) cr_tree(lvl + 1, npos, scale(rot(dir, -0.4 + (Math.random() - .5) * .7), .8)); | |
} | |
cr_tree(0, [0, 0], [0, 1]); | |
let stars = []; | |
for (let i = 0; i < 100; i++) { | |
stars.push({ | |
pos: [Math.random() * 2 - 1, Math.random() * 2 - 1], | |
size: Math.random() * 3 + 1, | |
}); | |
} | |
const star_pos = gl.createBuffer(); | |
const star_uv = gl.createBuffer(); | |
const star_br = gl.createBuffer(); | |
{ | |
const buf = new Float32Array(stars.length * 6 * 2); | |
for (let i = 0; i < stars.length; i++) { | |
const { pos, size } = stars[i]; | |
const rsc = scale([2 / c.width, 2 / c.height], 1); | |
const pos2 = mul(add(round(div(pos, rsc)), [.5, .5]), rsc); | |
const offs = scale([2 / c.width, 2 / c.height], size); | |
const x = add(pos2, mul(offs, [-1, -1])); | |
const y = add(pos2, mul(offs, [1, -1])); | |
const z = add(pos2, mul(offs, [-1, 1])); | |
const w = add(pos2, mul(offs, [1, 1])); | |
buf.set([].concat(x, z, w, x, w, y), i * 12); | |
} | |
gl.bindBuffer(gl.ARRAY_BUFFER, star_pos); | |
gl.bufferData(gl.ARRAY_BUFFER, buf, gl.STATIC_DRAW); | |
for (let i = 0; i < stars.length; i++) { | |
const x = [-1, -1]; | |
const y = [1, -1]; | |
const z = [-1, 1]; | |
const w = [1, 1]; | |
buf.set([].concat(x, z, w, x, w, y), i * 12); | |
} | |
gl.bindBuffer(gl.ARRAY_BUFFER, star_uv); | |
gl.bufferData(gl.ARRAY_BUFFER, buf, gl.STATIC_DRAW); | |
for (let i = 0; i < stars.length; i++) { | |
const r = Math.random() * .5; | |
buf.set([r, r, r, r, r, r], i * 6); | |
} | |
gl.bindBuffer(gl.ARRAY_BUFFER, star_br); | |
gl.bufferData(gl.ARRAY_BUFFER, buf.slice(0, stars.length * 6), gl.STATIC_DRAW); | |
} | |
const tree_pos = gl.createBuffer(); | |
const tree_col = gl.createBuffer(); | |
{ | |
const buf = new Float32Array(lines.length * 6 * 2); | |
for (let i = 0; i < lines.length; i++) { | |
const { a, b } = lines[i]; | |
const x = sub(a, scale(rot90(sub(b, a)), .15)); | |
const y = add(a, scale(rot90(sub(b, a)), .15)); | |
const z = sub(b, scale(rot90(sub(b, a)), .15)); | |
const w = add(b, scale(rot90(sub(b, a)), .15)); | |
buf.set([].concat(x, z, w, x, w, y), i * 6 * 2); | |
} | |
gl.bindBuffer(gl.ARRAY_BUFFER, tree_pos); | |
gl.bufferData(gl.ARRAY_BUFFER, buf, gl.DYNAMIC_DRAW); | |
const cbuf = new Float32Array(lines.length * 6 * 3); | |
for (let i = 0; i < lines.length; i++) { | |
// const col = [Math.random(), Math.random(), Math.random()]; | |
const tmp = Math.random(); | |
const col = [tmp, tmp, tmp]; | |
cbuf.set([].concat(col, col, col, col, col, col), i * 6 * 3); | |
} | |
gl.bindBuffer(gl.ARRAY_BUFFER, tree_col); | |
gl.bufferData(gl.ARRAY_BUFFER, cbuf, gl.DYNAMIC_DRAW); | |
} | |
const sky_pos = gl.createBuffer(); | |
gl.bindBuffer(gl.ARRAY_BUFFER, sky_pos); | |
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([-1, -1, -1, 3, 3, -1]), gl.STATIC_DRAW); | |
gl.viewport(0, 0, c.width, c.height); | |
// gl.clearColor(0, 0, 0.2, 1); | |
// gl.clear(gl.COLOR_BUFFER_BIT); | |
gl.useProgram(d_sky.prog) | |
gl.enableVertexAttribArray(d_stars.a.pos); | |
gl.bindBuffer(gl.ARRAY_BUFFER, sky_pos); | |
gl.vertexAttribPointer(d_sky.a.pos, 2, gl.FLOAT, false, 0, 0); | |
gl.drawArrays(gl.TRIANGLES, 0, 3); | |
gl.disableVertexAttribArray(d_sky.a.pos); | |
gl.useProgram(d_stars.prog); | |
gl.enableVertexAttribArray(d_stars.a.pos); | |
gl.bindBuffer(gl.ARRAY_BUFFER, star_pos); | |
gl.vertexAttribPointer(d_tree.a.pos, 2, gl.FLOAT, false, 0, 0); | |
gl.enableVertexAttribArray(d_stars.a.uv); | |
gl.bindBuffer(gl.ARRAY_BUFFER, star_uv); | |
gl.vertexAttribPointer(d_stars.a.uv, 2, gl.FLOAT, false, 0, 0); | |
gl.enableVertexAttribArray(d_stars.a.bright); | |
gl.bindBuffer(gl.ARRAY_BUFFER, star_br); | |
gl.vertexAttribPointer(d_stars.a.bright, 1, gl.FLOAT, false, 0, 0); | |
gl.enable(gl.BLEND); | |
gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); | |
gl.drawArrays(gl.TRIANGLES, 0, stars.length * 6); | |
gl.disable(gl.BLEND); | |
gl.disableVertexAttribArray(d_stars.a.pos); | |
gl.disableVertexAttribArray(d_stars.a.uv_); | |
gl.disableVertexAttribArray(d_stars.a.bright); | |
gl.useProgram(d_tree.prog); | |
gl.enableVertexAttribArray(d_tree.a.pos); | |
gl.bindBuffer(gl.ARRAY_BUFFER, tree_pos); | |
gl.vertexAttribPointer(d_tree.a.pos, 2, gl.FLOAT, false, 0, 0); | |
gl.enableVertexAttribArray(d_tree.a.col); | |
gl.bindBuffer(gl.ARRAY_BUFFER, tree_col); | |
gl.vertexAttribPointer(d_tree.a.col, 3, gl.FLOAT, false, 0, 0); | |
gl.uniform4f(d_tree.u.adj, c.height / c.width * 2 / 5, 2 / 5, 0, -1); | |
gl.drawArrays(gl.TRIANGLES, 0, lines.length * 6); | |
gl.disableVertexAttribArray(d_tree.a.pos); | |
gl.disableVertexAttribArray(d_tree.a.col); | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment