I remember when screen-shots and trailers for Hustle Kings started circulating back in the summer of 2009, many people were speculating that the pool balls were ray-traced. Of course, the astute out there will have quickly realised this not to be the case, but it got me thinking as to how a bit of thought can make something relatively simple look really ace.

The first thing to point out about the balls in Hustle Kings is that they are round. Very round. They are not highly tessellated meshes closely approximating a sphere, but merely screen-aligned quads, sprites if you like. All the work for making them look like they do is done in the pixel shader. So, although I’m not using ray-tracing per-se, I’m doing a ray-cast per-pixel to determine whether or not each ray intersects the ball. This is simple maths, equation of a line, equation of a sphere, put the two together and work out the points of intersection, if any. There is either none, or two – if the ray enters the sphere, it will eventually leave it on the other side – this info is useful for the glass ball sets where you can see straight through the ball.

Given the points of intersection, it’s dead easy to work out normals (well, they always point away from the centre of the sphere), and with this, we can do lighting and reflections in the traditional way. For reflections, we simply have a cube map per ball, which only get updated when what’s in them (ie, relative positions of nearby balls) is changing. Generating a mip-map chain for each of these cube maps is of paramount importance here, just having a simple mip makes side-on reflections look really ugly and this completely breaks the illusion. Texturing is slightly more complicated. We either use a cube map (the simple case), or we have to do a bit of inverse trig in the shader to work out a theta and phi, then map this into UV space.

It’s worth talking about the size of these screen-aligned quads here, as that is not as trivial as it may first seem. Sure, the radius of each ball is known, but due to perspective, it’s not correct simply to draw a quad of size 2R square. We need to do a bit of maths in the vertex shader to work out the maximal extents in screen space of the sphere. For this, I take the ray-sphere intersection equation, and determine the conditions that must be met for this to have any solutions – this then gives me the required values.

This is almost everything, but probably the most important aspect of the ball rendering in Hustle Kings is the edge anti-aliasing. Notice how the edges of the balls look perfectly smooth? At resolutions of 720p or less, we employ 4x MSAA for everything in the scene, except for the balls, as the sphere edges are worked out per pixel rather than per sample. MSAA wouldn’t look as good for the balls anyway, it’s good and all, but still has it’s limitations. No, instead we work out a pixel coverage value for the pixels around the edges of the balls, again using some simple maths. If the ball completely covers a pixel we get a value of 1, if it covers none of the pixel we get 0, and if it partially covers a pixel then we get something in between. Perfect, just spit this out as our alpha value and we’re done!

Well, this does mean we have to render our balls back to front to get correct blending, and being an expensive shader, we lose out on early z-cull. So to alleviate this we render the balls in two passes – firstly the opaque centres front to back, then the alpha edges in reverse order.

That’s pretty much it. Lighting and shadowing on the balls are another story altogether, so I’ll save that for another blog post.

Grant says

If I wanted to make a billiards game on a PC, what advantages does this technique provide compared to using a high-resolution mesh for the ball, assuming memory consumption of the mesh is not an issue?

Mark says

There are a couple of advantages to this approach. One is the ability to nicely smooth the edges of the balls without having to use MSAA (and like I say, the result looks better than MSAA anyway) – this would be difficult to acheive with mesh balls. Another useful feature is the ability to trace the rays right through the sphere so we can do refraction on glass balls, although with a bit of extra shader code, it would be possible to do this with mesh balls. It’s true that the pixel shader takes a few more cycles to intersect each ray with the sphere, but with only four vertices, the vertex shader is doing pretty much nothing, and so with the load balancing architecture of modern GPU’s, this is not really an issue until you zoom right into a ball (which is where you really notice how infinitely smooth the balls are). You also get automatic perfect lodding – we’re never shading any more vertices/pixels than we need to. You can lod mesh balls of course, but there are always going to be steps between lods.