WebGL Game Development Tutorials

WebGL Book: A WebGL Tutorial Reference Book
Easily understand WebGL and learn how to create 3D worlds and games from scratch.

WebGL Gems is the authorative guide on WebGL programming published by Learning Curve - education for software developers. This book in JavaScript programming series clearly, and in easy to follow language, explains fundamental principles behind programming 3D games using the WebGL framework for JavaScript.

Preorder Now in PDF format for Kindle and get a discount.
WebGL Gems is coming out on April 15th, 2017.
The paperback version will be available via this Amazon page, please bookmark it!

Drawing a Triangle With Shaders

A "hello triangle" example for drawing primitives in WebGL

I think we've accumulated enough knowledge up to this point to start doing something interesting on the screen. Let's construct a basic primitive out of vertex coordinates. Triangle is the building block of a 3-dimensional model. Let's see what it takes to create and render one on the canvas screen. We will use initialization and shader functions from previous chapters.

Recall how in Gem 8 we found out how to wait for shaders to load before starting the render loop. We used a function we created attached to the root window object called webGLResourcesLoaded.

In this section we will modify the contents of that function with triangle-rendering code. We'll take a look at the process one step at a time given that our shaders are already loaded from either SCRIPT tags or a URL location.

Let's enter the webGLResourcesLoaded function:


// An event that fires when all shader resources
// finish loading in CreateShadersFromFile
window.webGLResourcesLoaded = function()
{

First, we need to specify actual vertices of the triangle. In WebGL we will need two arrays to orchestrate this. The actual vertex array vertices and the array indices.


    console.log("webGLResourcesLoaded():" +
                "All webGL shaders have finished loading!");

    // Specify triangle vertex data:
    var vertices = [
        -0.0,  0.5, 0.0, // Vertex A (x,y,z)
        -0.5, -0.5, 0.0, // Vertex B (x,y,z)
         0.5, -0.5, 0.0  // Vertex C (x,y,z)
    ];

    var indices = [0, 1, 2]; // One index per vertex coordinate

The next step is creating buffers for storing both of our shaders. Each shader must be then bound to its respective array buffer gl.ARRAY_BUFFER and gl.ELEMENT_ARRAY_BUFFER flags help us accomplish this.

Once the buffers are bound, we now need to link it to the actual data stored in vertices and indices arrays. This is done by using gl.bufferData function.

Finally, the buffer is unbound. We have to detach the buffer because at this point we're done and we no longer need the buffer object. It was used temporarily only to link up our vertex and index array data to the buffer. Passing null as the second parameter to gl.bufferData will unbind it:


    // Create buffer objects for storing triangle vertex and index data
    var vertexbuffer = gl.createBuffer();
    var indexbuffer = gl.createBuffer();

    // Bind and create enough room for our data on respective buffers
    // Bind it to ARRAY_BUFFER
    gl.bindBuffer(gl.ARRAY_BUFFER, vertexbuffer);
    // Send our vertex data to the buffer using floating point array
    gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);
    // We're done; now we have to unbind the buffer
    gl.bindBuffer(gl.ARRAY_BUFFER, null);

    // Bind it to ELEMENT_ARRAY_BUFFER
    gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexbuffer);
    // Send index (indices) data to this buffer
    gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(indices), gl.STATIC_DRAW);
    // We're done; unbind, we no longer need the buffer object
    gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null);

But now we have to repeat the process for each buffer and rebind them to ARRAY_BUFFER and ELEMENT_ARARY_BUFFER:


    // Associate shaders with the buffer objects we just created
    // Bind our vertex and index buffers to their respective buffer types
    gl.bindBuffer(gl.ARRAY_BUFFER, vertexbuffer);
    gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexbuffer);

Assuming Shader.standardProgram has already been loaded, compiled and linked, we will now select it as the default shader program. This way the triangle being rendered will be using the vertex and fragment pair we created in the earlier chapters.


    // Use our standard shader program for rendering this triangle
    gl.useProgram( Shader.standardProgram );

