Lekcja 8. Mieszanie kolorów
Autor: Łukasz 'lmmilewski' Milewski
Oryginał: Blending (Tom Stanis, Jeff Molofee (NeHe))
Źródła: http://nehe.gamedev.net/data/lessons/vc/lesson08.zip

Prosta przezroczystość

Większość efektów specjalnych w OpenGL polega na jakimś rodzaju mieszania kolorów (ang. blending). Mieszanie kolorów jest używane do połączenia koloru piksela, który ma zostać wyświetlony, z kolorem tego, który już jest na ekranie. To, w jaki sposób kolory są łączone zależy od ich wartości alpha lub od funkcji mieszającej dane kolory. Alpha jest czwartą składową koloru (zwykle jest podawana jako ostatnia). Poprzednio używałeś GL_RGB aby podać 3 wartości składowych koloru. Aby móc zadać dodatkowo składową alpha możesz użyć GL_RGBA. Dodatkowo możesz użyć glColor4f() zamiast glColor3f().

Większość ludzi myśli o składowej alpha jak o współczynniku nieprzezroczystości materiału. Wartość 0.0 składowej alpha oznacza, że materiał jest całkowicie przezroczysty, natomiast wartość 1.0 przeciwnie - zupełnie nieprzezroczysty.

Równanie mieszania kolorów (ang. The blending equation)

Jeżeli nie lubisz matematyki i interesuje cię sam efekt przezroczystości, to po prostu pomiń ten akapit. Jeżeli natomiast chcesz zrozumieć jak działa mieszanie kolorów, to ten akapit jest właśnie dla Ciebie.

(Rs Sr + Rd Dr, Gs Sg + Gd Dg, Bs Sb + Bd Db, As Sa + Ad Da)

OpenGL wyliczy wynik mieszania kolorów dwóch pikseli korzystając z powyższego równania. Indeksy s oraz d oznaczają odpowiednio: źródłowy (source) i docelowy (destination) piksel. S i D to współczynniki mieszania kolorów. Ich wartości wskazują w jaki sposób chcesz mieszać kolory. Indeksy r g b a oznaczają odpowiednio składowe: zieloną, czerwoną, niebieską oraz alpha. Najbardziej powszechne wartości dla S to (As, As, As, As) (czyli wartości źródła alpha), natomiast dla D to (1,1,1,1) - (As, As, As, As). To daje następujące równanie mieszania kolorów:

(Rs As + Rd (1 - As), Gs As + Gd (1 - As), Bs As + Bd (1 - As), As As + Ad (1 - As)).

To równanie da efekt przezroczystości/półprzezroczystości.

Mieszanie kolorów w OpenGL

Włączasz mieszanie kolorów tak samo jak wszystko inne. Następnie ustawiasz równanie i wyłączasz zapisywanie w buforze głębi podczas rysowania przezroczystych obiektów (aby obiekty za obiektami półprzezroczystymi były rysowane). To nie jest właściwy sposób mieszania kolorów, ale w większości wypadków w prostych projektach będzie działał. Rui Martins dodaje: właściwy sposób polega na narysowaniu wszystkich przezroczystych (z alpha < 1.0) wielokątów po narysowaniu całej sceny i rysowaniu ich w odwrotnej kolejności niż są w buforze głębi (najdalszy najpierw). Należy tak postępować gdyż mieszanie kolorów dwóch wielokątów w różnych kolejnościach daje różne wyniki (np. jeżeli wielokąt 1 jest najbliżej widza (ang. viewer) to poprawna kolejność rysowania jest następująca: najpierw wielokąt 2, potem 1. Jest jak w rzeczywistości: światło biegnące zza przezroczystych obiektów musi najpierw przejść przez wielokąt 2, a potem przez 1, zanim dotrze do oka widza.) POWINIENEŚ POSORTOWAĆ PRZEZROCZYSTE WIELOKĄTY PO ODLEGŁOŚCI OD WIDZA i narysować je PO NARYSOWANIU CAŁEJ SCENY, z WŁĄCZONYM BUFOREM GŁĘBI. W przeciwnym przypadku dostaniesz niepoprawny wynik. Wiem, że czasem ta metoda "boli" (ang. it's a pain), ale to jest właściwy sposób wyświetlania przezroczystych obiektów na scenie.

Użyjemy kodu z poprzedniego tutoriala. Zaczynamy dodając dwie zmienne. Przepiszę jeszcze raz cały kod dla jasności.

#include <windows.h>         // Nagłówek dla Windowsa
#include <stdio.h>         // Nagłówek dla standardowego wejścia/wyjścia
#include <gl\gl.h>         // Nagłówek dla biblioteki OpenGL
#include <gl\glu.h>         // Nagłówek dla biblioteki GLu32
#include <gl\glaux.h>         // Nagłówek dla biblioteki GLaux
HDC        hDC=NULL;         // Prywatny kontekst urządzenia GDI
HGLRC        hRC=NULL;         // Stały kontekst renderowania
HWND        hWnd=NULL;         // Trzyma uchwyt okna
HINSTANCE    hInstance;         // Trzyma instancję aplikacji
bool    keys[256];         // Tablica używana do trzymania stanu klawiatury
bool    active=TRUE;         // Flaga aktywności okna (domyślnie TRUE)
bool    fullscreen=TRUE;         // Flaga pełnego ekranu (domyślnie TRUE)
bool    light;         // Oświetlenie Włączone/Wyłączone
bool blend;         // Mieszanie kolorów Włączone/Wyłączone
bool    lp;         // Naciśnięto L?
bool    fp;         // Naciśnięto F?
bool    bp;         // Naciśnięto B? (NOWE)
GLfloat    xrot;         // Obrót X
GLfloat    yrot;         // Obrót Y
GLfloat xspeed;         // Prędkość obrotu X
GLfloat yspeed;         // Prędkość obrotu Y
GLfloat    z=-5.0f;         // Głębokość ekranu
GLfloat LightAmbient[]= { 0.5f, 0.5f, 0.5f, 1.0f };         // Wartości światła otaczającego
GLfloat LightDiffuse[]=     { 1.0f, 1.0f, 1.0f, 1.0f };         // Wartości światła rozproszonego
GLfloat LightPosition[]= { 0.0f, 0.0f, 2.0f, 1.0f };         // Pozycja światła
GLuint    filter;         // Którego filtra użyć
GLuint    texture[3];         // Pamięć na 3 tekstury
LRESULT    CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);         // Deklaracja WndProc

