1) Theory
WebGL is a 3D rendering API designed for Web.
basically you’ll need to send two types of data to run WebGL : a clip space coordinates & the colors.
WebGL runs on the GPU and as developper you’ll need to provide the code that run on a GPU, those code are in a form of two shaders :
- The Vertex Shader
- The Fragment Shader
Those shaders are written in a language called GLSL (GL Shader Language), together they are called a program. Nearly the entire WebGL API is about setting up those shaders and make them run on the GPU.
For the next two parts we will do a quick overview of what are those shaders and how they work.
The Vertex Shader
What is a shader ? Shaders are a set of instruction. All those instructions are executed all at once for every single pixel on your screen. The code you will write will have to behave differently depending on the position of the pixel on the screen.
Before starting with Vertex Shaders, let’s see what a vertex is.
A vertex means a corner or a point where two (or more) lines meet. For example a square has four corners, each is called a vertex. (the plural form of vertex is vertices) With this said, the vertex shader performs the transform of the vertices position on the screen. You can also decide to add a bunch of other type of data like matrices, colors, normal map. (a normal map is the direction of a surface and is used for lighting) The vertex shader is called once per vertex.
A vertex shader always take this form :
void main()
{
gl_Position = clipSpaceCoords;
}
Each time the shader is call you’ll need to specified to the special variable gl_Position some coordinates.
The Fragment Shader
The job of the fragment shader is to provide a color for the current pixel.
Your fragment shader is called once per pixel. Each time the shaded is called, you’ll need to specified to the special variable gl_FragColor some color.
A fragment shader always take this form :
precision mediump float;
void main()
{
gl_Fragcolor = colors;
}
2) A first shape : Triangle
Now let’s draw our first shape : a basic green triangle. First of all we need to setup our WebGL context.
To do this, you’ll need to setup a canvas on your .html file.
// SETTING UP OUR CANVAS
this.canvasElm = document.createElement("canvas");
this.canvasElm.width = 800;
this.canvasElm.height = 800;
// SETTING UP OUR WEBGL Context.
const gl = this.canvasElm.getContext("webgl2");
document.body.appendChild(this.canvasElm);
Once it’s done, we will need to create our data for our vertices. This data will be a 3x3 matrice, each row will corresponds to a point of our triangle
// X, Y, Z the position of the corners of our triangle.
const verticesData =
[
0,1,0,
1,-1,0,
-1,-1,0
]
We then need to create a buffer for our data. I will head back in a future article on how buffers work. Just see them as temporary box that give a data to the GPU.
A buffer is a part of a memory storage used to temporarily store data. It’s commonly use to increase application performance by allowing synchronous operations
const buffer = gl.createBuffer();
// The line below do a couple of things, it assign the constant ARRAY_BUFFER to our new buffer
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
// We then pass the data to that buffer
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(verticesData), gl.STATIC_DRAW);
Then we need to create our two shaders (I’ll write them on my index.html file) :
<!-- VERTEX SHADER -->
<script id="vertext_shader" type="vs_01">
attribute vec3 a_position;
void main()
{
gl_Position = vec4(a_position, 1);
}
</script>
<!-- FRAGMENT SHADER -->
<script id="fragment_shader" type="fs_01">
void main()
{
gl_Fragcolor = vec4(1, 0, 1, 1); // R, G, B, A
}
</script>
Now let’s head back to our js file, we need to create the shaders and specify their source code.
// Create the shader and specify is source code.
const vertexShader = gl.createShader(gl.VERTEX_SHADER);
gl.shaderSource(vertexShader, document.querySelector("vertext_shader"));
gl.compileShader(vertexShader);
const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
gl.shaderSource(vertexShader, document.querySelector("fragment_shader"));
gl.compileShader(fragmentShader);
Now we need to create the program that will link our pair of shader to the WebGL context.
// create the program and attach both shaders.
const program = gl.createProgram();
gl.attachShader(program, vertexShader);
gl.attachShader(program, fragmentShader);
gl.linkProgram(program);
Finally we need to enable the attributes (here the only attributes we have is the position of our vertex)
// enable attributes, (in this case the position of our vertex)
const posAttribLocation = gl.getAttribLocation(program, `a_position`);
gl.enableVertexAttribArray(posAttribLocation);
gl.vertexAttribPointer(posAttribLocation, 3, gl.FLOAT, false, 0, 0);
// We ask WebGL to use the program we've created and draw our triangle
gl.useProgram(program);
// The line above describe the shape we want to have (typeOfShape, startIndex, count)
gl.drawArrays(gl.TRIANGLES, 0, 3);
That’s all for this short introduction to WebGL, thanks for reading !