Vertex shaders have a value a_Position (although, you can name it anything) which stands for attribute position. This maps the memory location of the vertex arrays we're passing from JavaScript into the shader program.

We're now ready to draw the bridge between our vertex arrays and the shader. This will help us to pass data to the shader dynamically. If it changes in our JavaScript program, it will be automatically updated in the shader. This is accomplished by code that follows:


    // Get attribute location
    var coords = gl.getAttribLocation(Shader.standardProgram, "a_Position");

    // Pointer to the currently bound VBO (vertex buffer object)
    gl.vertexAttribPointer(coords, 3, gl.FLOAT, false, 0, 0);

    // Enable it
    gl.enableVertexAttribArray(coords);

We simply created the coords variable and stored the attribute location in it. Then we passed it to vertexAttribPointer function to establish a link between the two in memory.

Finally we must enable this vertex attribute array by calling enableVertexAttribArray by passing it our coords variable containing the association.

Congratulations, we have just finished the preliminary setup for rendering a triangle on canvas. I know it was a bit of a handful, but this code is necessary to get started. It would be nice to create separate functions for each process that could be reused later and make the code look cleaner but I kept the examples simple in purpose.

And now we are going to enter the main rendering loop:


    // Start main drawing loop
    var T = setInterval(function() {

        if (!gl)
            return;

        // Create WebGL canvas
        gl.clearColor(0.0, 0.0, 0.0, 1.0);

        gl.clear(gl.COLOR_BUFFER_BIT);

        // Draw triangle
        gl.drawElements(gl.TRIANGLES, indices.length, gl.UNSIGNED_SHORT,0);

    });

Here we clear the background with black color and execute gl.drawElements function that ultimately renders the results on the screen.

This is where our indices array is used because drawElements needs us to specify the number of vertices that are stored in vertex array we bound to the vertex array buffer earlier. Luckily JavaScript objects of type Array have a native length property for that.


    } // End of window.webGLResourcesLoaded function.

The result of these operations is a white triangle on a black background:

Draw single triangle using VBO, VAO and a WebGL shader (vertex and fragment pair)

Changing values in vertex array is the key to manipulating its location. Usually, vertex data isn't typed by hand but is loaded from a file in some sort of an object format such as OBJ or PLY. This type of a 3D model can be generated by free software such as Blender and many other popular 3D editing packages.

OBJ models do not contain vertex color data. That's their only downfall. That's okay if you're taking a completely modern approach and will be using "global illumination" for your entire game world rendering where vertex color isn't very meaningful and everything is accomplished within shader logic.

However, chances are your first WebGL game is probably not going to be Mario Kart 8. Instead you probably want ease into gradually building your engine up increasing quality along the way. In this sense, you will probably have to experiment with light using vertex colors.

All this means is that I recommend using PLY format for that. PLY is amazingly great for storing all possibly needed data for your 3D world from vertices, texture coordinates, normal coordinates, and yes… the r,g,b color for each vertex. PLY format can accomplish so much more than OBJ, and surprisingly even the PLY format loader code is simpler to write.

Most beginners however start with OBJ format. Thankfully, in this book we will cover both PLY and OBJ model loaders. But it's up to you which one you want to use in your own projects.

WebGL Book: A WebGL Tutorial Reference Book
WebGL Book - A WebGL Tutorial Reference Book

If tutorials on this site are not enough, or you simply like reading from a physical book or a digital device (Kindle, iPad, tablets, etc.) check out WebGL Book. Written by the author of tutorials on this site.

This book is a WebGL tutorial and a reference that guides the reader through the process of setting up and initializing WebGL, drawing 3D primitives and creating 3D computer games.

Preorder Here
© 2017 Copyright WebGL Tutorials (webgltutorials.org)

All content and graphics on this website are the property of webgltutorials.org - please provide a back link when referencing on other sites.

Lyrics Haven: a song lyrics website, with clean printable lyrics react js tutorials react js elements react js components vue js tutorials angular js tutorials