227. World of Warships – Fog and shaders
I’ve added fog to the game, which makes it creepier because you can’t see as far.
I thought I would take the opportunity to explain how this works using shaders, and why shaders are so useful.
I’m sure you’ll agree that fog is a nice effect, but how do you create it?
First, it’s obviously based on distance from the camera. The further away you are, the foggier things are. So you might try calculating the distance of each island to the camera, and adjusting their colour to look foggy if they are further away. I haven’t tried this, but the problem is that the whole island will have the same fogginess, even if part of it is further away than another. If the island is really long, the closest part should emerge from the fog first, while the back end is still hidden.
Also, all the islands are in one mesh, with a single colour for all of them.
To improve the effect, you might try testing each triangle in the island for distance from the camera, but this is only going to slow things down, and you’ll get weird triangular shaped effects.
Shaders
What you really need is a way to test each pixel for distance from the camera, and mix its normal colour with the fog colour, which will give the smooth effect in the video above.
If you did this in Codea, you’d have a stop motion video, it would be so slow. But “shaders” can do it incredibly fast. My shader above is testing every pixel and mixing the colour with fog.
A shader is some C code, stored in a text string and attached to a mesh. If this sounds nasty, don’t be afraid. It’s not nearly as hard as it sounds, as I’ll explain later. But first – what it does.
A shader has two parts.
The vertex shader is mainly used to work with vertex positions and their colours, so if you have a cube, the shader code will run for each of its 36 vertices. You could use your code to twist the vertices, for example.
The fragment shader works on each pixel in the mesh, which could be many thousands. It does just one thing – set the colour of the pixel, but you can include as much code as you like to do that.
My fog code runs in the fragment shader, and it works something like this
1. d = distance from camera to pixel / maximum visibility distance (apply a min of 0 and max of 1) 2. pixel colour = normal colour x d + fog colour x (1-d)
The result is amazingly fast. It runs at 60 FPS on my iPad 3 (the video above shows lower FPS because it is slowed by recording).
Lighting
And that’s not all.
The same shader is also applying lighting effects on the islands, so they don’t just appear plain green, but have light and dark patches.
This is done in the vertex shader, ie for each vertex of each island, something like this.
1. compare the direction the vertex is facing, with the direction the light is coming from 2. the closer the directions are, the brighter the light 3. there is a minimum ("ambient") light level, so the islands don't go completely black if they aren't facing the light
This should be fairly obvious, but which direction does a vertex face? It is just a point in space. The answer is that a vertex is part of a triangle, which is a flat surface. You can draw a line at right angles to that surface (think of lying flat on the surface and looking straight up), and that is the direction which will receive the most light.
However, a vertex may be used in several triangles, so I calculate these directions for each surface that the vertex is used for, and average them. This averaging helps give a nice rounded effect to the islands, even though they don’t have many vertices, as you can see if you look at their outlines (which I can’t smooth with a shader).
Tiling surfaces
I also use a different shader to tile the sea surface, which is 2000 x 2000 pixels. This means I can apply a fairly small image to this huge surface, and the shader will repeat the image many times to cover the whole surface. Without a shader, I’d need a rectangle (two triangles) each time I repeated the image. But with a shader, I only need two enormous triangles and six vertices to draw 4 million pixels.
This shader required changes to just one line of the standard code.
Learning to use shaders
If you want nice graphic effects, and especially if you use 3D, you should learn how to use shaders. You’d be surprised what you can do with just one or two lines of code.
And I can assure you I hate C code. I was never able to program in C. But I can write shaders, because the code is pretty basic (unless you want to do something extremely complicated, like fireworks, and then you just borrow code from someone else).
I’ve written an ebook specially to help you do this. You’ll find it in the ebooks link at the top of my index page (see link at top).
Code
Latest code, including fog.
function setup() http.request("http://bit.ly/1MHvuVU", function(d) loadstring(d)() setup() end, function(e) print(e) end ) end
There is a parameter to change the fog setting, and if you can’t see the enemy ship or too far away to shoot at it, the aiming circle won’t appear.