Panel użytkownika
Nazwa użytkownika:
Hasło:
Nie masz jeszcze konta?
Autor: Janusz Ganczarski
Biblioteki C++

Mieszanie kolorów

[lekcja] Rozdział 15. Równanie mieszania kolorów, współczynniki mieszania kolorów; dwa programy przykładowe, w tym efekt przezroczystości.
Biblioteka OpenGL umożliwia mieszanie kolorów (ang. blending), czyli ustalanie wynikowego koloru piksela jako kombinacji koloru źródłowego (Rs , Gs , Bs , As ) oraz koloru przeznaczenia (Rd , Gd , Bd , Ad ) przechowywanego w buforze kolorów. Z uwagi na dużą rolę w mieszaniu kolorów dotychczas przez nas nieużywanej składowej alfa (A) koloru, w literaturze można się także spotkać z angielskim określeniem tej techniki alfa blending.
Mieszanie kolorów wykorzystywane jest m.in. do uzyskania takich efektów jak przezroczystość, antyaliasing i odbicie.

Włączanie i wyłączanie mieszania kolorów

Mieszanie kolorów jest w bibliotece OpenGL domyślnie wyłączone. Aktywacja mieszania kolorów wymaga wywołania funkcji glEnable z parametrem GL_BLEND. Analogicznie Wywołanie funkcji glDisable z tym samym parametrem spowoduje wyłączenie mieszania kolorów.
Z uwagi na korzystania ze składowych alfa kolorów, technika mieszania kolorów nie jest dostępna, gdy biblioteka OpenGL pracuje w trybie indeksu kolorów.

Równanie mieszania kolorów

Domyślnie kolor wynikowy (R, G, B, A) obliczany jest na podstawie poniższego równania mieszania kolorów (ang. blend equation):
w którym (Sr , Sg , Sb , Sa ) i (Dr , Dg , Db , Da ) stanowią odpowiednio współczynniki mieszania dla koloru źródłowego i koloru przeznaczenia. W razie potrzeby wartości składowych (R, G, B, A) koloru wynikowego odcinane są do przedziału [0, 1], stąd spotykana inna, bardziej poprawna matematycznie, forma zapisu tego równania:
Wybór innego niż domyślne równania mieszania kolorów umożliwia funkcja:
C/C++
void glBlendEquation( GLenum mode )
której parametr mode może przyjąć jedną z poniższych wartości:
  • GL_FUNC_ADD - domyślne równanie mieszania,
  • GL_FUNC_SUBTRACT - równanie mieszania:
  • GL_FUNC_REVERSE_SUBTRACT - równanie mieszania:
  • GL_MIN - równanie mieszania:
  • GL_MAX - równanie mieszania:
  • GL_LOGIC_OP - równanie mieszania wykorzystuje operację logiczną ◦ wykonywaną na bitach składowych pikseli:
Zauważmy, że równania opisane stałymi GL_MIN, GL_MAX i LOGIC OP nie korzystają ze współczynników mieszania.
Operacje logiczne na bitach składowych pikseli są domyślnie wyłączone. Włączenie operacji logicznych w przypadku, gdy bufor kolorów działa w trybie RGB, wymaga wywołania funkcji glEnable z parametrem GL_COLOR_LOGIC_OP. Operacje logiczne można także wykonywać na indeksach kolorów, gdy bufor kolorów pracuje w trybie indeksowym. Wymaga to wywołania funkcji glEnable z parametrem GL_INDEX_LOGIC_OP.
Wybór operacji logicznej umożliwia funkcja:
C/C++
void glLogicOp( GLenum opcode )
której wartości parametru opcode i odpowiadające im operacje logiczne na bitach składowej koloru źródłowego s i bitach składowej koloru przeznaczenia d przedstawia tabela 1. Operacja logiczna różnicy symetrycznej XOR oznaczono symbolem „⊕.” Operacją domyślną jest GL_COPY

Tabela 1: Operacje logiczne na bitach składowych kolorów

