Conway's Game Of Life in GLSL
Overview:
I was inspired by this video to implement a per-pixel Conway's Game Of Life in GLSL. The video demonstrates at around 1 minute and 55 seconds that you can obtain a neighbor count for each cell by summing each of all possible "one rotations" of the matrix of cells. I noticed that each "one rotation" in every possible direction is in fact simply a translation by one cell in each possible direction.
I was inspired by this video to implement a per-pixel Conway's Game Of Life in GLSL. The video demonstrates at around 1 minute and 55 seconds that you can obtain a neighbor count for each cell by summing each of all possible "one rotations" of the matrix of cells. I noticed that each "one rotation" in every possible direction is in fact simply a translation by one cell in each possible direction.
After noticing this, I realized that I could easily count neighbors in a pixel shader by using texture coordinates.
My design for achieving this uses an OpenGL frame buffer and by rendering to two textures. The textures are used to process each new generation of the
simulation. For each render step the textures are swapped so that the next generation can be processed on the next render step. I will name these
textures 'sourceTexture' and 'destinationTexture.' 'sourceTexture' will contain the previous generation and the next generation will be placed in 'destinationTexture.' their handles will be swapped at the end of the frame and the process repeats.
The render step consists of four phases:
Note: C++ snippets will be using GLM for vector math as well as a simple shader API that I built as an undergraduate. The shader API is self-explanatory and will be used to 'Execute' and 'Terminate' as well as for setting uniforms.
Lets take a look at the shader for simulating CGoL.
Vertex shader (GLSL):
My design for achieving this uses an OpenGL frame buffer and by rendering to two textures. The textures are used to process each new generation of the
simulation. For each render step the textures are swapped so that the next generation can be processed on the next render step. I will name these
textures 'sourceTexture' and 'destinationTexture.' 'sourceTexture' will contain the previous generation and the next generation will be placed in 'destinationTexture.' their handles will be swapped at the end of the frame and the process repeats.
The render step consists of four phases:
- Process the Conway's Game of Life simulation
- Draw some simple geometry
- Final Pass (render to screen)
- Swap textures for next frame
Note: C++ snippets will be using GLM for vector math as well as a simple shader API that I built as an undergraduate. The shader API is self-explanatory and will be used to 'Execute' and 'Terminate' as well as for setting uniforms.
Lets take a look at the shader for simulating CGoL.
Vertex shader (GLSL):
We pass into the vertex shader the texture coordinates for looking into sourceTexture. My original plan for this was to use 9 attribute arrays (pre-calculated during init) and depend on the shader pipeline to interpolate the coordinates for each. However, after a bit of research I discovered using 'textureOffset' would be a much simpler method.
Fragment shader (GLSL):
Fragment shader (GLSL):
Note that I chose to only check the red component of each texel. This is because of the internal format being used for the source and destination textures, which you can see below. I convert each red component to an integer so that I can sum them, and perform some simple integer comparisons to carry out the rules of
the simulation.
Here are some snippets from my code which shows the initialization and draw phases of the simulation.
Init & Draw (C++):
the simulation.
Here are some snippets from my code which shows the initialization and draw phases of the simulation.
- frameBuffer: The frame buffer handle
- sourceTexture: The source texture handle
- destinationTexture: The destination texture handle
- colorAttachment: The enum value for the frame buffer color attachment
- projection: A matrix stack which is used for the projection transformation matrix.
Init & Draw (C++):
Final Thoughts:
I continued to expand on this by designing some framework for rendering simple 2d geometry and applying logic to those render objects. I did this for the purpose of turning this into a screen saver for my home computer. I created some state machines for some critters to roam around the screen and seed the simulation. My home machine runs this screen saver at 3360x1080 with no performance issues. Thank you for reading. If you have any questions, comments, or critique, feel free to send me an e-mail at [email protected]. |