Recently I’ve been working on a CUDA ray tracing application which uses OpenGL for CUDA graphics output. I was slightly annoyed by the rough, aliased, edges of my output, but I wanted to avoid implementing a fully fledged anti-aliasing solution. This short post described how to fake anti-aliasing in your CUDA output in a highly computationally efficient (and simple) manner by using OpenGL’s texture filtering.
One way to display CUDA output is to render it to an OpenGL PBO, copy it to a texture and texture it on a quad covering whole OpenGL view port, see here for more info. In a typical situation one texture pixel (texel) maps directly to corresponding screen pixel.
However, if you shift the textured quad, or the viewport, slightly, so that there is no direct mapping between texels and pixels you will enforce OpenGL to applying subtle linear filtering to the texture. Thus, you’ll get rid of those nasty aliased edges. See the image for side-by-side comparison.
Shifting the viewport:
float offset = 0.998; // slighly shifted from 1.0 glOrtho(-offset, offset, -offset, offset, 0, 1000); // zNear, zFar
Remember to have OpenGL texture filtering set to GL_LINEAR for the texture:
GL_LINEAR returns the weighted average of the four texture elements that are closest to the centre of the pixel being textured
GLuint tex; // Texture to which PBO content from CUDA is copied glGenTextures(1, &tex); glBindTexture(GL_TEXTURE_2D, tex); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); // GL_LINEAR glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); // GL_LINEAR glBindTexture(GL_TEXTURE_2D, 0);
- it’s lighting-fast the whole ‘anti-aliasing’ process is done by rasterization hardware of your GPU
- it’s dead simple to implement — which means little changes to your existing code
- It’s still not true anti-aliasing; thus the image gets slightly blurred also in low-contrast areas (which is not the case with true AA)
- It’s not a perfect solution — it works best for close to diagonal edges (like to ones on the image). if a given edge is close to being horizontal/vertical the results are far less impressive.