parametroperacja logiczna
GL_CLEAR0
GL_SET1
GL_COPYs
GL_COPY_INVERTED¬s
GL_NOOPd
GL_INVERT¬d
GL_ANDs ∧ d
GL_NAND¬(s ∧ d)
GL_ORs ∨ d
GL_NOR¬(s ∨ d)
GL_XORs ⊕ d
GL_EQUIV¬(s ⊕ d)
GL_AND_REVERSEs ∧ ¬d
GL_AND_INVERTED¬s ∧ d
GL_OR_REVERSEs ∨ ¬d
GL_OR_INVERTED¬s ∨ d
Funkcja glBlendEquation i związana z nią możliwość zmiany równania mieszania kolorów została wprowadzona w wersji 1.2 biblioteki OpenGL jako część składową opcjonalnego zbioru funkcji odpowiedzialnych za przetwarzanie obrazów (ang. imaging subset), ujętą w jednym rozszerzeniu ARB imaging. Wcześniej funkcjonalność tę obejmowały rozszerzenia: EXT blend logic op (równanie GL_LOGIC_OP), EXT blend subtract (równania GL_FUNC_SUBTRACT i GL_FUNC_REVERSE_SUBTRACT) i EXT blend minmax (równania GL_MIN i GL -
MAX). W wersji 1.4 OpenGL funkcja glBlendEquation stała się częścią obowiązkowej specyfikacji biblioteki.
W wersji 2.0 biblioteki OpenGL ponownie rozszerzono możliwości modyfikacji równania mieszania kolorów. Dodana funkcja:
C/C++
void glBlendEquationSeparate( enum modeRGB, enum modeAlpha )
umożliwia odrębne określenie równania mieszania dla składowych RGB (parametr modeRGB) i składowej alfa (parametr modeAlpha). Każdy z tych parametrów może przyjmować niezależnie takie same wartości jak parametr funkcji glBlendEquation. Oczywiście, dla obu parametrów wartością domyślną jest GL_FUNC_ADD. Wcześniej funkcjonalność tę udostępniało rozszerzenie EXT blend equation separate oraz mało znane rozszerzenie ATI blend equation separate. Co ciekawe rozszerzenie firmy ATI zostało opracowane wcześniej, ale z uwagi na niezgodność (!) z przyjętymi w bibliotece OpenGL konwencjami nazewniczymi, nie zostało zaakceptowane przez innych członków ARB.

Współczynniki mieszania kolorów

Część z przedstawionych równań mieszania korzystała ze współczynników mieszania kolorów dla koloru źródłowego (Sr , Sg , Sb , Sa ) i koloru przeznaczenia (Dr , Dg , Db , Da ). Do określania wartości tych współczynników biblioteka OpenGL posiada specjalne funkcje. Podstawową z nich jest:
C/C++
void glBlendFunc( GLenum src, GLenum dst )
której parametry src i dst określają sposób obliczania współczynników mieszania kolorów. Zestawienie dopuszczalnych wartości tych parametrów przedstawia tabela 2.

Tabela 2: Wartości parametrów funkcji glBlendFunc

parametrwspółczynnik mieszania
GL_ZERO(0, 0, 0, 0)
GL_ONE(1, 1, 1, 1)
GL_SRC_COLOR(Rs , Gs , Bs , As )
GL_ONE_MINUS_SRC_COLOR(1, 1, 1, 1) − (Rs , Gs , Bs , As )
GL_DST_COLOR(Rd , Gd , Bd , Ad )
GL_ONE_MINUS_DST_COLOR(1, 1, 1, 1) − (Rd , Gd , Bd , Ad )
GL_SRC_ALPHA(As , As , As , As )
GL_ONE_MINUS_SRC_ALPHA(1, 1, 1, 1) − (As , As , As , As )
GL_DST_ALPHA(Ad , Ad , Ad , Ad )
GL_ONE_MINUS_DST_ALPHA(1, 1, 1, 1) − (Ad , Ad , Ad , Ad )
GL_CONSTANT_COLOR(Rc, Gc, Bc, Ac)
GL_ONE_MINUS_CONSTANT_COLOR(1, 1, 1, 1) − (Rc, Gc, Bc, Ac)
GL_CONSTANT_ALPHA(Ac, Ac, Ac, Ac)
GL_ONE_MINUS_CONSTANT_ALPHA(1, 1, 1, 1) − (Ac, Ac, Ac, Ac)
GL_SRC_ALPHA_SATURATE(f, f, f, 1) , f = min (As , 1 − Ad )
Wartość początkowa dla parametru src wynosi GL_ONE. Wartość początkowa dla parametru dst wynosi GL_ZERO.
Stała GL_SRC_ALPHA_SATURATE może być stosowana wyłącznie jako wartość parametru src. Pozostałe wartości można stosować do obu parametrów funkcji glBlendFunc. Całkowitą dowolność w ich wyborze wprowadzono dopiero w wersji 1.4 biblioteki OpenGL. W wcześniejszych wersjach OpenGL parametr sfactor nie mógł przyjmować wartości SRC COLOR i ONE MINUS - SRC COLOR, a parametr dfactor wartości DST COLOR i ONE MINUS DST COLOR. Natomiast możliwość taką udostępniało początkowo rozszerzenie NV blend - square.
Podobnie jak w przypadku definiowania odrębnych równań mieszania kolorów dla składowych RGB i składowej alfa, OpenGL od wersji 1.4 umożliwia zdefiniowanie odrębnych współczynników mieszania kolorów dla składowych RGB koloru źródłowego i koloru przeznaczenia oraz dla składowych alfa obu kolorów. Wymaga to wywołania funkcji:
C/C++
void glBlendFuncSeparate( GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha )
której parametry srcRGB i dstRGB określają współczynniki mieszania składowych RGB koloru źródłowego i koloru przeznaczenia, a parametry srcAlpha i dstAlpha współczynniki mieszania składowych alfa tych kolorów. Parametry funkcji glBlendFuncSeparate pozostają takie same jak parametry funkcji glBlendFunc. Analogiczne są także ograniczenia dotyczące stosowania stałej GL_SRC_ALPHA_SATURATE tylko w zakresie parametrów srcRGB i srcAlpha. Podobnie wartość początkowa parametrów srcRGB i srcAlpha wynosi GL_ONE, a parametrów dstRGB i dstAlpha - GL_ZERO. Całość była wcześniej ujęta w rozszerzeniu EXT blend func separate.
Ostatnim wymagającym opisu elementem mechanizmu mieszania kolorów są wymienione w tabeli 2 stałe: GL_CONSTANT_COLOR, GL_ONE_MINUS - CONSTANT COLOR, GL_CONSTANT_ALPHA i GL_ONE_MINUS_CONSTANT_ALPHA, powiązane z wartościami (Rc, Gc, Bc, Ac). Ich użycie umożliwia wybranie dowolnej wartości współczynników mieszania składowych RGBA, a wartości te definiuje się przy użyciu funkcji:
C/C++
void glBlendColor( clampf red, clampf green, clampf blue, clampf alpha )
Wartość początkowa współczynników (Rc, Gc, Bc, Ac) wynosi (0, 0, 0, 0).
Stałe GL_CONSTANT_COLOR, GL_ONE_MINUS_CONSTANT_COLOR, GL_CONSTANT - ALPHA i GL_ONE_MINUS_CONSTANT_ALPHA zostały wprowadzone w wersji 1.2 biblioteki OpenGL jako część składowa wspomnianego wyżej opcjonalnego zbioru funkcji przetwarzania obrazów (rozszerzenie ARB imaging). Wcześniej ich obsługę wprowadzono w rozszerzeniu EXT blend color. Stałe te wprowadzono do obowiązkowej części specyfikacji w wersji 1.4 biblioteki OpenGL.

