BWR2015: A sea in WebGL
I’ve collaborated with the Barcelona World Race event several times in the past. Last year they approached me because they wanted to have a new 3D renderer for the web to enhance their online game (which I coded 5 years ago). I forgot to create a propper entry in my blog so here it is. You can play with the demo.
Click in the image to see it in action, and if you want to know more about the development, read the rest of the entry.
The BWR
So for those of you who don’t know what the BWR is, it is a sailing event that take splace every four years where some boats try to circle the Earth just using the wind and without being able to stop at any port. It takes 3 months to do it, so it is an endurance challenge. So the BWR people wanted to have an online game where people could compete with the real boats in the same weather conditions, so we did and it was a success (50.000 suscribed users and 10.000 finished the race!).
During the development I had to focus on the 2D client (coded all in ActionScript) but they wanted a 3D visualizer (flash didnt had 3D capabilities back then in 2010) so a work mate did it using Google’s O3D, not a great technology but our only option back in the day.
Fast-forward 4 years and now they want to launch the game again for the 2015 edition, but the O3D is totally outdated and not supported any more. So it was my chance to do a proper 3D visualization in WebGL.
Tayloring an engine
To create the 3D game I decided to create a new engine from scratch. I already had one (LiteScene) working but I felt that one was too heavy and this project demanded something very lightweight that could be run even in tablets and old computers. So for that purpose I created Rendeer.js. The idea behind Rendeer is that it wont do anything that you do not ask it to do, that way I could optimize the rendering pipeline as much as I wanted.
Rendeer.js has grown from then till a point where is the default engine I use in all my Gamejam entries. Is very easy to use and helps me get things done quickly. Somehow is similar to Three.js but it forces you to code the shaders.
Sea water
One of the main challenges was going to be to render the sea. I read lots of papers related to the subject but the one that got my attention was the Real-time water rendering – Introducing the projected grid concept by Claes Johanson. It is a great concept, relatively easy to implement, and that fit well in the WebGL (not too much CPU stuff).
The idea is that you create a screen-space grid of triangles and in the vertex shader you reproject those to the sea level, and then you can perturbate them using mathematical functions, etc. The main problem is that they produce a horrible aliasing in the distance that I had to solve doing lots of tweaking of the fragment shader.
After the sea was working, I spend one month doing a beautiful water shader. I ended up mixing animated normalmaps with strange factors to control every possible shading property because it was hard to achieve the results I wanted just by playing with the shader.
It is nice to shade water, most of the properties are easy to compute from the shader so you have plenty of room to tweak the reflections and the colors based on the height of the wave, or the angle of the view vector. I know I could had done better was I was pleased with the results and there were still lots of things to do.
Sky
Another big challenge was to have a believable day-night cycle. For that I started using the code from codeflow and kept tweaking it.
The main problem was that it always looked too dark or too shiny, probably because it was working in linear space and I tend to use the regular gamma space.
Because the performance was very bad I decided to precache it inside a Cubemap, this way I only had to compute it when the sun moved and I could use it for the reflections. The problem was that during the sailing event Chrome rolled an updated that for some reason broke the render-to-cubemap so I had to improvise, gladly it was fixed soon.
Weather
The game also requested to have different weather conditions. Some of them could be infered from the sea movement or the sky color, but others requested to add more elements to the scene, like fog, rain and clouds.
The fog was easily resolved in the shaders using classical approaches. The rain took me a while, instead of using particles I decided to use a mesh with a texture animated from the shader, so it was very lightweight for the CPU.
The clouds… that was kind of hard. I tried different approaches, like billboards, skybox with normalmaps, even raymarching. But they all look bad or took too much rendering time.
Then I decided to go for the simplest approach, a plane with perlin noise, and I was surprise how well it looked, and it was easy to tweak, so I decided to leave it that way and do not waste more time. Although the billboards solution was beautiful it never ended up in the final game.
Adding behaviour
The boring part was to connect everything with the REST API that told me all about the boat (long/lat coordinates, weather in that position, kind of boat, etc). I had to map a tiny number of factors to the big number of properties that my system had, and while doing it I had to be cautious otherwise it could happend that in some situations the scene would look not-realistic (impossible waves, shiny sea, etc).
And also there where many beautiful situations that would never happend because they werent mapped. This part I could had put more effort but I already was almost out of time so I ended up having something that worked.
Other issue was the movement of the boat, it should look realistic but I didnt want to create a physics model to control the bouyancy. So what I did was to extract the normal and the height of the sea in several position along the boat surface and interpolate between them. Adding some smoothing the results where good but in some situations the boat could navigate below sea level, so I had to add more tweaking parameters.
Final thoughts
It was a pity I didnt have more time, but I was happy with the result. The game ended up being a success again, the 3D even worked in mobile devices with a very decent frame-rate. The sad part is that the 3D didnt get too much attention by the users, it was relegated to some kind of gimmick due to the 2D nature of the game, but it is fine, I did my part and in just three months. And it helped me to learn a lot about camera projection, raymarching, shader optimizaction for mobile devices.