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

Systemy cząstek

[lekcja] Rozdział 25. Podstawy budowy systemów cząstek, rozszerzona geometria punktów (ARB_point_parameters), sprajty punktowe (ARB_point_sprite); program przykładowy animujący śnieg.
Systemy cząstek (ang. particle systems) wykorzystywane są głównie do uzyskiwania różnorodnych efektów atmosferycznych, takich jak deszcz, śnieg lub mgła. Pomijając kwestie fizyczne, systemy cząstek można zbudować w oparciu o punkty, odcinki lub czworokąty pokryte teksturą. Możliwości biblioteki OpenGL w tym zakresie rozszerzano dwukrotnie. W wersji 1.4 dodano obsługę rozszerzonej geometrii punktów, a w wersji 2.0 wprowadzono sprajty punktowe (ang. point sprite).

Rozszerzona geometria punktów

Punkty opisywaliśmy już dwukrotnie. Jednak z punktu widzenia systemu cząstek punkty w bibliotece OpenGL mają jedną zasadniczą wadę. Ich wielkość nie jest w żaden sposób uzależniona od położenia w przestrzeni 3D, a w szczególności od odległości punktu od obserwatora. Tę wadę usunięto w wersji 1.4 biblioteki OpenGL, a wcześniej w rozszerzeniach ARB point parameters, EXT point parameters i SGIS point parameters.
Kontrolę nad rozszerzoną geometrią punktów umożliwiają funkcje z grupy glPointParameter:
C/C++
void glPointParameterf( GLenum pname, GLfloat param )
void glPointParameterx( GLenum pname, GLfixed param )
void glPointParameterfv( GLenum pname, const GLfloat * params )
void glPointParameterxv( GLenum pname, const GLfixed * params )
gdzie pname określa rodzaj definiowanego parametru i może przyjąć jedną z poniższych wartości:
  • GL_POINT_SIZE_MIN - minimalna wielkość punktu po przekształceniach geometrycznych; wartość początkowa 0,
  • GL_POINT_SIZE_MAX - maksymalna wielkość punktu po przekształceniach geometrycznych; wartość początkowa jest maksymalną wielkością punktu z aliasingiem i antyaliasingiem obsługiwaną przez daną implementację OpenGL,
  • GL_POINT_FADE_THRESHOLD_SIZE - wartość progowa używana przy włączonym wielopróbkowaniu do zmiany wielkości punktu oraz zmiany składowej alfa koloru punktu; wartość początkowa 1,
  • GL_POINT_DISTANCE_ATTENUATION - współczynniki a, b i c równania określającego rozmiar punktu po przekształceniach geometrycznych.
Wielkość punktu po przekształceniach geometrycznych określa równanie:
gdzie size jest wielkością punktu określoną przy użyciu funkcji glPointSize, d odległością punktu od obserwatora, a clamp wynikiem obcięcia wielkości punktu do przedziału określonego stałymi GL_POINT_SIZE_MIN i GL_POINT_SIZE_MAX. Przy wartościach domyślnych współczynników równania wynoszących a = 1, b = 0 i c = 0, wielkość punktu jest równa wielkości określonej funkcją glPointSize.
Jak już napisaliśmy przy włączonym wielopróbkowaniu stała GL_POINT_FADE_THRESHOLD_SIZE jest używana do zmiany wielkości punktu oraz zmiany składowej alfa koloru punktu. Wielkość punktu określa funkcja width, a współczynnik mnożenia składowej alfa określa funkcja fade:

Sprajty punktowe