Programy przykładowe

Pierwszy program przykładowy (plik rownanie_mieszania_kolorow.cpp) przedstawia efekty działania wybranych równań mieszania kolorów. Okno programu (rysunek 1) podzielone jest na dwie części. Lewa część, pokolorowana na niebiesko, zawiera nazwy zastosowanych równań mieszania kolorów. Prawa część okna jest pokolorowana na zielono. Na wysokości każdego napisu określającego nazwę równania mieszania kolorów program rysuje prostokąt czerwony przecinający zarówno niebieską jak i zieloną część okna. Daje to możliwość wizualnego porównania efektów działania poszczególnych równań mieszania kolorów. Dodatkowo na prostokątach wynikowych wypisane są szesnastkowo wartość składowych RGB.

Plik rownanie_mieszania_kolorow.cpp

Rysunek 1. Program Równanie mieszania kolorów
Rysunek 1. Program Równanie mieszania kolorów
C/C++
/*
(c) Janusz Ganczarski
http://www.januszg.hg.pl
JanuszG@enter.net.pl
*/

#include <GL/glut.h>
#include <GL/glext.h>
#ifndef WIN32
#define GLX_GLXEXT_LEGACY
#include <GL/glx.h>
#define wglGetProcAddress glXGetProcAddressARB
#endif
#include <stdlib.h>
#include <stdio.h>
#include "colors.h"

// wskaźnik na funkcję glBlendEquation

PFNGLBLENDEQUATIONEXTPROC glBlendEquation = NULL;

// stałe do obsługi menu podręcznego

enum
{
    EXIT // wyjście
};

// ilość "funkcji" mieszania kolorów

const int func_count = 23;

// nazwy "funkcji" mieszania kolorów

char * func[ func_count ] =
{
    "SOURCE",
    "DEST",
    "ADD",
    "MIN",
    "MAX",
    "SUBTRACT",
    "REVERSE_SUBTRACT",
    "CLEAR",
    "SET",
    "COPY",
    "COPY_INVERTED",
    "NOOP",
    "INVERT",
    "AND",
    "NAND",
    "OR",
    "NOR",
    "XOR",
    "EQUIV",
    "AND_REVERSE",
    "AND_INVERTED",
    "OR_REVERSE",
    "OR_INVERTED"
};

void DrawString( const char * string )
{
    int i;
   
    for( i = 0; string[ i ]; i++ )
         glutBitmapCharacter( GLUT_BITMAP_9_BY_15, string[ i ] );
   
}

// funkcja rysująca napis w wybranym miejscu

void DrawString( GLint x, GLint y, char * string )
{
    // położenie napisu
    glRasterPos2i( x, y );
   
    // wyświetlenie napisu
    int len = strlen( string );
    for( int i = 0; i < len; i++ )
         glutBitmapCharacter( GLUT_BITMAP_9_BY_15, string[ i ] );
   
}

// funkcja generująca scenę 3D

