LiquidLight Android Live Wallpaper

LiquidLight Android Live Wallpaper

Update: LiquidLight is now available for download on Google Play. Give it a try and let me know what you think.

Description

LiquidLight is a Live Wallpaper for Android that creates a dynamically changing water surface on the home screen of your phone or tablet using OpenGLES 2.0. The water surface features visually interesting lighting effects, such as reflection and refraction, and the application settings allow the appearance of the water to be modified to resemble a placid lake, a stormy ocean, or anything inbetween.

Several pre-built themes will be shipped with the wallpaper to add even more options for changing its appearance. These themes will change textures and lighting conditions as well as provide custom shaders to add even more interesting effects. There is scope to do other things with themes too: such as time of day based lighting or weather effects like rainfall or moving clouds. If the app is popular enough I may implement some of these ideas in the future.

LiquidLight live wallpaper settings

Various built in themes are provided

Video

Check out the demo video to see it in action – the images don’t really convey the effect very well.

wavesfeatured

WebGL Waves

Description

WebGL Waves was developed as an experiment with Emscripten, which compiles C++ into Javascript for use in browser applications. I was interested in seeing what kind of performance I could get out of a browser based 3D application so I chose to implement Tessendorf’s FFT ocean simulation[1], which I’ve already used in my Python based WaterCaustics project.

The demo currently uses a 64×64 mesh grid to represent an ocean surface tile and every surface update requires the computation of 5 64×64 point 2DFFTs (heights, x/z displacements & normals). Performance wise, an Intel i5-2500K based machine with a Radeon HD6950 achieves 53FPS in Chrome and a Samsung Galaxy S3 running experimental WebGL in Chrome gets around 10FPS, which isn’t really fast enough to be considered interactive but I’m still impressed that it worked without any additional effort on my part. Performance could be improved considerably by moving parts of the ocean simulation (notably the FFT) onto the GPU.

The Demo

Access the demo here.

The sections on the right hand side of the page let you control the ocean surface parameters and adjust the inputs to the surface shader. Enable or disable the mouse based camera by pressing ‘M’ and move around the scene using the WSAD keys.

Conclusion

I’m quite impressed by Emscripten, compiling my code to Javascript was a relatively painless experience and now it will work on any platform with a WebGL capable browser without the user having to install anything. This will be a great way to distribute games or other 3D rich applications in the future, and you can reuse your existing code base without having to rewrite it all in Javascript.

[1] Tessendorf, Jerry. “Simulating ocean water.” Simulating Nature: Realistic and Interactive Techniques. SIGGRAPH (2001).

bloxfeatured

OpenGL Minecraft Style Volume Rendering

To help reinforce my knowledge of OpenGL and C++ I’ve been working on a project to build a rendering engine which can then be used as the basis for a game or other type of 3D application. I opted to implement a rendering engine that renders volumes in a ‘blocky’ style similar to that of the game Minecraft.

Minecraft is a hugely popular indie game set in a world composed of destructible blocks. Players can add or remove blocks at will or explore a vast, randomly generated world of rolling hills and subterranean networks. I decided that writing an engine that performed rendering like Minecraft would be a good exercise as there is plenty of scope for learning and it is simple enough that I’d be able to get something that looked semi-decent running quickly.

If you’re looking for a complete implementation of this kind of engine then you should check out PolyVox.

I frequently refer to parts of my code which are a bit too large to paste here. The code can be found in my GitHub repository here.

Tools

I’m using a mix of Python and C++ on this project, but I might move everything into C++ at a later stage.

Pyglet and gletools are used in conjunction with Python for rapid prototyping of new ideas and for providing a window manager and mouse/keyboard input. Much of the functionality is written in C++, which uses the OpenGL mathematics library and GLEW as a dependency.

Volume Storage

In Minecraft the world environment is stored as a Voxel volume which is then rendered as polygons by using some method of extracting a polygonal mesh from the volume data. Blocks can be added or removed from the environment by making modifications to the volume data and then re-generating the polygonal mesh; it has be to be able to do this at interactive speeds without any visible pauses, so making this process efficient is important. Minecraft subdivides a large volume (such as a game world) into smaller volumes referred to ‘chunks’ in order to speed up the process of regenerating the polygon mesh after terrain modifications or loading new terrain sections, I will use a similar approach to this.

Volume arrangement

