Lighter, Faster, Prettier #webgl #3d #performances #threejs
We just released our new chess games for which we've worked a lot on rendering optimization. The previous version of the game was ok but too heavy in terms of device resources.
We would like to share our progress on this issue made in the last few months.
Our 3D games use the brilliant Threejs library for webgl.
To create 3D pieces, we follow this main process:
design and create objects in blender
import piece in game using THREE.JSONLoader
modify geometry and materials to suit our needs
The contraint we have is to minimize the size of the JS files, and the "weight" of the objects in the 3D scene.
Low resolution meshes lead to ugly objects. It depends on the shapes needed of course, if we have very geometric pieces like cubes, it's not a problem. But if we consider complex geometries like chess pieces, we need to have good looking curves and shapes.
The old way
We decided to design low poly meshes to get small files, always keeping in mind we would smooth them once loaded in the scene. Let's use the knight example to explain.
We do not apply the smooth modifier in blender, that would produce a much too big file, but it's just to have an idea of what it will look like in the game after Threejs smooth modifier is applied to loaded geometry. (blender and threejs smooth modifiers are not the same but will look similar)
Not bad we thought. The problem is that these repeated smoothing modifications make the polygone counters go too high (about 300 000 faces in this version), the game becomes "heavy" if we choose a 2 subdivisions smoothing, and not pretty enough if we only make a 1 level smoothing. We had to find new solutions.
The new way
We want low poly meshes but higly detailed rendering. There is a miracle solution: normal maps. Interested in going deeper? Read this.
The idea is to keep low poly structure, i.e. small number of faces, but we will simulate details of volume by artificial normal information: a map will tell the graphic engine how to simulate the normal for each point. This is some kind of volumes texture. The orientation of the normal for every point is given by blueish pseudo colors information and looks like this:
But how can we get these details information? We will take them from real volumes: the high level mesh. So go back to blender and enjoy modeling your object with all the finest details you want. Once your beautifull piece is finished, we will make a second piece, a low poly version which will be as close as possible to the main bounding shapes of the high res one. We call this retopolgy: here is a tutorial for blender
It was great fun to redesign a typical chess game inspired by "staunton" chess set. I will not insist on this part, modeling + texturing, classical stuff in 3D.
Next step: retopologize the pieces to low polygone.
Once all this is done, we need to compute our normal maps. For this, we have to unwrap the UVs which determine how textures are applied on the 3d object surfaces. An expert would have optimized this much better than me :)
Now we have our final object and its UVs, we can compute its diffuse and normal maps. There is a process for that which is called baking textures. Carefully set up lights and materials for the high res object. It is the result of its full rendering that we will steal to project on the low poly mesh (for more details, here is a texture baking tutorial).
The enlighted diffuse map and the normal light reflection information will give your low poly mesh a highly detailed great look and the result is pretty amazing.
We need to create the high poly piece with all details and textures
This is done with high subdivision level
which means tons of polygones
Then we retoplogize by creating a new object, a low poly optimized mesh (the final object we need) very close to the high poly.
Unwrap UVs and bake textures. First start with the diffuse chanel:
then the normals chanel
the topology of the object (vertices, faces)
the UVs (texture mapping)
Then load diffuse and normal maps (images), and finally create materials and mesh to add to the scene.
To give you an idea, the code looks like this
As a result, the total scene faces counter has been divided by 10! And we believe the result is even prettier that the first version because we were able to work deeper in 1st step modeling and texturing.
We hope you agree with us :)
You can see the result live: play chess now!