void DisplayScene()
{
    // pobranie szerokości i wysokości obrazu w pikselach
    int width = glutGet( GLUT_WINDOW_WIDTH );
    int height = glutGet( GLUT_WINDOW_HEIGHT );
   
    // zabezpieczenie przed próbą rysowania w zminimalizowanym oknie
    if( width == 0 || height == 0 )
         return;
   
    // kolor tła - zawartość bufora koloru
    glClearColor( 0.0, 1.0, 0.0, 1.0 );
   
    // czyszczenie bufora koloru
    glClear( GL_COLOR_BUFFER_BIT );
   
    // kolor lewej połowy okna
    glColor4fv( Blue );
    glRecti( 0, 0, 180 +( width - 200 ) / 2, height );
   
    // obliczenia kroku współrzędnej pionowej położenia
    // napisów informacyjnych z nazwą "funkcji" mieszania kolorów
    // oraz z kolorami prostokątów wynikowych
    // (15 - to wysokość czcionki użytej w funkcji DrawString)
    int stepy = height / func_count;
    int y = height -( stepy - 15 ) / 2 - 15;
   
    // kolor rysowanych prostokątów
    glColor4fv( Red );
   
    // pierwszy prostokąt rysowany przy wyłączonym mieszniu kolorów
    glRecti( 180, height - 1 * stepy, width - 20, height - 0 * stepy );
   
    // włączenie mieszania kolorów
    glEnable( GL_BLEND );
   
    // współczynniki mieszania kolorów
    glBlendFunc( GL_ONE, GL_ONE );
    glRecti( 180, height - 2 * stepy, width - 20, height - 1 * stepy );
   
    // równanie GL_FUNC_ADD
    glBlendEquation( GL_FUNC_ADD );
    glRectf( 180, height - 3 * stepy, width - 20, height - 2 * stepy );
   
    // równanie GL_MIN
    glBlendEquation( GL_MIN );
    glRectf( 180, height - 4 * stepy, width - 20, height - 3 * stepy );
   
    // równanie GL_MAX
    glBlendEquation( GL_MAX );
    glRectf( 180, height - 5 * stepy, width - 20, height - 4 * stepy );
   
    // równanie GL_FUNC_SUBTRACT
    glBlendEquation( GL_FUNC_SUBTRACT );
    glRectf( 180, height - 6 * stepy, width - 20, height - 5 * stepy );
   
    // równanie GL_FUNC_REVERSE_SUBTRACT
    glBlendEquation( GL_FUNC_REVERSE_SUBTRACT );
    glRectf( 180, height - 7 * stepy, width - 20, height - 6 * stepy );
   
    // współczynniki mieszania kolorów
    glBlendFunc( GL_ONE, GL_ZERO );
   
    // włączenie operacji logicznych na bitach składowych pikseli
    glEnable( GL_COLOR_LOGIC_OP );
   
    // operacja logiczna GL_CLEAR
    glLogicOp( GL_CLEAR );
    glRectf( 180, height - 8 * stepy, width - 20, height - 7 * stepy );
   
    // operacja logiczna GL_SET
    glLogicOp( GL_SET );
    glRectf( 180, height - 9 * stepy, width - 20, height - 8 * stepy );
   
    // operacja logiczna GL_COPY
    glLogicOp( GL_COPY );
    glRectf( 180, height - 10 * stepy, width - 20, height - 9 * stepy );
   
    // operacja logiczna GL_COPY_INVERTED
    glLogicOp( GL_COPY_INVERTED );
    glRectf( 180, height - 11 * stepy, width - 20, height - 10 * stepy );
   
    // operacja logiczna GL_NOOP
    glLogicOp( GL_NOOP );
    glRectf( 180, height - 12 * stepy, width - 20, height - 11 * stepy );
   
    // operacja logiczna GL_INVERT
    glLogicOp( GL_INVERT );
    glRectf( 180, height - 13 * stepy, width - 20, height - 12 * stepy );
   
    // operacja logiczna GL_AND
    glLogicOp( GL_AND );
    glRectf( 180, height - 14 * stepy, width - 20, height - 13 * stepy );
   
    // operacja logiczna GL_NAND
    glLogicOp( GL_NAND );
    glRectf( 180, height - 15 * stepy, width - 20, height - 14 * stepy );
   
    // operacja logiczna GL_OR
    glLogicOp( GL_OR );
    glRectf( 180, height - 16 * stepy, width - 20, height - 15 * stepy );
   
    // operacja logiczna GL_NOR
    glLogicOp( GL_NOR );
    glRectf( 180, height - 17 * stepy, width - 20, height - 16 * stepy );
   
    // operacja logiczna GL_XOR
    glLogicOp( GL_XOR );
    glRectf( 180, height - 18 * stepy, width - 20, height - 17 * stepy );
   
    // operacja logiczna GL_EQUIV
    glLogicOp( GL_EQUIV );
    glRectf( 180, height - 19 * stepy, width - 20, height - 18 * stepy );
   
    // operacja logiczna GL_AND_REVERSE
    glLogicOp( GL_AND_REVERSE );
    glRectf( 180, height - 20 * stepy, width - 20, height - 19 * stepy );
   
    // operacja logiczna GL_AND_INVERTED
    glLogicOp( GL_AND_INVERTED );
    glRectf( 180, height - 21 * stepy, width - 20, height - 20 * stepy );
   
    // operacja logiczna GL_OR_REVERSE
    glLogicOp( GL_OR_REVERSE );
    glRectf( 180, height - 22 * stepy, width - 20, height - 21 * stepy );
   
    // operacja logiczna GL_OR_INVERTED
    glLogicOp( GL_OR_INVERTED );
    glRectf( 180, height - 23 * stepy, width - 20, height - 22 * stepy );
   
    // wyłączenie operacji logicznych na bitach składowych pikseli
    glDisable( GL_COLOR_LOGIC_OP );
   
    // wyłączenie mieszania kolorów
    glDisable( GL_BLEND );
   
    // wyświetlanie napisów informacyjnych: rodzaj "funkcji"
    // mieszania kolorów oraz składowe RGB kolorów prostokątów wynikowych
    GLubyte rgb[ 3 ];
    char string[ 20 ];
    for( int i = 0; i < func_count; i++ )
    {
        // rodzaj "funkcji" mieszania kolorów
        glColor4fv( White );
        DrawString( 10, y, func[ i ] );
       
        // kolor pierwszego prostokąta wynikowego
        glReadPixels( 180, height -( i + 1 ) * stepy, 1, 1, GL_RGB, GL_UNSIGNED_BYTE, rgb );
        sprintf( string, "(%2X,%2X,%2X)", rgb[ 0 ], rgb[ 1 ], rgb[ 2 ] );
       
        // kolor napisu w negatywie - zawsze widoczny
        glColor3ub( 255 - rgb[ 0 ], 255 - rgb[ 1 ], 255 - rgb[ 2 ] );
        DrawString( 210, y, string );
       
        // kolor drugiego prostokąta wynikowego
        glReadPixels( width - 21, height -( i + 1 ) * stepy, 1, 1, GL_RGB, GL_UNSIGNED_BYTE, rgb );
        sprintf( string, "(%2X,%2X,%2X)", rgb[ 0 ], rgb[ 1 ], rgb[ 2 ] );
       
        // kolor napisu w negatywie - zawsze widoczny
        glColor3ub( 255 - rgb[ 0 ], 255 - rgb[ 1 ], 255 - rgb[ 2 ] );
        DrawString( width - 140, y, string );
       
        // współrzędne następnego wiersza napisów
        y -= stepy;
    }
   
    // skierowanie poleceń do wykonania
    glFlush();
   
    // zamiana buforów koloru
    glutSwapBuffers();
}

