Lekce 46 - Fullscreenový antialiasing

Chtěli byste, aby vaše aplikace vypadaly ještě lépe než doposud? Fullscreenové vyhlazování, nazývané též multisampling, by vám mohlo pomoci. S výhodou ho používají ne-realtimové renderovací programy, nicméně s dnešním hardwarem ho můžeme dosáhnout i v reálném čase. Bohužel je implementováno pouze jako rozšíření ARB_MULTISAMPLE, které nebude pracovat, pokud ho grafická karta nepodporuje.

V tomto zajímavém tutoriálu zkusíme posunout grafický vzhled aplikací ještě dále. O antialiasingu jste už četli v minulých tutoriálech, multisampling, narozdíl od něj, neoperuje s jednotlivými objekty zvlášť, ale pracuje až s vykreslovanými pixely. Ve výsledném obrázku se pokouší najít a odstranit ostré hrany. Protože se musí vzít v úvahu každý zobrazovaný pixel, bez hardwarové akcelerace grafické karty by velice snížil výkon aplikace.

Vid_mem = sizeof(Front_buffer) + sizeof(Back_buffer) + num_samples * (sizeof(Front_buffer) + sizeof(ZS_buffer))

Pro více informací prosím zkuste tyto odkazy:

GDC2002 - OpenGL Multisample
OpenGL Pixel Formats and Multisample Antialiasing

Po tomto nutném úvodu se konečně můžeme pustit do práce. Narozdíl od jiných rozšíření, která OpenGL při renderingu využívá, musíme s ARB_MULTISAMPLE počítat už při vytváření okna. Postupujeme tedy následovně:

Začneme v souboru arb_multisample.cpp. Jako vždy inkludujeme hlavičkové soubory pro OpenGL a knihovnu GLU. O arb_multisample.h se budeme bavit později.

#include <windows.h>

#include <gl/gl.h>

#include <gl/glu.h>

#include "arb_multisample.h"

Symbolické konstanty použijeme při definování atributů pixel formátu. Podporuje-li grafická karta multisampling, bude logická proměnná arbMultisampleSupported obsahovat true.

#define WGL_SAMPLE_BUFFERS_ARB 0x2041// Symbolické konstanty pro multisampling

#define WGL_SAMPLES_ARB 0x2042

bool arbMultisampleSupported = false;// Je multisampling dostupný?

int arbMultisampleFormat = 0;// Formát multisamplingu

Následující funkce testuje, zda je WGL OpenGL rozšíření na systému dostupné v daném formátu.

bool WGLisExtensionSupported(const char *extension)// Je rozšíření podporováno?

{

const size_t extlen = strlen(extension);

const char *supported = NULL;

// Pokud je to možné, pokusí se wglGetExtensionStringARB použít na aktuální DC

PROC wglGetExtString = wglGetProcAddress("wglGetExtensionsStringARB");

if (wglGetExtString)// WGL OpenGL rozšíření

{

supported = ((char*(__stdcall*)(HDC))wglGetExtString)(wglGetCurrentDC());

}

if (supported == NULL)// Zkusí ještě standardní OpenGL řetězec s rozšířeními

{

supported = (char*)glGetString(GL_EXTENSIONS);

}

if (supported == NULL)// Pokud selže i toto, není řetězec dostupný

{

return false;

}

for (const char* p = supported; ; p++)// Testování obsahu řetězce

{

p = strstr(p, extension);// Hledá podřetězec

if (p == NULL)// Podřetězec není v řetězci

{

return false;// Rozšíření nebylo nalezeno

}

// Okolo podřetězce se musí vyskytovat oddělovač (mezera nebo NULL)

if ((p == supported || p[-1] == ' ') && (p[extlen] == '\0' || p[extlen] == ' '))

{

return true;// Rozšíření bylo nalezeno

}

}

}

Funkce InitMultisample() je svým způsobem jádrem programu. Dotážeme se na podporu potřebného rozšíření a pokud ji máme, získáme požadovaný pixel formát.

bool InitMultisample(HINSTANCE hInstance, HWND hWnd, PIXELFORMATDESCRIPTOR pfd)// Inicializace multisamplingu

{

if (!WGLisExtensionSupported("WGL_ARB_multisample"))// Existuje řetězec ve WGL

{

arbMultisampleSupported = false;

return false;

}

PFNWGLCHOOSEPIXELFORMATARBPROC wglChoosePixelFormatARB = (PFNWGLCHOOSEPIXELFORMATARBPROC)wglGetProcAddress("wglChoosePixelFormatARB");// Získání pixel formátu

if (!wglChoosePixelFormatARB)// Daný pixel formát není dostupný

{

arbMultisampleSupported = false;

return false;

}

HDC hDC = GetDC(hWnd);// Získání kontextu zařízení

int pixelFormat;

int valid;

UINT numFormats;

float fAttributes[] = {0, 0};

Následující pole atributů slouží pro definování vlastností pixel formátu. Všechny položky kromě WGL_SAMPLE_BUFFERS_ARB a WGL_SAMPLE_ARB jsou standardní, a proto by nám neměly činit potíže. Pokud uspěje hlavní test podpory multisamplingu, který reprezentuje wglChoosePixelFormatARB(), máme vyhráno.

int iAttributes[] =// Atributy

{

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);// Pixel formát pro čtyři vzorkování

if (valid && numFormats >= 1)// Vráceno true a počet formátů je větší než jedna

{

arbMultisampleSupported = true;

arbMultisampleFormat = pixelFormat;

return arbMultisampleSupported;

}

iAttributes[19] = 2;// Čtyři vzorkování nejsou dostupná, test dvou

valid = wglChoosePixelFormatARB(hDC, iAttributes, fAttributes, 1, &pixelFormat, &numFormats);

if (valid && numFormats >= 1)

{

arbMultisampleSupported = true;

arbMultisampleFormat = pixelFormat;

return arbMultisampleSupported;

}

return arbMultisampleSupported;// Vrácení validního formátu

}