Sprajty lub duszki punktowe (ang. point sprite) zostały wprowadzone w wersji 2.0 biblioteki OpenGL w oparciu o rozszerzenia ARB point sprite i NV point sprite. W uproszczeniu technika ta sprowadza się do obsługi tekstury dwuwymiarowej opartej na pojedynczym wierzchołku - punkcie. Pozwala to na znaczne przyspieszenie generowania grafiki, bowiem karta graficzna przetwarza jeden zamiast typowo czterech wierzchołków.
Sprajty punktowe są domyślnie wyłączone. Ich aktywacja wymaga wywołania funkcji glEnable z parametrem GL_POINT_SPRITE. Włączenie sprajtów punktowych powoduje jednocześnie deaktywację antyaliasingu punktów.
Wraz z techniką sprajtów punktowych wprowadzono środowisko tekstur GL_POINT_SPRITE, o którym już wspominaliśmy opisując tekstury. Środowisko to dopuszcza jeden parametr pname funkcji z grupy glTexEnv, który oznaczony jest stałą GL_COORD_REPLACE. Parametr ten określa czy współrzędne tekstur mają być zastępowane współrzędnymi sprajtów punktowych (wartość GL_TRUE). Domyślnie jest to, podobnie jak i sprajty punktowe, wyłączone (wartość GL_FALSE).
Ponadto rozszerzono dopuszczalne wartości parametru pname funkcji z grupy glPointParameter o stałą GL_POINT_SPRITE_COORD_ORIGIN, która określa czy lewy górny róg duszka ma pokrywać się z lewym górnym rogiem tekstury (wartość GL_UPPER_LEFT) czy też z prawym dolnym rogiem tekstury (wartość GL_LOWER_LEFT). Domyślnie OpenGL przyjmuje wartość GL_UPPER_LEFT.

Program przykładowy

Typowy system cząstek zawiera informacje o ilości cząstek, ich położeniu i innych aspektach fizycznych. W naszym przykładowym programie system cząstek jest maksymalnie uproszczony i całkowicie oddzielony od graficznej reprezentacji cząstek. Pojedynczą cząstkę - płatek śniegu - reprezentuje struktura SnowFlake, która zwiera informację o położeniu cząstki (pola x, y i z) oraz pole logiczne active o jej aktywności. Płatki śniegu generowane są losowo a szybkość ich opadania także jest losowo zmieniana. Cała ta warstwa fizyczna systemu cząstek zaimplementowana jest w funkcji timera. Oczywiście funkcja ta zapewnia także stałą szybkość opadania śniegu, ograniczoną jedynie wydajnością karty graficznej.
Rysunek 1. Program Śnieg - klasyczne punkty
Rysunek 1. Program Śnieg - klasyczne punkty
Program wyświetla śnieg korzystając z czterech metod. Pierwsza stosuje punkty z antyaliasingiem, przy czym wielkość punktu jest prostą (liniową) funkcją wartości jego współrzędnej z. Komputer Autora pozwala na rendering 20.000 punktów z szybkością około 10 ramek na sekundę (patrz rysunek 1). Druga z zastosowanych metod korzysta z rozszerzonej geometrii punktów i wizualnie praktycznie nie różni się od efektów uzyskanych pierwszą metodą (rysunek 1). Kilkukrotny jest za to przyrost prędkości renderingu (około 65 ramek na sekundę), który dodatkowo ogranicza tempo wywołań funkcji timera.
Druga metoda ma jeszcze jedną zaletę - umożliwia wykorzystanie tablic wierzchołków. Nie było to możliwe przy rysowaniu klasycznych punktów, bowiem jak pamiętamy wielkość punktu może być zmieniana tylko przed wywołaniem funkcji glBegin z parametrem GL_POINTS.
Rysunek 2. Program Śnieg - punkty z rozszerzoną geometrią
Rysunek 2. Program Śnieg - punkty z rozszerzoną geometrią
Dwie ostatnie metody stosują teksturę z rysunkiem płatka śniegu (rysunek 3). Rysunek ten, a właściwie zdjęcie, pochodzi ze wspaniałej kolekcji autorstwa Wilsona Bentley’a której niewielki fragment jest dostępny pod adresem http://snowflakebentley.com/. Rysowane 20.000 płatków śniegu utworzonych z pokrytych teksturą czworokątów na komputerze Autora odbywał się z szybkością około 46 ramek na sekundę (rysunek 4). Analogiczny test z użyciem sprajtów punktowych pozwala na zauważalne przyspieszenie. Scena rysowana jest z szybkością około 65 FPS (rysunek 5) i ponownie jest to szybkość ograniczona wywołaniami funkcji timera.
Rysunek 3. Zdjęcie przykładowego płatka śniegu
Rysunek 3. Zdjęcie przykładowego płatka śniegu
Przy sprajtach punktowych program ponownie korzysta z tablic wierzchołków, co oczywiście jest możliwe także w przypadku tradycyjnych tekstur, ale wymagałoby radykalnego zwiększenia ilości informacji przechowywanych przez system cząstek.