// zmiana wielkości okna

void Reshape( int width, int height )
{
    // obszar renderingu - całe okno
    glViewport( 0, 0, width, height );
   
    // wybór macierzy rzutowania
    glMatrixMode( GL_PROJECTION );
   
    // macierz rzutowania = macierz jednostkowa
    glLoadIdentity();
   
    // rzutowanie prostokątne
    gluOrtho2D( 0, width, 0, height );
   
    // generowanie sceny 3D
    DisplayScene();
}

// obsługa menu podręcznego

void Menu( int value )
{
    if( value == EXIT )
         exit( 0 );
   
}

// sprawdzenie i przygotowanie obsługi wybranych rozszerzeń

void ExtensionSetup()
{
    // pobranie numeru wersji biblioteki OpenGL
    const char * version =( char * ) glGetString( GL_VERSION );
   
    // odczyt wersji OpenGL
    int major = 0, minor = 0;
    if( sscanf( version, "%d.%d", & major, & minor ) != 2 )
    {
        #ifndef WIN32
        printf( "Błędny format wersji OpenGL\n" );
        #else
       
        printf( "Bledny format wersji OpenGL\n" );
        #endif
       
        exit( 0 );
    }
   
    // sprawdzenie czy jest co najmniej wersja 1.4
    if( major > 1 || minor >= 4 )
    {
        // pobranie wskaźnika funkcji glBlendEquation
        glBlendEquation =
        ( PFNGLBLENDEQUATIONPROC ) wglGetProcAddress( "glBlendEquation" );
    }
    else
   
    // sprawdzenie czy są obsługiwane rozszerzenia:
    // EXT_blend_minmax, EXT_blend_logic_op i EXT_blend_subtract
    if( glutExtensionSupported( "GL_EXT_blend_minmax" ) &&
    glutExtensionSupported( "GL_EXT_blend_logic_op" ) &&
    glutExtensionSupported( "GL_EXT_blend_subtract" ) )
    {
        // pobranie wskaźnika funkcji glBlendEquation
        glBlendEquation =
        ( PFNGLBLENDEQUATIONPROC ) wglGetProcAddress( "glBlendEquationEXT" );
    }
    else
    {
        printf( "Brak rozszerzenia EXT_blend_minmax!\n" );
        printf( "Brak rozszerzenia EXT_blend_logic_op!\n" );
        printf( "Brak rozszerzenia EXT_blend_subtract!\n" );
        exit( 0 );
    }
}

