var gl, canvas;
const FLOAT_SIZE = 4;
var handleWebGLError = function (type, obj) {
if (type === 'shader') {
if (!gl.getShaderParameter(obj, gl.COMPILE_STATUS)) {
console.error(gl.getShaderInfoLog(obj));
return null;
}
} else if (type === 'program') {
if (!gl.getProgramParameter(obj, gl.LINK_STATUS)) {
console.error(gl.getProgramInfoLog(obj));
return null;
}
}
};
var getAndCompileShaders = function (id, type) {
var shader = gl.createShader(type);
var shaderText = document.getElementById(id).text.trim();
gl.shaderSource(shader, shaderText);
gl.compileShader(shader);
handleWebGLError('shader', shader);
return shader;
};
var getProgram = function (vsId, fsId, use) {
var vs = getAndCompileShaders(vsId, gl.VERTEX_SHADER);
var fs = getAndCompileShaders(fsId, gl.FRAGMENT_SHADER);
var program = gl.createProgram();
gl.attachShader(program, vs);
gl.attachShader(program, fs);
gl.linkProgram(program);
handleWebGLError('program', program);
if (use) {
gl.useProgram(program);
}
return program;
};
var createBuffer = function (gl, coords, isStatic) {
var b = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, b);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(coords), isStatic ? gl.STATIC_DRAW : gl.DYNAMIC_DRAW);
return b;
};
var createBuffers = function (gl, coordsArr, isStatic) {
var bufferArr = [];
coordsArr.forEach (function (coords) {
var b = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, b);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(coords), isStatic ? gl.STATIC_DRAW : gl.DYNAMIC_DRAW);
bufferArr.push(b);
});
return bufferArr;
};
var initWebGLCanvas = function (canvasId) {
canvas = document.getElementById(canvasId);
canvas.width = window.innerWidth * 0.90;
canvas.height = window.innerHeight * 0.90;
//get Context
var gl = canvas.getContext('webgl2');
//assign a default color
gl.clearColor(0.002, 0.12, 0.3, 1.0);
gl.clear(gl.COLOR_BUFFER_BIT);
return gl;
};
var clearCanvas = function (gl) {
gl.clearColor(0.002, 0.12, 0.3, 1.0);
gl.clear(gl.COLOR_BUFFER_BIT);
}
var fetchDataFromGPUAndRenderTrianglesToCanvas = function (o, gl) {
o.buffer.forEach (function (data) {
gl.bindBuffer(gl.ARRAY_BUFFER, data);
gl.vertexAttribPointer(o.positionIndex, o.positionSize, gl.FLOAT, gl.FALSE, o.stride*FLOAT_SIZE, o.positionOffset*FLOAT_SIZE);
gl.vertexAttribPointer(o.colorIndex, o.colorSize, gl.FLOAT, gl.FALSE, o.stride*FLOAT_SIZE, o.colorOffset*FLOAT_SIZE);
gl.drawArrays(gl.TRIANGLES, o.startIndexToDraw, o.numOfComponents);
});
};
var fetchDataFromGPUAndRenderToCanvas = function (o, gl) {
o.buffer.forEach (function (data) {
gl.bindBuffer(gl.ARRAY_BUFFER, data);
gl.vertexAttribPointer(o.positionIndex, o.positionSize, gl.FLOAT, gl.FALSE,
(o.stride || 0)*FLOAT_SIZE, (o.colorOffset || 0)*FLOAT_SIZE);
o.uniforms.forEach (function (uniform) {
gl.uniform1f(gl.getUniformLocation(gl.program, uniform.key), uniform.value);
});
gl.drawArrays(o.drawingType, o.startIndexToDraw, o.numOfComponents);
});
};
var enableVerticesToPickBinaryDataWithinGPU = (position, color) => {
if (position) {
gl.positionIndex = gl.getAttribLocation(gl.program, position);
gl.enableVertexAttribArray(gl.positionIndex);
}
if (color) {
gl.colorIndex = gl.getAttribLocation(gl.program, color);
gl.enableVertexAttribArray(gl.colorIndex);
}
};
var getSampleTriangleUsingPoints = () => {
//left
var triangleVertices = new Float32Array(20 + 2 + 20 + 20);
for (var i = 0; i < 19; i = i + 2) {
var lastX = i === 0 ? -1.1 : triangleVertices[i - 2];
var lastY = i === 0 ? -1.2 : triangleVertices[i - 1];
triangleVertices[i] = lastX + 0.1;
triangleVertices[i + 1] = lastY + 0.2;
}
//top
triangleVertices[20] = 0.0; triangleVertices[21] = 1.0;
//right
for (var i = 22; i < 40; i = i + 2) {
triangleVertices[i] = triangleVertices[i - 2] + 0.1;
triangleVertices[i + 1] = triangleVertices[i - 1] - 0.2;
}
triangleVertices[40] = 1.0; triangleVertices[41] = -1.0;
//bottom
var x = -1.0, y = -1.0;
for (var i = 42; i <= 60; i = i + 2) {
if (i !== 42) {
x += 0.2;
}
triangleVertices[i] = x;
triangleVertices[i + 1] = y;
}
//allocate a memory in GPU
var verticesBuffer = gl.createBuffer();
//link CPU variable to point to GPU using gl.ARRAY_BUFFER gate
gl.bindBuffer(gl.ARRAY_BUFFER, verticesBuffer);
//Send data to the memory location allocated above, gl.STATIC_DRAW means vertices won't be changed later and GPU will store the vertices far from the CPU.
gl.bufferData(gl.ARRAY_BUFFER, triangleVertices, gl.STATIC_DRAW);
return verticesBuffer;
}
var getBufferUsingPoints = function (points) {
var f32a = new Float32Array(points);
//allocate a memory in GPU
var verticesBuffer = gl.createBuffer();
//link CPU variable to point to GPU using gl.ARRAY_BUFFER gate
gl.bindBuffer(gl.ARRAY_BUFFER, verticesBuffer);
//Send data to the memory location allocated above, gl.STATIC_DRAW means vertices won't be changed later and GPU will store the vertices far from the CPU.
gl.bufferData(gl.ARRAY_BUFFER, f32a, gl.STATIC_DRAW);
return verticesBuffer;
};
document.addEventListener('DOMContentLoaded', function () {
//resize canvas
gl = initWebGLCanvas('canvas');
gl.program = getProgram('vertexShader', 'fragmentShader', true);
clearCanvas(gl);
var isDragging = false;
var points = [];
var clear = document.getElementById('clear');
clear.onclick = () => {
points = [];
clearCanvas(gl);
};
canvas.onmouseup = (e) => {
isDragging = false;
};
canvas.onmouseout = (e) => {
isDragging = false;
};
canvas.onmousedown = (e) => {
isDragging = true;
};
canvas.onmousemove = (e) => {
if (isDragging) {
clearCanvas(gl);
enableVerticesToPickBinaryDataWithinGPU('position');
points.push(-1.0 + e.offsetX/canvas.width*2.0, 1.0 - e.offsetY/canvas.height*2.0);
var pointsBuffer = getBufferUsingPoints(points);
fetchDataFromGPUAndRenderToCanvas({
positionIndex : gl.positionIndex,
buffer : [pointsBuffer],
positionSize : 2,
uniforms : [{key : 'vSize', value : 1/canvas.width}],
startIndexToDraw : 0,
numOfComponents : points.length/2,
drawingType : gl.POINTS
}, gl);
}
};
});
<canvas id="canvas"></canvas>
<button id="clear">Clear</button>
<script type="vertexShader" id="vertexShader">
#version 300 es
in vec2 position;
in vec4 color;
out vec4 fcolor;
uniform float vSize;
void main () {
gl_Position = vec4(position, 0.0, 1.0);
gl_PointSize = 1.0;
fcolor = color;
}
</script>
<script type="fragmentShader" id="fragmentShader">
#version 300 es
precision mediump float;
in vec4 fcolor;
out vec4 finalColor;
void main () {
finalColor = vec4(0.0, 1.0, 0.0, 1.0);
}
</script>