Znajdź funkcję LoadGLTextures(). Znajdź linię: if (TextureImage[0]=LoadBMP("Data/Crate.bmp")). Zamień ją na poniższą. W tym tutorialu jako tekstury użyjemy witrażu zamiast skrzyni.

    if (TextureImage[0]=LoadBMP("Data/glass.bmp"))         // Załaduj bitmapę z witrażem (NOWE)

Dodaj następujące dwie linie gdzieś w InitGL(). Ta funkcja ustawia jasność rysowania obiektu na pełną jasność z współczynnikiem alpha równym 50%. To oznacza, że kiedy mieszanie kolorów jest włączone, obiekt będzie przezroczysty w 50%. Druga linia ustawia typ mieszania koloru, który zostanie użyty.

Rui Martins Dodaje: Wartość 0.0 oznacza, że materiał jest całkowicie przezroczysty. Wartość 1.0 oznacza całkowicie nieprzezroczysty.

    glColor4f(1.0f,1.0f,1.0f,0.5f);         // Pełna jasność, współczynnik alpha = 50% ( NOWE )
    glBlendFunc(GL_SRC_ALPHA,GL_ONE);         // Funkcja mieszająca kolory dla półprzezroczystości oparta o wartość źródłową alpha ( NOWE )
    Następną sekcję kodu możesz znaleźć pod koniec lekcji siódmej.
    if (keys[VK_LEFT])         // Czy wciśnięto strzałkę w lewo?
    {
        yspeed-=0.01f;         // Jeśli tak to zmniejsz prędkość
    }

Zaraz pod powyższym kodem, dodamy poniższe linie. Te linie sprawdzają czy został naciśnięty klawisz 'B'. Jeżeli tak to komputer sprawdza czy mieszanie kolorów jest włączone czy wyłączone. Jeżeli jest włączone to zostaje wyłączone. Jeżyli jest wyłączone to zostaje włączone.

    if (keys['B'] && !bp)         // Czy klawisz B jest wciśnięty i bp jest równe FALSE?
    {
        bp=TRUE;         // Jeżeli tak to bp = TRUE
        blend = !blend;         // Przełącz mieszanie kolorów TRUE/FALSE
        if(blend)         // Czy mieszanie kolorów jest TRUE?
        {
            glEnable(GL_BLEND);         // Włącz mieszanie kolorów
            glDisable(GL_DEPTH_TEST);         // Wyłącz testowanie głębokości
        }
        else         // W przeciwnym wypadku
        {
            glDisable(GL_BLEND);         // Wyłącz mieszanie kolorów
            glEnable(GL_DEPTH_TEST);         // Włącz testowanie głębokości
        }
    }
    if (!keys['B'])         // Czy klawisz B został puszczony
    {
        bp=FALSE;         // Jeśli tak to bp=FALSE
    }

Ale w jaki sposób określić kolor jeżeli używamy tekstury? Prosto, każdy piksel, który jest odwzorowany przez teksturę (ang. texture mapped), jest mnożony przez aktualny kolor. Zatem, jeżeli kolor do narysowania to (0.5, 0.6, 0.4), to mnożymy go przez aktualny kolor i dostajemy (0.5, 0.6, 0.5, 0.2) (zakłada się, że współczynnik alpha jest równe 1.0, jeżeli nie został określony).

To wszystko! Mieszanie kolorów jest bardzo proste w OpenGL.