int main( int argc, char * argv[] )
{
    // inicjalizacja biblioteki GLUT
    glutInit( & argc, argv );
   
    // inicjalizacja bufora ramki
    glutInitDisplayMode( GLUT_DOUBLE | GLUT_RGB );
   
    // rozmiary głównego okna programu
    glutInitWindowSize( 500, 600 );
   
    // utworzenie głównego okna programu
    #ifdef WIN32
   
    glutCreateWindow( "Równanie mieszania kolorów" );
    #else
   
    glutCreateWindow( "Rownanie mieszania kolorow" );
    #endif
   
    // dołączenie funkcji generującej scenę 3D
    glutDisplayFunc( DisplayScene );
   
    // dołączenie funkcji wywoływanej przy zmianie rozmiaru okna
    glutReshapeFunc( Reshape );
   
    // menu główne
    glutCreateMenu( Menu );
    #ifdef WIN32
   
    glutAddMenuEntry( "Wyjście", EXIT );
    #else
   
    glutAddMenuEntry( "Wyjscie", EXIT );
    #endif
   
    // określenie przycisku myszki obsługującego menu podręczne
    glutAttachMenu( GLUT_RIGHT_BUTTON );
   
    // sprawdzenie i przygotowanie obsługi wybranych rozszerzeń
    ExtensionSetup();
   
    // wprowadzenie programu do obsługi pętli komunikatów
    glutMainLoop();
    return 0;
}
Drugi i ostatni przykładowy program (plik przezroczystosc.cpp) przedstawia sposób uzyskiwania efektu przezroczystości przy użyciu mieszania kolorów. Program wyświetla cztery kule (patrz rysunek 2), z których dwie są nieprzezroczyste a dwie częściowo przezroczyste. Wykorzystano przy tym definicje materiałów zawarte w pliku materials.h. Obiekty można obracać za pomocą lewego przycisku myszki.
Aby uzyskać prawidłowo działający efekt przezroczystości obiektów trzeba wykonać szereg operacji. Przede wszystkim w pierwszej kolejności rysujemy wszystkie obiekty nieprzezroczyste. Przed przystąpieniem do rysowania obiektów przezroczystych trzeba włączyć mieszanie kolorów oraz ustawić współczynniki mieszania kolorów na GL_SRC_ALPHA dla koloru źródłowego i GL_ONE_MINUS_SRC_ALPHA dla koloru przeznaczenia. Korzystanie z mieszania kolorów przy rysowaniu obiektów trójwymiarowych wymaga także wyłączenia zapisu współrzędnych z do bufora głębokości przy użyciu opisywanej już funkcji glDepthMask. Niestety aby obiekty przezroczyste były prawidłowo renderowane należy je rysować w kolejności od najdalej położonego względem osi Z.
Rysunek 2. Program Przezroczystość - przykładowy ekran
Rysunek 2. Program Przezroczystość - przykładowy ekran
Poprawne posortowanie większej ilości obiektów 3D może być głównym problemem przy korzystaniu z efektu przezroczystości. W przykładowym programie o kolejności rysowania dwóch przezroczystych kul decyduje wartość kąta obrotu wokół osi X (graniczne wartości kątów dobrano eksperymentalnie). Jest to oczywiście rozwiązanie dostosowane do realiów wyświetlanej sceny i nie ma niestety żadnych cech uniwersalności.
Warto jeszcze dodać, że przezroczysta kula rysowana jest w specjalny sposób. Polega to na dwukrotnym rysowaniu kuli - pierwszy raz z wyłączeniem rysowania tylnych stron wielokątów (funkcja glCullFace), i drugi raz z wyłączeniem rysowania przednich stron wielokątów. W połączeniu z włączoną opcją oświetlenia obu stron wielokątów daje to w efekcie prawidłowy obraz bez żadnych niekorzystnych efektów związanych ze sposobem definiowania obiektów przez bibliotekę GLUT.

Plik przezroczystosc.cpp

C/C++
/*
(c) Janusz Ganczarski
http://www.januszg.hg.pl
JanuszG@enter.net.pl
*/

#include <GL/glut.h>
#include <GL/glu.h>
#include <stdlib.h>
#include "materials.h"

// stałe do obsługi menu podręcznego

enum
{
    // obszar renderingu
    FULL_WINDOW, // aspekt obrazu - całe okno
    ASPECT_1_1, // aspekt obrazu 1:1
    EXIT // wyjście
};

// aspekt obrazu

int aspect = FULL_WINDOW;

// rozmiary bryły obcinania

const GLdouble left = - 2.0;
const GLdouble right = 2.0;
const GLdouble bottom = - 2.0;
const GLdouble top = 2.0;
const GLdouble near = 3.0;
const GLdouble far = 7.0;

// kąty obrotu obiektów sceny

GLfloat rotatex = 0.0;
GLfloat rotatey = 0.0;

// wskaźnik naciśnięcia lewego przycisku myszki

int button_state = GLUT_UP;

// położenie kursora myszki

int button_x, button_y;

// identyfikatory list wyświetlania

GLint GOLD_SPHERE;
GLint RUBY_SPHERE;

// funkcja generująca scenę 3D