A singe block represents a value on a regular grid in three dimensional space, so an intuitive way of storing this data would be to use a three dimensional array where the array index represents the position in space and the value of the element represents some property of the block at that position (for example: material type). Initially I used a flattened 3D array of bytes to store chunk data, the value of a block at a given point in the chunk could by found by:

value = volume_array[size_x + (y * size_x) + (z * size_x * size_y)]

Unfortunately the memory used by this approach quickly turned out to be unmanageable for a volume of any significant size, so I turned to using a sparse data structure like a boost::unordered_map. With this approach, the block x,y,z co-ordinates are packed into a boost::tuple and used as the key to the map and the block properties are stored as a byte. Only ‘solid’ blocks are stored in the map, so if a particular key cannot be found in the map the block at that co-ordinate is assumed to be empty, or ‘air’, and will not be drawn. Using a map allows quick access and modification of an arbitrary element, which is one of the main requirements of an engine like this, and less memory is used as ’empty’ blocks are no longer stored. Fast block access is not only important for changing the volume data, but for rendering those changes too.

Generating a Polygon Mesh

A single cube can be drawn by creating a list of vertices representing each of its six sides and then passing these to the graphics card. Each side of a cube requires two triangles (using 6 vertices, two of which are duplicates) or a single quad (using 4 vertices).

Cube vertices

I initially used quads for rendering as it sounded the most efficient (less vertices must be better, right?), but I’ve since found out that not all hardware supports quads and, curiously, quads are less efficient than triangles even on the hardware that does support them, so I switched to using triangles.