Plik snieg.cpp

Rysunek 4. Program Śnieg - tekstury oparte na czworkątach
Rysunek 4. Program Śnieg - tekstury oparte na czworkątach
Rysunek 5. Program Śnieg - sprajty punktowe
Rysunek 5. Program Śnieg - sprajty punktowe
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 <stdio.h>
#include <stdlib.h>
#include <time.h>
#include "colors.h"
#include "targa.h"

// wskaźnik na funkcję glWindowPos2i

PFNGLWINDOWPOS2IPROC glWindowPos2i = NULL;

// wskaźnik na funkcje glPointParameterf i glPointParameterfv

PFNGLPOINTPARAMETERFPROC glPointParameterf = NULL;
PFNGLPOINTPARAMETERFVPROC glPointParameterfv = NULL;

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

enum
{
    STD_POINTS, // śnieg - punkty
    GEOMETRIC_POINTS, // śnieg - punkty z rozszerzoną geometrią
    TEXTURE, // śnieg - tekstura
    POINT_SPRITE, // śnieg - sprajty punktowe
    FULL_WINDOW, // aspekt obrazu - całe okno
    ASPECT_1_1, // aspekt obrazu 1:1
    EXIT // wyjście
};

// aspekt obrazu

int aspect = FULL_WINDOW;

// usuniŕcie definicji makr near i far

#ifdef near
#undef near
#endif
#ifdef far
#undef far
#endif

// 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;

// ilość animowanych płatków sniegu

int flake_count = 1000;

// maksymalna ilość animowanych płatków śniegu

const int MAX_FLAKE_COUNT = 20000;

// struktura z informacjami o płatku śniegu

struct SnowFlake
{
    GLfloat x, y, z;
    bool active;
};

// tablica ze wszystkim płatkami śniegu

SnowFlake SnowFlakes[ MAX_FLAKE_COUNT ];

// rodzaj generowanego śniegu

int snow_mode = STD_POINTS;

// domyślne współczynniki równania przekształcenia wielkości punktów

GLfloat std_attenation[ 3 ] =
{
    1.0, 0.0, 0.0
};

// współczynniki równania przekształcenia wielkości punktów

GLfloat quad_attenation[ 3 ] =
{
    0.0, 0.0, 0.2
};

// identyfikator tekstury

GLuint SNOW_FLAKE;

// licznik ramek (FPS)

int frames = 0;

// licznik czasu

long start_time = 0;

// tablica znaków ze wartością FPS

char time_string[ 100 ] = "FPS:";

// funkcja rysująca napis w wybranym miejscu
// (wersja korzystająca z funkcji glWindowPos2i)

