WebGL Engine

As my Degree Final Project, I have decided to develop a WebGL Engine and study what are the differences between Forward and Deferred Rendering under the supervision of Javi Agenjo, Graphics professor and researcher of the Universitat Pompeu Fabra, here at Barcelona. I decided work on this project because WebGL could be useful in the future to render 2D or 3D in a Web, creating a new engine will refresh my knowledge about them and how they work, and the most important reason is learn to code in Javascript and how Deferred Rendering works using WebGL, because as a good engineer I love to learn new things on every project I work.

WebGL

WebGL pipeline

WebGL is a Javascript API used to render 2D and 3D graphics on the web with a browser compatible with the API without any plugin. WebGL is fully integrated with the browser standards, allowing the GPU accelerate the image and web effects processing. The WebGL elements can be integrated with other HTML elements and be part of the web page.

WebGL is based on OpenGL so its pipeline will be very familiar for someone who have worked before on graphics. It works on a HTML5 Canvas and has a completely integration with the Document Object Model (DOM) interface, and the memory management is done automatically thanks to Javascript.

Engine

The first steps that I had to do on this project were learn how Javascript and WebGL works. So I started as anyone would from the scratch, following a tutorial to learn the basis, and it was not very hard to find a good tutorial. Googling: “webgl tutorial” I found the web Learning WebGL, pretty straight forward. This tutorial taught me from how render an static white triangle on the canvas, to how render an scene on a texture and use it on an object of another scene. Once I had all the tutorials complete in a very messy structure without classes or objects, I decided to restart all of them but following the OOP paradigm and start to give form to my engine.

The GameObject

composition-1

Like the engine I made two years ago, I implemented the Composite Reuse Principle over inheritance, which allows me to create objects with different behaviors adding one or other component to instances of the same base class. I have called this base class as GameObject which contains the name of the object, a color to tint the whole object, a container of children objects, a container of components and a pointer to its parent. This few attributes allow me to identify the object, have a hierarchy and make the object do what I want it to do. Also the GameObject class have the method update that will call the update of every component.

The Components

composition-2

I have not created a base class for the rest of classes inherit from it because it would only contain the name of the component to identify it, and the method update, but this is not a problem because Javascript allows me to store instances of different classes on the same container. Since this engine is intended to study and render using different lighting techniques, I will not need a lot of components to compose the objects of the scene.

  • Transform: has the data needed by the object to define the position, rotation and scale of the object in the space that is contained.
  • Camera: with this component the object can be used to render the scene from its point of view, it has the field of view, aspect ratio, near and far plane of the Camera, and the view and projection matrices. Also has methods to orientate the transform to some point in the space (lookAt) and update the perspective.
  • Light: this component gives to the object the capability to light the scene. Stores all the data needed to define the 3 types of lights that use to be in a game: Directional Light, Point Light and Spot (or Focus) Light. Also it has the Ambient, Specular and Diffuse light components, since my Shading Model implements for each Light. And I thought could be useful a method to orientate the transform to some point in the space (lookAt).
  • ObjectRenderer: this component stores the Mesh and the Texture data that will represent the object on the screen.

The Scene

This class contains the group of objects that are in the world that we are rendering, we can have different instances to create different scenes to render. Apart from the GameObject pointer container I have a container for the Cameras and the Lights components of the scene, to have a fast access to them on the Renderer. Also it has an update method to update all the GameObjects.

The Renderer

The Renderer manage the Render Pipeline, it receive the GameObjects, Lights and the active Camera of the Scene, and the type of render to use, Forward or Deferred. For each type of render has a method that implements each technique:

  • Forward: iterates over every GameObject that has received, and for each object iterates over every Light received. Updates the data of the object, the light and the Camera to the shader, and renders to the canvas after calculating the matrices and vectors needed to do it.
  • Deferred: when the rendered is created initialize the GBuffer with the Albedo, Normals and Depth of the scene, so when this method is called, generates the GBuffer with the data taken from all the GameObjects received and the Camera (Geometry Pass), and once the GBuffer is generated, renders the frame calculating the illumination using the GBuffer and every Light data (Lightning Pass).

gbuffer

Forward Rendering vs Deferred Rendering

Once created the Engine, and some Scenes to do some tests I could see the differences between them and confirm what I have learnt during the study of the techniques.

Looking at the implementation of each technique we can say that the complexity of the Forward Rendering is O(N_geometry_fragments * L_lights), and for the Deferred Rendering is [lates]O(N_geometry_fragments + L_lights)[/latex]. Knowing that we could say that if the Scene has lots of objects but only one light, and vice-versa, both implementations will have the same performance. But at the moment we have more than one of both elements, the Deferred performance will be better than the Forward.
But in the other hand, the basic Deferred Shading has some limitations when it has to work with more than one material or transparencies, to implement this features we should use diferent techniques of the Deferred Rendering, meanwhile the Forward is able to do it.

The next canvas is an interactive implementation of my Engine