To build a polygonal mesh representing an entire volume we have to iterate across the std::map containing the volume data and for every entry store the list of vertices required to draw a cube at that location in space. The number of vertices generated by this process can be reduced by checking the neighbours of each volume element to see if there are any solid neighbouring blocks that would obscure the one we are currently generating vertices for. For any block where there is a solid neighbour there is no need to draw the faces between both blocks as we will never see them anyway. The following code snippet from chunk.cpp illustrates this process:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
// Loop through every voxel in the volume and create the vertices required
// to render exposed faces. Faces hidden by surrounding cubes are not drawn.
for(boost::unordered_map<Position, byte>::iterator ii=chunk.data.begin(); 
    ii!=chunk.data.end();
    ++ii)
{        
   // Get the voxel chunk coords.
   x = (*ii).first.tuple.get<0>();
   y = (*ii).first.tuple.get<1>();
   z = (*ii).first.tuple.get<2>();
   // Find out if there are surrounding empty neighbours for each axis.
   if (x > 0)
   {
      if(not data.is_solid(x-1, y, z))
      {
         // The neighbour to our left is empty, generate face.
         // Setpos simply loads the appropriate vertices into a vertex array.
         setPos(x, y, z, LEFT); 
      }
   }
   else
   {
      setPos(x, y, z, LEFT);
   }
   if (x < (size - 1))
   {
      if(not data.is_solid(x+1, y, z))
      {
         // The neighbour to our right is empty, generate face.
         setPos(x, y, z, RIGHT);
      }
   }
   else
   {
      setPos(x, y, z, RIGHT);
   }  
   if (y > 0)
   {
      if(not data.is_solid(x, y-1, z))
      {
         // The neighbour below us is empty, generate face.
         setPos(x, y, z, BELOW);
      }
   }
      ... and so on for the remaining directions ...

To store the vertex positions generated by the above process, I initially used a single array of vertices which was then uploaded to the graphics card as a vertex buffer object (VBO). This worked fine up until the point where I tried to add optimisations to my rendering like back face culling.

Back face culling can be used to further reduce the number of redundant faces sent to the graphics card, it involves determining which faces in each chunk are visible from the current viewpoint and sending only those to the GPU. To be able to do this there has to be some method of filtering the vertex positions based on which face direction they belong to. One way of doing this is to group vertex positions into six arrays, one for each possible face direction. Any time the viewpoint moves, the position of each chunk relative to the viewpoint position should be checked to determine which face directions are visible. For example: if the viewpoint is immediately to the left of a particular chunk then the faces pointing to the right in that chunk do not need to be rendered and so the array holding right-facing vertices does not need to be passed to the GPU; this process can be repeated for the other two axis as well. Only the arrays holding faces that we know are visible are sent to the graphics card, across many chunks this can be a considerable saving.

Back face culling (x-axis example)

Face Normals

Face normals are useful as they will allow us to apply lighting effects to the blocks later, for now a very simple lighting model will be used which attenuates the colour of each block face depending on which direction it is pointing. Because we are using arrays to group vertex positions based on which side of a cube they represent, we can avoid having to create an array of vertex normals to accompany the array of vertex positions, and instead tell the graphics card the normal of the vertex positions it is currently receiving. It is possible to compute the face normals directly in the pixel shader and avoid having to send them to the GPU at all, but for now I will stick to simply calculating them on the CPU. Here is a section of the code from chunk.cpp that sets the vertex normals:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/* VisibleFaceGroups is part of the back-face culling code described above */
if(visibleFaceGroups.z == DRAW_FRONT or visibleFaceGroups.z == DRAW_BOTH_Z)
{
   /* Set the GLSL Normal uniform */
   glUniform3fv(normAttrib, 1, glm::value_ptr(glm::vec3(0, 0, -1)));
   /* Upload the forward-pointing faces to the GPU */
   glBindBuffer(GL_ARRAY_BUFFER, verticesFrontVBO);
   glVertexAttribPointer(posAttrib, 4, GL_UNSIGNED_BYTE, GL_FALSE, 0, 0);
   glDrawArrays(GL_TRIANGLES, 0, verticesFront.size());
}
if(visibleFaceGroups.z == DRAW_BACK or visibleFaceGroups.z == DRAW_BOTH_Z)
{
   /* Set the GLSL Normal uniform */
   glUniform3fv(normAttrib, 1, glm::value_ptr(glm::vec3(0, 0, 1)));
   /* Upload the back-pointing faces to the GPU */
   glBindBuffer(GL_ARRAY_BUFFER, verticesBackVBO);
   glVertexAttribPointer(posAttrib, 4, GL_UNSIGNED_BYTE, GL_FALSE, 0, 0);
   glDrawArrays(GL_TRIANGLES, 0, verticesBack.size());
}
 
/* ...continue for each axis... */

The image below illustrates a basic lighting model on a scene:

No normals (left) Normals for basic lighting (right).
Face Normals

Heightmap to Block Terrain

To test out my engine I initially used a set of procedures which loaded each chunk with a primitive shape like a sphere or pyramid. I got bored of this pretty fast, so to make things more interesting I created a procedure to load a heightmap image into the world. If I set the size of the world chunks to 64x64x64 blocks and use a 256×256 heightmap and the intensity of the pixels in the heightmap ranges from 0 to 255, I need 4x4x4 chunks, or 256^3 blocks to represent the heightmap in three dimensional space. In addition to this, I also wrote a fragment shader in GLSL which colours each block in the volume based on its vertical position using the common jet colour mapping:

1
2
3
4
5
//VALUE in this case is the y position of the block and ranges from 0 to 255
float k = 4*(VALUE/float(255));
float red = clamp(min(k - 1.5, -k + 4.5),0.0,1.0);
float green = clamp(min(k - 0.5, -k + 3.5),0.0,1.0);
float blue  = clamp(min(k + 0.5, -k + 2.5),0.0,1.0);

Here’s a few images of where the engine is now:

Pyramids (Each one is stored in a chunk)

Pyramids

Mountains

Mountains

Volcano

Volcano

Hills

Volcano

uart2

RS232 UART (VHDL)

Description

I designed the UART core to allow me send and receive data from a Spartan 6 LX9 Microboard which has an on board Silicon Labs Cp2102 USB-UART Bridge.

The UART Core is a simple RS232 communications controller which can be used to provide a quick and easy means of communicating with your FPGA board. The baud rate used by the controller is easily set using the generic map, although I have only tested it so far with a rate of 115200 baud using a 100MHz system clock. The communications scheme used is 8 data bits, no parity and 1 stop bit (8N1), so make sure your terminal software is using these settings if you have any problems.

Internally, the UART provides a simple interface incorporating a data strobe and acknowledge for sending and receiving data. To send data from the FPGA, the data must be presented to the DATA_STREAM_IN port and the DATA_STREAM_IN_STB (Strobe) asserted; when the DATA_STREAM_IN_ACK (Acknowledge) output is asserted by the UART the data has been sent. The strobe should be deasserted 1 clock cycle after the acknowledge is seen to avoid an additional byte of data getting sent.

Handshaking Scheme

Specifications

  • RS232 8 data bits, 1 stop bit, no parity data stream format
  • Customisable baud rate
  • Simple internal interface and handshaking scheme

Download

You can grab the source code for the UART through my GitHub repository here.