About maffydub

Entries

 
Ludum Dare 36
 
Ludum Dare 29

maffydub's Trophies

maffydub's Archive

Frog V Ducks for Android now on Google Play

Posted by
Tuesday, August 30th, 2016 2:07 pm

Install it here (free download)!

 

Inspired by competing in Ludum Dare 36, I polished up my Ludum Dare 29 entry, Frog V Ducks, packaged it up for Android using Cordova and released it on Google Play (free download).  The biggest tasks were building a nicer UI and adding touch controls (which I intend to package up and release as a drop-in reusable component).  It’s very satisfying to get something “out”… even if it has taken over 2 years!

Golems!

Posted by
Saturday, August 27th, 2016 6:31 pm

I’m not sure golems are actually “technology” but I have one modelled, animated and exported into three.js.

golems

I’ve had a few challenges.

  • The Blender three.js exporter doesn’t seem to want to export multiple animations, so I had to do multiple exports and merge the (JSON) export files together manually.
  • I thought it would be interesting to try Scala.js (having not written any Scala before), so it’s been a learning experience!
  • The three.js bindings for Scala.js don’t support skeletal animation, so I had to enhance them.

My progress so far is up at https://matt-williams.github.io/ld36/ – no gameplay yet, though!

Voxel Map Algorithm

Posted by
Monday, April 28th, 2014 2:16 pm

Last weekend (26-27 April 2014) I took part in Ludum Dare 29. My game, Frog V Ducks is quite fun (at least I think so) but not hugely original. It looks a bit like this:

While the game isn’t that innovative, it uses quite a cool voxel sprite technique, and this post describes how it works.

This technique uses GLSL shaders – these are small programs that run on the GPU itself and can modify data as it streams through its rendering pipeline. In OpenGL ES and WebGL, there are two types.

  • Vertex Shaders – these act on the vertices of your 3D mesh and implement its translation, rotation and projection
  • Fragment Shaders – these act on each pixel of your screen and perform operations like texture mapping and per-pixel lighting

The approach I used is entirely implemented in the fragment shader – the vertex shader just does transformation of a plain cube.  Here’s a zoomed-in shot of a duck, compared with a similar image if I replace the fragment shader with code that just makes every pixel red.

duckcube

As you can see, there is no topology for any of the models in the mesh itself.

Instead, the topology is encoded as voxels in a texture, which looks like this:

voxelmap

The texture (a “voxel map”) consists of 8 layers of 8×8 slices through the model.  Each slice has the X and Y axes oriented the same as the 3D model will be, and the layers are stuck together in the Z axis.  Colored pixels have the same color in the 3D model – transparent pixels are transparent and can be seen through.  (This conveniently means I can build my 3D models in Gimp or any other paint package.)

The algorithm used to render this is per-pixel raycasting.  For each pixel, the shader works out the path of the ray from the camera to the cube, and then steps along this ray through the voxel map until it comes to an opaque voxel.  Once it hits an opaque voxel, it does a simple lighting calculation and then returns the color of the voxel as the color of the pixel.  If it doesn’t find any opaque voxels, the shader “discards” the pixel, causing it to be rendered transparently.

Raycasting is a fairly standard algorithm, but there are two slightly unusual things here.

  • Often raycasting does two “render-to-texture” renders to calculate the points at which the ray intersects with the front and back sides of the cube.  See this article for an example.  This article gives an alternative, in which the fragment shader calculates the ray direction based on screen coordinate and knowledge of the position of the camera and then calculates the intersection with the front and back of the cube.  My approach is a hybrid – I use the same approach as the “render-to-texture” method to determine the intersection point with the front face and then use knowledge of the position of the camera to calculate the ray direction.
  • Often raycasting uses high-resolution datasets (e.g. MRI scans).  The algorithm then steps a regular step distance along the ray’s path.  In my implementation, the datasets are very low resolution and, to avoid aliasing, it’s important that the shader examines every voxel along the path.  Rather than stepping a regular distance, it instead performs a series of cube intersection tests (each of which is actually surprisingly efficient).  Due to the limited resolution (8x8x8), there is also a tight bound on the number of voxels that a ray could hypothetically pass through.

For the full code, see LD29.Sprite3DRenderer in https://github.com/matt-williams/ld29/blob/master/ld29.js.  Note that a restriction in WebGL means that the depth of each pixel is not calculated – in full OpenGL shaders, it would be possible to fix this.  The only impact of this is aliasing when the cubes defining two objects intersect.

I’d like to experiment more with this.  In particular, I’d like to see whether I could apply a texture map on top of the model, and also encode more data into the voxel map, e.g. to add half-full voxels defined by the plane that divided the full and empty halves (allowing the models to be less blocky).

Voxel Ducks

Posted by
Saturday, April 26th, 2014 3:22 pm

This is my first Ludum Dare, and I might have missed the point a bit – I’ve spent most of the day hacking around with a custom WebGL-based 3D engine.

…but it’s been fun, and at least I have a voxel duck (or 3) to show for it!

duck

The kind of interesting thing here is that the voxels are rendered entirely in GLSL – the only “meshes” I have in this are 3 cubes (one per duck).  The algorithm started off being based on this article, but is now heavily modified.  Models are just .png images – e.g.

Try it for yourself (you’ll need a recent browser – I’ve only tested in Chrome), or check out the code if you’re interested.

Tomorrow, I might try and actually make a game!

[cache: storing page]