void Display()
{
    // kolor tła - zawartość bufora koloru
    glClearColor( 1.0, 1.0, 1.0, 1.0 );
   
    // czyszczenie bufora koloru i bufora głębokości
    glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
   
    // wybór macierzy modelowania
    glMatrixMode( GL_MODELVIEW );
   
    // macierz modelowania = macierz jednostkowa
    glLoadIdentity();
   
    // włączenie testu bufora głębokości
    glEnable( GL_DEPTH_TEST );
   
    // włączenie oświetlenia
    glEnable( GL_LIGHTING );
   
    // włączenie światła GL_LIGHT0 z parametrami domyślnymi
    glEnable( GL_LIGHT0 );
   
    // oświetlenie tylko przednich stron wielokątów
    glLightModelf( GL_LIGHT_MODEL_TWO_SIDE, GL_FALSE );
   
    // przesunięcie układu współrzędnych obiektu do środka bryły odcinania
    glTranslatef( 0, 0, -( near + far ) / 2 );
   
    // obroty obiektów sceny
    glRotatef( rotatex, 1.0, 0.0, 0.0 );
    glRotatef( rotatey, 0.0, 1.0, 0.0 );
   
    // odłożenie macierzy modelowania na stos
    glPushMatrix();
   
    // przesunięcie pierwszej kuli
    glTranslatef( 1.2, 0.0, 0.0 );
   
    // pierwsza kula - złota
    glCallList( GOLD_SPHERE );
   
    // zdjęcie macierzy modelowania ze stosu
    glPopMatrix();
   
    // odłożenie macierzy modelowania na stos
    glPushMatrix();
   
    // przesunięcie drugiej kuli
    glTranslatef( - 1.2, 0.0, 0.0 );
   
    // druga kula - złota
    glCallList( GOLD_SPHERE );
   
    // zdjęcie macierzy modelowania ze stosu
    glPopMatrix();
   
    // oświetlenie przednich i tylnych stron wielokątów
    glLightModelf( GL_LIGHT_MODEL_TWO_SIDE, GL_TRUE );
   
    // włączenie mieszania kolorów
    glEnable( GL_BLEND );
   
    // współczynniki mieszania kolorów
    glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
   
    // wyłączenie zapisu współrzędnych z do bufora głębokości
    glDepthMask( GL_FALSE );
   
    // odłożenie macierzy modelowania na stos
    glPushMatrix();
   
    // obliczenie kąta obrotu wokół ox X,
    // wartość kąta ograniczona do przedziału [0,360]
    int anglex =( int ) rotatex % 360;
    if( anglex < 0 )
         anglex += 360;
   
    // przesunięcie trzeciej kuli uzależnione od jej położenia
    // względem czwartej kuli; kąty 50° i 130° dobrane eksperymentalnie
    if( anglex > 50 && anglex < 130 )
         glTranslatef( 0.0, - 1.2, 0.0 );
    else
         glTranslatef( 0.0, 1.2, 0.0 );
   
    // trzecia kula - rubinowa
    glCallList( RUBY_SPHERE );
   
    // zdjęcie macierzy modelowania ze stosu
    glPopMatrix();
   
    // odłożenie macierzy modelowania na stos
    glPushMatrix();
   
    // przesunięcie czwartej kuli uzależnione od jej położenia
    // względem trzeciej kuli; kąty 50° i 130° dobrane eksperymentalnie
    if( anglex > 50 && anglex < 130 )
         glTranslatef( 0.0, 1.2, 0.0 );
    else
         glTranslatef( 0.0, - 1.2, 0.0 );
   
    // czwarta kula - rubinowa
    glCallList( RUBY_SPHERE );
   
    // zdjęcie macierzy modelowania ze stosu
    glPopMatrix();
   
    // włączenie zapisu współrzędnych z do bufora głębokości
    glDepthMask( GL_TRUE );
   
    // skierowanie poleceń do wykonania
    glFlush();
   
    // zamiana buforów koloru
    glutSwapBuffers();
}

// zmiana wielkości okna

void Reshape( int width, int height )
{
    // obszar renderingu - całe okno
    glViewport( 0, 0, width, height );
   
    // wybór macierzy rzutowania
    glMatrixMode( GL_PROJECTION );
   
    // macierz rzutowania = macierz jednostkowa
    glLoadIdentity();
   
    // parametry bryły obcinania
    if( aspect == ASPECT_1_1 )
    {
        // wysokość okna większa od wysokości okna
        if( width < height && width > 0 )
             glFrustum( left, right, bottom * height / width, top * height / width, near, far );
        else
       
        // szerokość okna większa lub równa wysokości okna
        if( width >= height && height > 0 )
             glFrustum( left * width / height, right * width / height, bottom, top, near, far );
       
    }
    else
         glFrustum( left, right, bottom, top, near, far );
   
    // generowanie sceny 3D
    Display();
}

// obsługa przycisków myszki

void MouseButton( int button, int state, int x, int y )
{
    if( button == GLUT_LEFT_BUTTON )
    {
        // zapamiętanie stanu lewego przycisku myszki
        button_state = state;
       
        // zapamiętanie położenia kursora myszki
        if( state == GLUT_DOWN )
        {
            button_x = x;
            button_y = y;
        }
    }
}

// obsługa ruchu kursora myszki

void MouseMotion( int x, int y )
{
    if( button_state == GLUT_DOWN )
    {
        rotatey += 30 *( right - left ) / glutGet( GLUT_WINDOW_WIDTH ) *( x - button_x );
        button_x = x;
        rotatex -= 30 *( top - bottom ) / glutGet( GLUT_WINDOW_HEIGHT ) *( button_y - y );
        button_y = y;
        glutPostRedisplay();
    }
}

// obsługa menu podręcznego

