First triangle
Finally it's time to put everything together and draw our first triangle! That's quite a large HTML page, however we've already seen almost all of it parts. Take a closer look:
<!DOCTYPE html>
<html>
<body>
<style type="text/css">
textarea
{
display: none;
}
</style>
<canvas id="canvas" width="600" height="600"></canvas>
<textarea id="vs">
#version 300 es
precision highp float;
in vec2 position;
void main(void)
{
gl_Position = vec4(position, 0.0, 1.0);
}
</textarea>
<textarea id="fs">
#version 300 es
precision highp float;
out vec4 frag_color;
void main(void)
{
frag_color = vec4(1.0, 0.5, 0.25, 1.0);
}
</textarea>
<script type="text/javascript">
const canvas = document.getElementById("canvas");
const gl = canvas.getContext("webgl2");
function text(id)
{
return document.getElementById(id).value;
}
function compile(shader, source)
{
gl.shaderSource(shader, source);
gl.compileShader(shader);
if(!gl.getShaderParameter(shader, gl.COMPILE_STATUS))
{
alert(gl.getShaderInfoLog(shader));
}
}
const vs = gl.createShader(gl.VERTEX_SHADER);
const fs = gl.createShader(gl.FRAGMENT_SHADER);
const program = gl.createProgram();
compile(vs, text("vs"));
compile(fs, text("fs"));
gl.attachShader(program, vs);
gl.attachShader(program, fs);
gl.linkProgram(program);
if(!gl.getProgramParameter(program, gl.LINK_STATUS))
{
alert(gl.getProgramInfoLog(program));
}
const h = Math.sqrt(0.75);
const vertices =
[
-0.5, -h / 3,
0.0, 2 * h / 3,
0.5, -h / 3
];
const buffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);
const position = gl.getAttribLocation(program, "position");
gl.enableVertexAttribArray(position);
gl.vertexAttribPointer(position, 2, gl.FLOAT, false, 0, 0);
function draw(timestamp)
{
gl.clearColor(0, 0, 0, 1);
gl.clear(gl.COLOR_BUFFER_BIT);
gl.useProgram(program);
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
gl.drawArrays(gl.TRIANGLES, 0, 3);
window.requestAnimationFrame(draw);
}
window.requestAnimationFrame(draw);
</script>
</body>
</html>
Our position
input value is a vertex attribute. We need to get its location, enable it, and bind a data to it:
const position = gl.getAttribLocation(program, "position");
gl.enableVertexAttribArray(position);
gl.vertexAttribPointer(position, 2, gl.FLOAT, false, 0, 0);
First two lines are obvious. Let's take a closer look at the last one:
gl.vertexAttribPointer
(
position, // The attrubute location
2, // The size of the attribute. The size is 2 because position is 2-components vector
gl.FLOAT, // A type of each component
false, // True if values are normalized. In our case they're not
0, // A stride between the vertex data. In our case the data is packed tightly, that's why the stride is 0
0 // The offset of the data in the buffer. 0 in our case since the data is started right away
);
So, vertexAttribPointer
is a function that tells the GPU how the data is presented in a GPU buffer.
And the last step is:
gl.useProgram(program);
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
gl.drawArrays(gl.TRIANGLES, 0, 3);
Finally, we used our GLSL program. After that we told GPU that we're going to work with buffer
GPU buffer. And right after that we called drawArrays
which has drawn our first triangle. Whew! Put the example above into a file and open it in your web browser. You should see a picture like this:
drawArrays
takes three parameters. The first one sets a type of the geometry topology. In the example above it's gl.TRIANGLES
which means that every three vertices represent a triangle. The second parameter is a vertex offset in the GPU buffer. And the third parameter is a count of vertices that should be rendered. One triangle consists of three vertices — that's why the third parameter is 3.
Congratulations! You've just drawn your very first triangle with WebGL!