void DrawString( GLint x, GLint y, char * string )
{
    // położenie napisu
    glWindowPos2i( 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()
{
    // licznik czasu
    if( !frames++ )
         start_time = clock();
   
    // kolor tła - zawartość bufora koloru
    glClearColor( DarkBlue[ 0 ], DarkBlue[ 1 ], DarkBlue[ 2 ], 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();
   
    // przesunięcie układu współrzędnych do środka bryły odcinania
    glTranslatef( 0, 0, -( near + far ) / 2 );
   
    // włączenie testu bufora głębokości
    glEnable( GL_DEPTH_TEST );
   
    // śnieg - punkty
    if( snow_mode == STD_POINTS )
    {
        // włączenie antyaliasingu punktów
        glEnable( GL_POINT_SMOOTH );
       
        // kolor punktów
        glColor4fv( Snow );
       
        // narysowanie wszystich płatków śniegu
        for( int i = 0; i < flake_count; i++ )
        {
            // rozmiar punktów uzależniony w liniowy sposób
            // od wielkości współrzednej z
            glPointSize( 3 + SnowFlakes[ i ].z );
            glBegin( GL_POINTS );
            glVertex3f( SnowFlakes[ i ].x, SnowFlakes[ i ].y, SnowFlakes[ i ].z );
            glEnd();
        }
       
        // wyłączenie antyaliasingu punktów
        glDisable( GL_POINT_SMOOTH );
    }
   
    // śnieg - punkty z rozszerzoną geometrią
    if( snow_mode == GEOMETRIC_POINTS )
    {
        // minimalna wielkość punktu po przekształceniach geometrycznych
        glPointParameterf( GL_POINT_SIZE_MIN, 1.0 );
       
        // maksymalna wielkość punktu po przekształceniach geometrycznych
        glPointParameterf( GL_POINT_SIZE_MAX, 10.0 );
       
        // współczynniki równania przekształcenia wielkości punktów
        glPointParameterfv( GL_POINT_DISTANCE_ATTENUATION, quad_attenation );
       
        // bazowa wielkość punktu
        glPointSize( 10.0 );
       
        // kolor punktów
        glColor4fv( Snow );
       
        // włączenie antyaliasingu punktów
        glEnable( GL_POINT_SMOOTH );
       
        // włączenie tablic wierzchołków
        glEnableClientState( GL_VERTEX_ARRAY );
       
        // zdefiniowanie danych tablic wierzchołków
        glVertexPointer( 3, GL_FLOAT, sizeof( SnowFlake ), SnowFlakes );
       
        // narysowanie danych zawartych w tablicach
        glDrawArrays( GL_POINTS, 0, flake_count );
       
        // wyłączenie tablic wierzchołków
        glDisableClientState( GL_VERTEX_ARRAY );
       
        // domyślne współczynniki równania przekształcenia wielkości punktów
        glPointParameterfv( GL_POINT_DISTANCE_ATTENUATION, std_attenation );
       
        // wyłączenie antyaliasingu punktów
        glDisable( GL_POINT_SMOOTH );
    }
   
    // śnieg - tekstura
    if( snow_mode == TEXTURE )
    {
        // włączenie tekstur 2D
        glEnable( GL_TEXTURE_2D );
       
        // dowiązanie stanu tekstury
        glBindTexture( GL_TEXTURE_2D, SNOW_FLAKE );
       
        // włączenie testowania kanału alfa
        glEnable( GL_ALPHA_TEST );
        glAlphaFunc( GL_GREATER, 0.2 );
       
        // ustawienie parametów środowiska tekstur
        glTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE );
       
        // czworokąty
        glBegin( GL_QUADS );
       
        // narysowanie wszystich płatków śniegu
        for( int i = 0; i < flake_count; i++ )
        {
            glTexCoord2f( 1.0, 1.0 );
            glVertex3f( SnowFlakes[ i ].x + 0.04, SnowFlakes[ i ].y + 0.04, SnowFlakes[ i ].z );
            glTexCoord2f( 0.0, 1.0 );
            glVertex3f( SnowFlakes[ i ].x - 0.04, SnowFlakes[ i ].y + 0.04, SnowFlakes[ i ].z );
            glTexCoord2f( 0.0, 0.0 );
            glVertex3f( SnowFlakes[ i ].x - 0.04, SnowFlakes[ i ].y - 0.04, SnowFlakes[ i ].z );
            glTexCoord2f( 1.0, 0.0 );
            glVertex3f( SnowFlakes[ i ].x + 0.04, SnowFlakes[ i ].y - 0.04, SnowFlakes[ i ].z );
        }
        glEnd();
       
        // wyłączenie testowania kanału alfa
        glDisable( GL_ALPHA_TEST );
       
        // wyłączenie tekstur 2D
        glDisable( GL_TEXTURE_2D );
    }
   
    // śnieg - sprajty punktowe
    if( snow_mode == POINT_SPRITE )
    {
        // minimalna wielkość punktu po przekształceniach geometrycznych
        glPointParameterf( GL_POINT_SIZE_MIN, 1.0 );
       
        // maksymalna wielkość punktu po przekształceniach geometrycznych
        glPointParameterf( GL_POINT_SIZE_MAX, 10.0 );
       
        // współczynniki równania przekształcenia wielkości punktów
        glPointParameterfv( GL_POINT_DISTANCE_ATTENUATION, quad_attenation );
       
        // bazowa wielkość punktu
        glPointSize( 15.0 );
       
        // włączenie teksturowania 2D
        glEnable( GL_TEXTURE_2D );
       
        // dowiązanie stanu tekstury
        glBindTexture( GL_TEXTURE_2D, SNOW_FLAKE );
       
        // włączenie testowania kanału alfa
        glEnable( GL_ALPHA_TEST );
        glAlphaFunc( GL_GREATER, 0.2 );
       
        // ustawienie parametów środowiska tekstur
        glTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE );
       
        // ustawienie parametrów środowska sprajtów punktowych
        glTexEnvf( GL_POINT_SPRITE, GL_COORD_REPLACE, GL_TRUE );
       
        // włączenie sprajtów punktowych
        glEnable( GL_POINT_SPRITE );
       
        // włączenie tablic wierzchołków
        glEnableClientState( GL_VERTEX_ARRAY );
       
        // zdefiniowanie danych tablic wierzchołków
        glVertexPointer( 3, GL_FLOAT, sizeof( SnowFlake ), SnowFlakes );
       
        // narysowanie danych zawartych w tablicach
        glDrawArrays( GL_POINTS, 0, flake_count );
       
        // wyłączenie tablic wierzchołków
        glDisableClientState( GL_VERTEX_ARRAY );
       
        // domyślne współczynniki równania przekształcenia wielkości punktów
        glPointParameterfv( GL_POINT_DISTANCE_ATTENUATION, std_attenation );
       
        // wyłączenie sprajtów punktowych
        glDisable( GL_POINT_SPRITE );
       
        // wyłączenie testowania kanału alfa
        glDisable( GL_ALPHA_TEST );
       
        // wyłączenie teksturowania 2D
        glDisable( GL_TEXTURE_2D );
    }
   
    // wyłączenie testu bufora głębokości
    glDisable( GL_DEPTH_TEST );
   
    // ilość wyświetlanych płatków śniegu
    glColor3fv( Yellow );
    char str[ 100 ];
    sprintf( str, "PARTICLES COUNT = %i", flake_count );
    DrawString( 2, 2, str );
   
    // komunikat o ilości ramek rysowanych na sekundę (FPS)
    if( frames == 50 )
    {
        frames = 0;
        sprintf( time_string, "FPS: %i",( int )( 50 * CLOCKS_PER_SEC /( float )( clock() - start_time ) ) );
    }
    DrawString( 2, 16, time_string );
   
    // skierowanie poleceń do wykonania
    glFlush();
   
    // zamiana buforów koloru
    glutSwapBuffers();
}

// funkcja timera

void Timer( int value )
{
    // określenie zarodka dla  ciągu liczb pseudolosowych
    srand( time( NULL ) );
   
    // aktualizacja parametrów płatków śniegu
    for( int i = 0; i < flake_count; i++ )
   
    // generowanie nowych płatków śniegu
    if( SnowFlakes[ i ].active == false )
    {
        SnowFlakes[ i ].x = 2 *( rand() /( float ) RAND_MAX ) *( right - left ) - 2 * right;
        SnowFlakes[ i ].y =( rand() /( float ) RAND_MAX ) *( top - bottom ) + top;
        SnowFlakes[ i ].z =( rand() /( float ) RAND_MAX ) *( far - near );
        SnowFlakes[ i ].active = true;
    }
   
    // realizacja opadania płatków śniegu i ich usuwania
    else
    {
        SnowFlakes[ i ].y -= 0.005 + rand() /( float ) RAND_MAX / 200;
        SnowFlakes[ i ].x += 0.001 - rand() /( float ) RAND_MAX / 200;
        SnowFlakes[ i ].z += 0.001 - rand() /( float ) RAND_MAX / 200;
        if( SnowFlakes[ i ].y < bottom )
             SnowFlakes[ i ].active = false;
       
    }
   
    // wyświetlenie sceny
    DisplayScene();
   
    // następne wywołanie funkcji timera
    glutTimerFunc( 10, Timer, 0 );
}

// 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
    DisplayScene();
}