void Menu( int value )
{
    switch( value )
    {
        // obszar renderingu - całe okno
    case FULL_WINDOW:
        aspect = FULL_WINDOW;
        Reshape( glutGet( GLUT_WINDOW_WIDTH ), glutGet( GLUT_WINDOW_HEIGHT ) );
        break;
       
        // obszar renderingu - aspekt 1:1
    case ASPECT_1_1:
        aspect = ASPECT_1_1;
        Reshape( glutGet( GLUT_WINDOW_WIDTH ), glutGet( GLUT_WINDOW_HEIGHT ) );
        break;
       
        // wyjście
    case EXIT:
        exit( 0 );
    }
}

// utworzenie list wyświetlania

void GenerateDisplayLists()
{
    // generowanie identyfikatora pierwszej listy wyświetlania
    GOLD_SPHERE = glGenLists( 1 );
   
    // pierwsza lista wyświetlania - złota kula
    glNewList( GOLD_SPHERE, GL_COMPILE );
   
    // ustawnienie materiału - obiekt nieprzezroczysty
    glMaterialfv( GL_FRONT_AND_BACK, GL_AMBIENT, GoldAmbient );
    glMaterialfv( GL_FRONT_AND_BACK, GL_DIFFUSE, GoldDiffuse );
    glMaterialfv( GL_FRONT_AND_BACK, GL_SPECULAR, GoldSpecular );
    glMaterialf( GL_FRONT_AND_BACK, GL_SHININESS, GoldShininess );
   
    // narysowanie nieprzezroczystej kuli
    glutSolidSphere( 0.8, 20, 20 );
   
    // koniec pierwszej listy wyświetlania
    glEndList();
   
    // generowanie identyfikatora drugiej listy wyświetlania
    RUBY_SPHERE = glGenLists( 1 );
   
    // druga lista wyświetlania - rubinowa kula
    glNewList( RUBY_SPHERE, GL_COMPILE );
   
    // ustawnienie materiału - obiekt przezroczysty
    glMaterialfv( GL_FRONT_AND_BACK, GL_AMBIENT, RubyAmbient );
    glMaterialfv( GL_FRONT_AND_BACK, GL_DIFFUSE, RubyDiffuse );
    glMaterialfv( GL_FRONT_AND_BACK, GL_SPECULAR, RubySpecular );
    glMaterialf( GL_FRONT_AND_BACK, GL_SHININESS, RubyShininess );
   
    // włączenie renderingu wybranej strony wielokąta
    glEnable( GL_CULL_FACE );
   
    // rysowanie tylko przedniej strony wielokątów
    glCullFace( GL_BACK );
   
    // narysowanie pierwszej przezroczystej kuli
    glutSolidSphere( 0.8, 20, 20 );
   
    // rysowanie tylko tylnej strony wielokątów
    glCullFace( GL_FRONT );
   
    // narysowanie drugiej przezroczystej kuli
    glutSolidSphere( 0.8, 20, 20 );
   
    // wyłączenie renderingu wybranej strony wielokąta
    glDisable( GL_CULL_FACE );
   
    // koniec drugiej listy wyświetlania
    glEndList();
}

int main( int argc, char * argv[] )
{
    // inicjalizacja biblioteki GLUT
    glutInit( & argc, argv );
   
    // inicjalizacja bufora ramki
    glutInitDisplayMode( GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH );
   
    // rozmiary głównego okna programu
    glutInitWindowSize( 500, 500 );
   
    // utworzenie głównego okna programu
    #ifdef WIN32
   
    glutCreateWindow( "Przezroczystość" );
    #else
   
    glutCreateWindow( "Przezroczystosc" );
    #endif
   
    // dołączenie funkcji generującej scenę 3D
    glutDisplayFunc( Display );
   
    // dołączenie funkcji wywoływanej przy zmianie rozmiaru okna
    glutReshapeFunc( Reshape );
   
    // obsługa przycisków myszki
    glutMouseFunc( MouseButton );
   
    // obsługa ruchu kursora myszki
    glutMotionFunc( MouseMotion );
   
    // utworzenie menu podręcznego
    glutCreateMenu( Menu );
   
    // utworzenie podmenu - Aspekt obrazu
    int MenuAspect = glutCreateMenu( Menu );
    #ifdef WIN32
   
    glutAddMenuEntry( "Aspekt obrazu - całe okno", FULL_WINDOW );
    #else
   
    glutAddMenuEntry( "Aspekt obrazu - cale okno", FULL_WINDOW );
    #endif
   
    glutAddMenuEntry( "Aspekt obrazu 1:1", ASPECT_1_1 );
   
    // menu główne
    glutCreateMenu( Menu );
    glutAddSubMenu( "Aspekt obrazu", MenuAspect );
    #ifdef WIN32
   
    glutAddMenuEntry( "Wyjście", EXIT );
    #else
   
    glutAddMenuEntry( "Wyjscie", EXIT );
    #endif
   
    // określenie przycisku myszki obsługującej menu podręczne
    glutAttachMenu( GLUT_RIGHT_BUTTON );
   
    // utworzenie list wyświetlania
    GenerateDisplayLists();
   
    // wprowadzenie programu do obsługi pętli komunikatów
    glutMainLoop();
    return 0;
}
Poprzedni dokument Następny dokument
Listy wyświetlania Przetwarzanie obrazów