Blue Pill, Red Pill
On the 30th anniversary of the Commodore Amiga, I watched some videos of its
legendary demo scene. I wanted to get back to graphics programming, but
rather than work on a full blown game, it should be pure and simple, a short
side project. So why not do a demo?
In last months post I used a screenshot of a (Windows) Matrix screensaver. Although it looks nice, it isn’t quite the same as the original rain code you see in the movie. I wanted to give it a shot myself.
Back in 1999 the rain code in the original Matrix movie was one of the coolest
things anyone had ever seen on a computer screen. It’s shown only a couple of
seconds in the intro of the movie, and later you see the operator (“Tank”)
behind a number of screens that run the code.
In the second movie, the code looks different, and the story says it has now been encrypted. I like to believe that in reality, the original code was lost due to a disk crash and was reimplemented by someone else, and made it look more fancy.
Anyway, if you look closely at the intro, you might notice that the code is actually Japanese katakana, which has been mirrored. Also, the characters are small and look stretched because the display is a CRT tube with standard 80x25 text mode. I bet the Matrix is using an old Japanese PC clone, one that looks a lot like a green terminal.
A modern computer doesn’t look anything like it simply because the displays of today are so different. Moreover, my Mac doesn’t even have a text mode, it runs with graphics all the way. But we can emulate it. A little.
I started out with a very straightforward code which appends randomly chosen
characters to strings on every animation step. These strings are rendered
vertically, character by character. As the rain drops, it puts random
characters in place. Characters that have fallen, stay in place until the
vertical strip runs down, fades away.
Once a string reaches the full height of the window (25 characters), an index counter starts to walk from the beginning of the string to the end. The index denotes the display start of the string, making the strip run down, and the rain drop.
At random, characters that have already fallen may be mutated. This gives the illusion that the rain drops slightly move as they roll down a wet window.
If you just let it roll, you will get a very weird effect that the “fill” just doesn’t look right. The rain code needs a distribution of about 75% green rain and 25% blank. Take this into account to make it look good.
The falling of the characters all perfectly in sync with each other looked robotic. I added an interval timer per vertical strip, so that they would drop asynchronously. The result looks smooth and natural.
The code is SDL with OpenGL, and uses FTGL to render the Japanese characters,
found in the Osaka truetype font.
I added the effect of having CRT scan lines by simply drawing horizontal lines all over the screen. It looks quite nice when viewed from a distance, or if you squint your eyes.
Porting the Matrix code to raspberry pi was a bit of an adventure. First of
all, the rpi uses OpenGLES rather than OpenGL. Secondly, FTGL gave me
Segmentation fault. The solution was to grab FTGLES from github, which is
a port of FTGL for iOS. To get it going in Linux though, you need to fix
a few things first … eventually I got it going.
Many people on the net complain about OpenGL on the rasberry pi only producing a black screen. The thing is, rpi OpenGL is actually OpenGLES (embedded systems), and it’s by default version 2 meaning that you have to use shaders, vertex buffer objects, do your own matrix calculations, lighting, texturing, etcetera. You can revert to old OpenGLES 1.1 by explicitly telling it to use that version, just before creating the window:
// set OpenGLES version 1.1 SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 1); SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 1); SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES);
Mind that your programs will compile but not necessarily run correctly if
you mix in OpenGL functions that are not right for that version. Use
glGetError() to detect any errors. Calling
glGetError() often considerably
slows down the code, so remove them (or use
#ifdef constructs) from
On the raspberry pi, I got a whopping five frames per second (and less). The rpi is simply not powerful enough to render that many truetype font characters. Because there is more than one way to do it, I implemented a second version that works differently. As always with OpenGL, once you change how something works, everything about the code becomes totally different.
Because the screen stays mostly static after a character has dropped, I chose to save the entire screen as a texture. That texture is updated when the next characters drop. The big win is that FTGL need not render many glyphs all the time.
This second version of the code is indeed a lot faster on a regular computer, but not all that much on the humble rpi. At eight frames per second it is just on the verge of acceptable for the speed of the Matrix rain code. What’s really cool though, is that it can act as a screensaver for the TV now that it is running on the raspberry pi.
I figured you might also be able to do without a texture and not redraw the
entire screen every frame, but it was a big mess. I’m convinced that we
can still do better by using a frame buffer object (FBO) and shader codes.
Ironically, all of this is a lot more complicated than that original Matrix code that apparently ran on a Japanese PC in text mode.