// obsługa klawiatury

void Keyboard( unsigned char key, int x, int y )
{
    switch( key )
    {
        // klawisz +
    case '+':
        if( flake_count < MAX_FLAKE_COUNT )
             flake_count += 1000;
       
        break;
       
        // klawisz -
    case '-':
        if( flake_count > 1000 )
             flake_count -= 1000;
       
        // deaktywacja usuniętych płatków
        for( int i = flake_count; i < 1000; i++ )
             SnowFlakes[ i ].active = false;
       
        break;
    }
   
    // narysowanie sceny
    DisplayScene();
}

// obsługa menu podręcznego

void Menu( int value )
{
    switch( value )
    {
        // rodzaj śniegu
    case STD_POINTS:
    case GEOMETRIC_POINTS:
    case TEXTURE:
    case POINT_SPRITE:
        snow_mode = value;
        DisplayScene();
        break;
       
        // 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 GenerateTextures()
{
    // zmienne użyte przy obsłudze plików TARGA
    GLsizei width, height;
    GLenum format, type;
    GLvoid * pixels;
   
    // tryb upakowania bajtów danych tekstury
    glPixelStorei( GL_UNPACK_ALIGNMENT, 1 );
   
    // wczytanie tekstury z płatkiem śniegu
    // źródło: http://snowflakebentley.com/
    GLboolean error = load_targa( "snowflakes.tga", width, height, format, type, pixels );
   
    // błąd odczytu pliku
    if( error == GL_FALSE )
    {
        printf( "Niepoprawny odczyt pliku snowflakes.tga" );
        exit( 0 );
    }
   
    // utworzenie bufora pomocniczego na obraz w formacie RGBA
    unsigned char * rgba_pixels = new unsigned char[ width * height * 4 ];
    unsigned char * rgb_pixels =( unsigned char * ) pixels;
   
    // utworzenie obrazu w formacie RGBA
    for( int i = 0; i < width * height; i++ )
    {
        rgba_pixels[ 4 * i + 0 ] = rgb_pixels[ 3 * i + 0 ];
        rgba_pixels[ 4 * i + 1 ] = rgb_pixels[ 3 * i + 1 ];
        rgba_pixels[ 4 * i + 2 ] = rgb_pixels[ 3 * i + 2 ];
       
        // wartość skłądowej alfa uzalezniona od jasności piksela
        if( rgb_pixels[ 3 * i + 0 ] + rgb_pixels[ 3 * i + 1 ] + rgb_pixels[ 3 * i + 2 ] < 10 )
             rgba_pixels[ 4 * i + 3 ] = 0;
        else
             rgba_pixels[ 4 * i + 3 ] = 255;
       
    }
   
    // utworzenie identyfikatora tekstury
    glGenTextures( 1, & SNOW_FLAKE );
   
    // dowiązanie stanu tekstury
    glBindTexture( GL_TEXTURE_2D, SNOW_FLAKE );
   
    // filtr pomniejszający
    glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR );
   
    // utworzenie tekstury wraz z mipmapami
    gluBuild2DMipmaps( GL_TEXTURE_2D, GL_RGBA, width, height, GL_BGRA, type, rgba_pixels );
   
    // porządki
    delete[]( unsigned char * ) pixels;
    delete[] rgba_pixels;
}

// 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 )
    {
        #ifdef 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 wybranej funkcji OpenGL 1.4
        glWindowPos2i =( PFNGLWINDOWPOS2IPROC ) wglGetProcAddress( "glWindowPos2i" );
    }
    else
    // sprawdzenie czy jest obsługiwane rozszerzenie ARB_window_pos
    if( glutExtensionSupported( "GL_ARB_window_pos" ) )
    {
        // pobranie wskaźnika wybranej funkcji rozszerzenia ARB_window_pos
        glWindowPos2i =( PFNGLWINDOWPOS2IPROC ) wglGetProcAddress
        ( "glWindowPos2iARB" );
    }
    else
    {
        printf( "Brak rozszerzenia ARB_window_pos!\n" );
        exit( 0 );
    }
   
    // sprawdzenie czy jest co najmniej wersja 1.4
    if( major > 1 || minor >= 4 )
    {
        // pobranie wskaźników wybranych funkcji OpenGL 1.4
        glPointParameterf =
        ( PFNGLPOINTPARAMETERFPROC ) wglGetProcAddress( "glPointParameterf" );
        glPointParameterfv =
        ( PFNGLPOINTPARAMETERFVPROC ) wglGetProcAddress( "glPointParameterfv" );
    }
    else
    // sprawdzenie czy jest obsługiwane rozszerzenie ARB_point_parameters
    if( glutExtensionSupported( "GL_ARB_point_parameters" ) )
    {
        // pobranie wskaźników wybranych funkcji rozszerzenia ARB_point_parameters
        glPointParameterf =
        ( PFNGLPOINTPARAMETERFPROC ) wglGetProcAddress( "glPointParameterf" );
        glPointParameterfv =
        ( PFNGLPOINTPARAMETERFVPROC ) wglGetProcAddress( "glPointParameterfv" );
    }
    else
    {
        printf( "Brak rozszerzenia ARB_point_parameters!\n" );
        exit( 0 );
    }
   
    // sprawdzenie czy jest co najmniej wersja 2.0 OpenGL lub
    // czy jest obsługiwane rozszerzenie ARB_point_sprite
    if( !( major >= 2 ) && !glutExtensionSupported( "GL_ARB_point_sprite" ) )
    {
        printf( "Brak rozszerzenia GL_ARB_point_sprite!\n" );
        exit( 0 );
    }
}

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( "Śnieg" );
    #else
   
    glutCreateWindow( "Snieg" );
    #endif
   
    // dołączenie funkcji generującej scenę 3D
    glutDisplayFunc( DisplayScene );
   
    // dołączenie funkcji wywoływanej przy zmianie rozmiaru okna
    glutReshapeFunc( Reshape );
   
    // dołączenie funkcji obsługi klawiatury
    glutKeyboardFunc( Keyboard );
   
    // 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 );
   
    // utworzenie podmenu - Rodzaj śniegu
    int MenuSnowMode = glutCreateMenu( Menu );
    glutAddMenuEntry( "Punkty", STD_POINTS );
    #ifdef WIN32
   
    glutAddMenuEntry( "Punkty z rozszerzoną geometrią", GEOMETRIC_POINTS );
    #else
   
    glutAddMenuEntry( "Punkty z rozszerzona geometria", GEOMETRIC_POINTS );
    #endif
   
    glutAddMenuEntry( "Tekstura", TEXTURE );
    glutAddMenuEntry( "Sprajty punktowe", POINT_SPRITE );
   
    // menu główne
    glutCreateMenu( Menu );
    #ifdef WIN32
   
    glutAddSubMenu( "Rodzaj śniegu", MenuSnowMode );
    glutAddSubMenu( "Aspekt obrazu", MenuAspect );
    glutAddMenuEntry( "Wyjście", EXIT );
    #else
   
    glutAddSubMenu( "Rodzaj sniegu", MenuSnowMode );
    glutAddSubMenu( "Aspekt obrazu", MenuAspect );
    glutAddMenuEntry( "Wyjscie", EXIT );
    #endif
   
    // określenie przycisku myszki obsługującej menu podręczne
    glutAttachMenu( GLUT_RIGHT_BUTTON );
   
    // utworzenie tekstur
    GenerateTextures();
   
    // sprawdzenie i przygotowanie obsługi wybranych rozszerzeń
    ExtensionSetup();
   
    // wywołanie funkcji timera
    glutTimerFunc( 10, Timer, 0 );
   
    // wprowadzenie programu do obsługi pętli komunikatów
    glutMainLoop();
    return 0;
}
Poprzedni dokument Następny dokument
Mgła Krzywe i powierzchnie Beziera