Kód pro detekci multisamplingu máme hotov, teď modifikujeme vytváření okna. Inkludujeme hlavičkový soubor arb_multisample.h a vytvoříme funkční prototypy.

#include "arb_multisample.h"// Hlavičkový soubor pro multisampling

BOOL DestroyWindowGL(GL_Window* window);// Funkční prototypy

BOOL CreateWindowGL(GL_Window* window);

Následující výpis kódu patří do funkce CreateWindowGL(). Původní kód povětšinou zůstane, ale uděláme v něm několik změn. V základu potřebujeme vyřešit problém, který spočívá v tom, že nemůžeme položit dotaz na pixel formát (detekovat přítomnost multisamplingu), dokud není vytvořeno okno. Nicméně naproti tomu nemůžeme vytvořit okno s vyhlazováním, dokud nemáme pixel formát, který ho podporuje. Trochu se to podobá otázce, zda bylo první vejce nebo slepice. Implementujeme dvouprůchodový systém - nejprve vytvoříme obyčejné okno, dotážeme se na pixel formát a pokud je multisampling podporován, zrušíme okno a vytvoříme správné. Trochu těžkopádné, ale neznám jiný způsob.

// Funkce CreateWindowGL()

window->hDC = GetDC(window->hWnd);// Grabování kontextu zařízení

if (window->hDC == 0)// Podařilo se ho získat?

{

DestroyWindow(window->hWnd);// Zrušení okna

window->hWnd = 0;// Nulování handle

return FALSE;// Neúspěch

}

Při prvním průchodu touto funkcí (další průchody např. při přepínání do/z fullscreenu) není možné multisampling natvrdo zapnout, takže jsme vytvořili pouze obyčejné okno. Pokud máme jistotu, že ho můžeme použít, nastavíme pixel formát na arbMultiSampleFormat.

if(!arbMultisampleSupported)// Multisampling není podporován

{

// Vytvoření normálního okna

PixelFormat = ChoosePixelFormat(window->hDC, &pfd);// Získá kompatibilní pixel formát

if (PixelFormat == 0)// Podařilo se ho získat?

{

ReleaseDC(window->hWnd, window->hDC);// Uvolnění kontextu zařízení

window->hDC = 0;// Nulování proměnné

DestroyWindow(window->hWnd);// Zrušení okna

window->hWnd = 0;// Nulování handle

return FALSE;// Neúspěch

}

}

else// Multisampling je podporován

{

PixelFormat = arbMultisampleFormat;

}

if (SetPixelFormat(window->hDC, PixelFormat, &pfd) == FALSE)// Zkusí nastavit pixel formát

{

ReleaseDC(window->hWnd, window->hDC);

window->hDC = 0;

DestroyWindow(window->hWnd);

window->hWnd = 0;

return FALSE;

}

window->hRC = wglCreateContext(window->hDC);// Zkusí získat rendering kontext

if (window->hRC == 0)// Podařilo se ho získat?

{

ReleaseDC(window->hWnd, window->hDC);

window->hDC = 0;

DestroyWindow(window->hWnd);

window->hWnd = 0;

return FALSE;

}

if (wglMakeCurrent(window->hDC, window->hRC) == FALSE)// Aktivuje rendering kontext

{

wglDeleteContext(window->hRC);

window->hRC = 0;

ReleaseDC(window->hWnd, window->hDC);

window->hDC = 0;

DestroyWindow(window->hWnd);

window->hWnd = 0;

return FALSE;

}

Okno bylo vytvořeno, takže máme k dispozici handle pro dotaz na multisampling. Pokud je podporován, zrušíme okno a vytvoříme ho s novým pixel formátem.

if(!arbMultisampleSupported && CHECK_FOR_MULTISAMPLE)// Je multisampling dostupný?

{

if(InitMultisample(window->init.application->hInstance, window->hWnd, pfd))// Inicializace multisamplingu

{

DestroyWindowGL(window);

return CreateWindowGL(window);

}

}

ShowWindow(window->hWnd, SW_NORMAL);// Zobrazí okno

window->isVisible = TRUE;

ReshapeGL(window->init.width, window->init.height);// Oznámí rozměry okna OpenGL

ZeroMemory(window->keys, sizeof(Keys));// Nulování pole indikující stisk kláves

window->lastTickCount = GetTickCount();// Inicializuje časovou proměnnou

return TRUE;// Vše v pořádku

}

OK, nastavování je kompletní, dostáváme se k zábavnější části, pro kterou jsme se tak snažili. Naštěstí se sdružení ARB rozhodlo učinit multisampling dynamickým, což nám ho umožňuje kdykoli zapnout nebo vypnout. Stačí jednoduché glEnable() a glDisable().

glEnable(GL_MULTISAMPLE_ARB);

// Vykreslení vyhlazovaných objektů

glDisable(GL_MULTISAMPLE_ARB);

A to je vše. Až spustíte ukázkové demo, uvidíte, jak kvalitně vyhlazování zlepšuje celkový vzhled scény.

napsal: Colt McAnlis - MainRoach <duhroach (zavináč) hotmail.com>
přeložil: Michal Turek - Woq <WOQ (zavináč) seznam.cz>

Zdrojové kódy

Lekce 46

<<< Lekce 45 | Lekce 47 >>>