Skip to content

Instantly share code, notes, and snippets.

@lefuturiste
Created March 8, 2022 22:19
Show Gist options
  • Save lefuturiste/45eee83ebe76adcac32579a72f31113a to your computer and use it in GitHub Desktop.
Save lefuturiste/45eee83ebe76adcac32579a72f31113a to your computer and use it in GitHub Desktop.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Voronoi Diagram with GPU</title>
</head>
<body>
<div>
<canvas id="glcanvas" width="512" height="512">
</canvas>
</div>
<script id="vertex-shader" type="x-shader/x-vertex">
attribute vec2 aVertexPosition;
varying vec2 uv;
void main() {
gl_Position = vec4(aVertexPosition.x, aVertexPosition.y, 0, 1.0);
uv = vec2(aVertexPosition.x, aVertexPosition.y);
}
</script>
<script id="fragment-shader" type="x-shader/x-fragment">
#ifdef GL_ES
precision highp float;
#endif
#define POINTS_COUNT %POINTS_COUNT%
uniform sampler2D diagPointsTex;
uniform sampler2D diagColorsTex;
varying vec2 uv;
void main() {
int closest = -1;
float closestDistance = 0.0;
for (int i = 0; i < POINTS_COUNT; ++i) {
mediump vec4 _diagPoint = texture2D(diagPointsTex, vec2((float(i)/float(POINTS_COUNT)), 0));
vec2 diagPoint = vec2(_diagPoint.x, _diagPoint.y);
float d = length(
diagPoint - uv
);
if (i == 0 || d < closestDistance) {
closest = i;
closestDistance = d;
}
if (d < 0.008) {
gl_FragColor = vec4(1, 1, 1, 1);
return;
}
}
gl_FragColor = texture2D(diagColorsTex, vec2((float(closest)/float(POINTS_COUNT)), 1));
}
</script>
<script src="voronoi_webgl.js">
</script>
</body>
</html>
let gl = null;
let glCanvas = null;
let aVertexPosition;
let uDiagPoints;
let uDiagPointsColors;
let vertexCount;
const vertexNumComponents = 2;
let numberOfPoints = 4;
const startup = (nb = null) => {
if (nb !== null) {
numberOfPoints = nb
}
glCanvas = document.getElementById("glcanvas");
gl = glCanvas.getContext("webgl");
const shaderSet = [
{ type: gl.VERTEX_SHADER, id: "vertex-shader" },
{ type: gl.FRAGMENT_SHADER, id: "fragment-shader" }
];
shaderProgram = buildShaderProgram(shaderSet);
if (shaderProgram == -1) {
return
}
vertexBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
let vertexArray = new Float32Array([ -1,1, -1,-1, 1,-1, 1,1 ]);
gl.bufferData(gl.ARRAY_BUFFER, vertexArray, gl.STATIC_DRAW);
vertexCount = vertexArray.length / vertexNumComponents;
let baseColor = [0x39/255, 0x47/255, 0x20/255]
let pointsRaw = []
let colorsRaw = []
for (let i = 0; i < numberOfPoints; i++) {
pointsRaw.push((Math.random() > 0.5 ? 1 : -1)*Math.random())
pointsRaw.push((Math.random() > 0.5 ? 1 : -1)*Math.random())
pointsRaw.push(0)
pointsRaw.push(0)
colorsRaw.push(0.2+i*(0.8/numberOfPoints))
colorsRaw.push(0)
colorsRaw.push(0)
colorsRaw.push(0.1+i*((1-0.1)/numberOfPoints))
}
console.log(pointsRaw, colorsRaw)
gl.getExtension('OES_texture_float');
let initTexture = (textureId, rawData, count) => {
// 3x1 pixel 1d texture
gl.activeTexture(textureId);
let tex = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, tex);
let data = new Float32Array(rawData)
gl.texImage2D(
gl.TEXTURE_2D,
0, gl.RGBA,
count, 1,
0, gl.RGBA,
gl.FLOAT,
data
);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
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);
}
initTexture(gl.TEXTURE0, pointsRaw, numberOfPoints)
initTexture(gl.TEXTURE1, colorsRaw, numberOfPoints)
animateScene();
}
const buildShaderProgram = (shaderInfo) => {
let program = gl.createProgram();
shaderInfo.forEach((desc) => {
let shader = compileShader(desc.id, desc.type);
if (shader == -1) {
return -1;
}
if (shader) {
gl.attachShader(program, shader);
}
});
gl.linkProgram(program)
if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
console.error("Error linking shader program:", gl.getProgramInfoLog(program));
return -1;
}
return program;
}
const compileShader = (id, type) => {
let code = document.getElementById(id).firstChild.nodeValue;
let shader = gl.createShader(type);
if (id == 'fragment-shader') {
code = code.replace('%POINTS_COUNT%', numberOfPoints)
}
gl.shaderSource(shader, code);
gl.compileShader(shader);
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
console.error(
`Error compiling ${type === gl.VERTEX_SHADER ? "vertex" : "fragment"} shader:`,
gl.getShaderInfoLog(shader)
);
return -1;
}
return shader;
}
const animateScene = () => {
gl.viewport(0, 0, glCanvas.width, glCanvas.height);
gl.clearColor(0.8, 0.9, 1.0, 1.0);
gl.clear(gl.COLOR_BUFFER_BIT);
gl.useProgram(shaderProgram);
// gl.uniform1f(uScalingFactor, currentScale);
gl.uniform1f(gl.getUniformLocation(shaderProgram, "numberOfPoints"), numberOfPoints);
// gl.uniform2fv(uRotationVector, currentRotation);
//gl.uniform4fv(uGlobalColor, [0.1, 0.7, 0.2, 1.0]);
gl.uniform1i(gl.getUniformLocation(shaderProgram, "diagPointsTex"), 0);
gl.uniform1i(gl.getUniformLocation(shaderProgram, "diagColorsTex"), 1);
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
aVertexPosition = gl.getAttribLocation(shaderProgram, "aVertexPosition");
gl.vertexAttribPointer(aVertexPosition, vertexNumComponents, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(aVertexPosition);
gl.drawArrays(gl.TRIANGLE_FAN, 0, vertexCount);
}
window.addEventListener("load", () => {
startup()
}, false);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment