Fullscreen AntiAliasing
Howdy all, the Friendly Neighborhood Roach here with an interesting tutorial that will help you get your apps top notch. In the realm of getting your OpenGL programs to look better, a big problem we all run into is aliasing. That is, the square edged "jaggies" that exist on diagonal lines in relation to the square pixels that exist on your screen. Ie, Bad Mojo. The answer, termed Anti-Aliasing, is used to smudge those "jaggies" in order to create a smoother edge for objects. One process used to achieve anti-aliasing is called "Multisampling." The idea is that for each pixel, we sample the pixels around it to determine if this edge needs to be anti-aliased, basically, we discard the jaggies by "smudging" the pixel itself.
Fullscreen AntiAliasing is something that non-realtime rendering programs have always had an advantage in. However, with current hardware, we're able to pull off the same effect real time. The ARB_MULTISAMPLE extension allows us to do this. Essentially, each pixel is sampled by it's neighbors to find out the optimal antialias to perform. This comes at a cost however, and can slow down performance.
1 2 | Vid_mem = sizeof (Front_buffer) + sizeof (Back_buffer) + num_samples
* ( sizeof (Front_buffer) + sizeof (ZS_buffer))
|
For more specific information on anti-aliasing, as well as the information I'm about to present, please check out the following links:
GDC2002 -- OpenGL Multisample
OpenGL Pixel Formats and Multisample Antialiasing
With that being said, a brief overview of how our process is going to work. Unlike other extensions, which relate to OpenGL rendering, the ARB_MULTISAMPLE extension must be dealt with during the creation of your rendering window. Our process shall go as follows:
- Create our Window as normal
- Query the possible Multisample pixel values (InitMultisample)
- If Multisampling is available, destroy this Window and recreate it with our NEW pixelFormat
- For parts we want to antialias, simply call glEnable(GL_ARB_MULTISAMPLE);
Let's start off from the top, and talk about our source file arbMultiSample.cpp. We start off with the standard includes for gl.h & glu.h, as well as windows.h, we'll talk about arb_multisample.h later.
1 2 3 4 | #include <windows.h>
#include <gl/gl.h>
#include <gl/glu.h>
#include "arb_multisample.h"
|
The two lines below define our access points into the WGL string listing. We'll use these in accessing the pixel format attributes to test for our sample format. The other two variables are used other places in the program to access our data.
1 2 3 4 5 6 | #define WGL_SAMPLE_BUFFERS_ARB 0x2041
#define WGL_SAMPLES_ARB 0x2042
bool arbMultisampleSupported = false ;
int arbMultisampleFormat = 0;
|
The next function we're going to talk about is WGLisExtensionSupported, which will be used to query the WGL extensions listing to verify if a given format is supported on the system. We'll provide the description of the code as we walk through it, because it's easier to do that then all the html back and forth jumping....
NOTE: The code below was rewritten by Henry Goffin. His update adds: Better parsing of the supported GL Extensions and fixes a problem with the fallback code if the first check was to fail.
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 | bool WGLisExtensionSupported( const char *extension)
{
const size_t extlen = strlen (extension);
const char *supported = NULL;
PROC wglGetExtString = wglGetProcAddress( "wglGetExtensionsStringARB" );
if (wglGetExtString)
supported = (( char *(__stdcall*)( HDC ))wglGetExtString)(wglGetCurrentDC());
if (supported == NULL)
supported = ( char *)glGetString(GL_EXTENSIONS);
if (supported == NULL)
return false ;
for ( const char * p = supported; ; p++)
{
p = strstr (p, extension);
if (p == NULL)
return false ;
if ((p==supported || p[-1]== ' ' ) && (p[extlen]== '\0' || p[extlen]== ' ' ))
return true ;
}
}
|
The next function is the gist of the program itself. Basically, we're going to query if our arb extention is supported on the system. From there, we'll move on to querying our device context to find out the scope of our multisample. Again... let's just jump into the code.
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 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 | bool InitMultisample( HINSTANCE hInstance, HWND hWnd,PIXELFORMATDESCRIPTOR pfd)
{
if (!WGLisExtensionSupported( "WGL_ARB_multisample" ))
{
arbMultisampleSupported= false ;
return false ;
}
PFNWGLCHOOSEPIXELFORMATARBPROC wglChoosePixelFormatARB =
(PFNWGLCHOOSEPIXELFORMATARBPROC)wglGetProcAddress( "wglChoosePixelFormatARB" );
if (!wglChoosePixelFormatARB)
{
arbMultisampleSupported= false ;
return false ;
}
HDC hDC = GetDC(hWnd);
int pixelFormat;
bool valid;
UINT numFormats;
float fAttributes[] = {0,0};
int iAttributes[] = { WGL_DRAW_TO_WINDOW_ARB,GL_TRUE,
WGL_SUPPORT_OPENGL_ARB,GL_TRUE,
WGL_ACCELERATION_ARB,WGL_FULL_ACCELERATION_ARB,
WGL_COLOR_BITS_ARB,24,
WGL_ALPHA_BITS_ARB,8,
WGL_DEPTH_BITS_ARB,16,
WGL_STENCIL_BITS_ARB,0,
WGL_DOUBLE_BUFFER_ARB,GL_TRUE,
WGL_SAMPLE_BUFFERS_ARB,GL_TRUE,
WGL_SAMPLES_ARB, 4 ,
0,0};
valid = wglChoosePixelFormatARB(hDC,iAttributes,fAttributes,1,&pixelFormat,&numFormats);
if (valid && numFormats >= 1)
{
arbMultisampleSupported = true ;
arbMultisampleFormat = pixelFormat;
return arbMultisampleSupported;
}
iAttributes[19] = 2;
valid = wglChoosePixelFormatARB(hDC,iAttributes,fAttributes,1,&pixelFormat,&numFormats);
if (valid && numFormats >= 1)
{
arbMultisampleSupported = true ;
arbMultisampleFormat = pixelFormat;
return arbMultisampleSupported;
}
return arbMultisampleSupported;
}
|
Now that we've gotten our query code done, we have to modify how our window creation works. First, we have to include our arb_multisample.h file, along with moving the destroywindow and createwindow prototypes up to the top of the file.
1 2 3 4 5 6 7 8 9 10 11 | #include <windows.h> // Header File For The Windows Library
#include <gl/gl.h> // Header File For The OpenGL32 Library
#include <gl/glu.h> // Header File For The GLu32 Library
#include "NeHeGL.h" // Header File For The NeHeGL Basecode
#include "ARB_MULTISAMPLE.h"
BOOL DestroyWindowGL (GL_Window* window);
BOOL CreateWindowGL (GL_Window* window);
|
This next bit needs to be added inside the CreateWindowGL function, the original code is left, with where you need to make modifications. Basically, we're doing a bit of overkill to get the job done. We can't request the pixel format (to query the multisampling) until we've created a window. But we can't create a FSAA screen, unless we know the pixel format will support it. Kinda the chicken and the egg thing. So, what I've done, is a little 2 pass system; We create the window, query the pixelFormat, then destroy/remake the window if multisampling is supported. Kinda nifty...
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 | window->hDC = GetDC (window->hWnd);
if (window->hDC == 0)
{
DestroyWindow (window->hWnd);
window->hWnd = 0;
return FALSE;
}
if (!arbMultisampleSupported)
{
PixelFormat = ChoosePixelFormat (window->hDC, &pfd);
if (PixelFormat == 0)
{
ReleaseDC (window->hWnd, window->hDC);
window->hDC = 0;
DestroyWindow (window->hWnd);
window->hWnd = 0;
return FALSE;
}
}
else
{
PixelFormat = arbMultisampleFormat;
}
if (SetPixelFormat (window->hDC, PixelFormat, &pfd) == FALSE)
{
ReleaseDC (window->hWnd, window->hDC);
window->hDC = 0;
DestroyWindow (window->hWnd);
window->hWnd = 0;
return FALSE;
}
|
Now, the window has been created, so we have a valid handle to query for Multisample support. If we find it's supported, we destroy this window, and call CreateWindowGL again, but with the new pixel format.
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 |
if (wglMakeCurrent (window->hDC, window->hRC) == FALSE)
{
wglDeleteContext (window->hRC);
window->hRC = 0;
ReleaseDC (window->hWnd, window->hDC);
window->hDC = 0;
DestroyWindow (window->hWnd);
window->hWnd = 0;
return FALSE;
}
if (!arbMultisampleSupported && CHECK_FOR_MULTISAMPLE)
{
if (InitMultisample(window->init.application->hInstance,window->hWnd,pfd))
{
DestroyWindowGL (window);
return CreateWindowGL(window);
}
}
ShowWindow (window->hWnd, SW_NORMAL);
window->isVisible = TRUE;
|
OK, so setup is now complete! We've setup our screen to allow proper depth for multi sampling. Now comes the fun part, actually doing it! Luckily, ARB decided to make multisampling dynamic, so that allows us to turn it on and off with a glEnable / glDisable call.
1 2 3 4 5 | glEnable(GL_MULTISAMPLE_ARB);
glDisable(GL_MULTISAMPLE_ARB);
|
And that's it! As far as the other code for the demo goes, it's a simple rotating effect to show off how nifty this method works. ENJOY!
Colt "MainRoach" McAnlis
* DOWNLOAD Visual C++ Code For This Lesson.
* DOWNLOAD Borland C++ Builder 6 Code For This Lesson. ( Conversion by Le Thanh Cong )
* DOWNLOAD Code Warrior 5.3 Code For This Lesson. ( Conversion by Scott Lupton )
* DOWNLOAD Delphi Code For This Lesson. ( Conversion by Michal Tucek )
* DOWNLOAD Dev C++ Code For This Lesson. ( Conversion by Neil Roy )
* DOWNLOAD Visual Studio .NET Code For This Lesson. ( Conversion by Joachim Rohde )
< Lesson 45Lesson 47 >