Here’s a little tutorial about how to create the anaglyph stereoscopic 3D effect as I did it in Hyper Blazer.
Although it might need some tinkering to make it look good and even though anaglyph 3D is not the most effective stereoscopic 3D technique, it’s a trick that’s so easy to add to an existing OpenGL game that I hope to see more implementing it as it can really add something to the experience.
You do look like a total dork when you wear those silly red-cyan 3D glasses though ;D
I’m assuming you know what anaglyph 3D is, but if you don’t, here’s a link:
So the idea is to render the image twice, one for your left eye and one for the right eye. Both images are filtered in a way that each eye only sees one image, and are blended together.
Both the direction and position of the left and right eye sights need to be changed a bit so that:
- They are about 10 cm or so apart
- They look slightly ‘cross eyed’
Then the images should be filtered so that if you wear the red-cyan glasses, your left eye will only see the image meant for the left eye, and the right eye only sees the image meant for the right eye.
This means that since the left glass of red-cyan glasses is red, only the red colour component should be rendered for the left eye, and only green and blue should be rendered for the right eye. Then the images should be merged together.
Fortunately, in OpenGL this can be done with a super easy and neat trick: Using the glColorMask function:
http://www.opengl.org/documentation/specs/man_pages/hardcopy/GL/html/gl/colormask.html
So what you do each frame is this:
- clear colour and depth buffers
- set glColorMask to only render red
- render the frame translated and rotated for the left eye
- clear only the depth buffer (not the colour buffer because otherwise the previously rendered red image would get erased)
- set glColorMask to only render green and blue
- render the frame translated and rotated for the right eye
In Hyper Blazer, it looks like this:
GL11.glClear(GL11.GL_COLOR_BUFFER_BIT | GL11.GL_DEPTH_BUFFER_BIT);
if (anaglyphMode) {
GL11.glColorMask(true, false, false, true);
renderEye(-eyeDistance, -crossEyedness);
GL11.glClear(GL11.GL_DEPTH_BUFFER_BIT);
GL11.glColorMask(false, true, true, true);
renderEye(+eyeDistance, +crossEyedness);
// reset the colormask again for things like HUD rendering or other things that do not have depth
GL11.glColorMask(true, true, true, true);
} else {
// no anaglyph mode means rendering for just one 'eye'
renderEye(0, 0);
}
renderHUD();
renderEye() renders everything in the game with 3D depth
renderHUD() renders things with no 3D depth (like the HUD)
The ‘eyeDistance’ will be used in the renderEye() method to translate the camera position a bit along the x-axis (so that both virtual ‘eyes’ have a distance in between them)
The ‘crossEyedness’ will be used in the renderEye() method to slightly rotate the camera around the z-axis.
This is VERY important because it determines how far the player is looking and how the depth will appear. Where the lines of sight of both eyes meet will be rendered with no depth.
With no crossEyedness, the player would be looking infinitely far, which implies that the game is rendered such that everything pops out of the TV screen and the farthest object would appear at the location of the screen, which doesn’t look natural.
What you want is that most of the image appears ‘behind’ the screen, and some really close objects popping out of the screen so you’ll have to adjust the crossEyedness value for that.
Without crossEyedness, the effect mostly doesn’t work at all.
Some notes:
- You will have to avoid colours like full red, green or blue. They will appear only in one eye, which will give you a big headache and no depth. In Hyper Blazer, all colours are a somewhat greyed out in anaglyph mode to avoid this.
- In my experience, the effect usually doesn’t work very well on laptop screens, but it works quite well on my flatscreen TV and my CRT monitor
- Adjust the eyeDistance and crossEyedness in such a way that you don’t overdo the effect. Overdo it, and you’ll get a headache
- Obviously using anaglyphic rendering will cost performance as the whole scene has to be rendered twice per frame
- I used LWJGL in the example code, but of course any OpenGL binding will do
- You can easily order red-cyan 3D glasses on the internet. They’re dirt cheap