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

Mgła

[lekcja] Rozdział 24. Parametry, obliczanie i jakość mgły; dwa programy przykładowe (mgła odległościowa i mgła objętościowa).
Mgła jest efektem specjalnym, który obok oświetlenie i teksturowania może znacząco wpłynąć na realizm generowanej grafiki 3D. Jednak w przeciwieństwie oświetlenia i teksturowania mgła jest efektem znacznie prostszym do uzyskania i kontrolowania.
Mgłę można podzielić na dwa rodzaje. Pierwsza to mgła odległościowa (ang depth-based fog), a druga to mająca znacznie większe możliwości mgła objętościowa (ang. volumetric fog). Efekt mgły objętościowej wprowadzono w wersja 1.4 biblioteki OpenGL w oparciu o rozszerzenie EXT fog coord.
Mgłę można także symulować przy użyciu np. systemów cząstek, które zostaną zaprezentowane w odrębnym odcinku kursu OpenGL.
Efekt mgły jest domyślnie wyłączony i należy go uaktywnić korzystając z funkcji glEnable z parametrem GL_FOG.

Parametry mgły

Pełną kontrolę nad parametrami mgły daje grupa funkcji glFog:
C/C++
void glFogf( GLenum pname, GLfloat param )
void glFogi( GLenum pname, GLint param )
void glFogfv( GLenum pname, const GLfloat * params )
void glFogiv( GLenum pname, const GLint * params )
gdzie pname określa rodzaj modyfikowanego parametru mgły, a param/params zawiera nową wartość modyfikowanego parametru. Zestawienie dopuszczalnych wartości powyższych parametrów zawiera tabela 1.

Tabela 1: Zestawienie właściwości mgły

parametrdopuszczalne wartościwartość początkowa
GL_FOG_MODEGL_LINEAR,
GL_EXP, GL_EXP
GL_EXP
GL_FOG_DENSITYdowolna wartość1
GL_FOG_STARTdowolna wartość0
GL_FOG_ENDdowolna wartość1
GL_FOG_INDEXnumer indeksu koloru0
GL_FOG_COLORRGBA(0, 0, 0, 0)
GL_FOG_COORD_SRCGL_FOG_COORD,
GL_FRAGMENT_DEPTH
GL_FRAGMENT_DEPTH
Zauważmy, że parametr GL_FOG_COLOR może być zmieniony tylko przy użyciu funkcji glFogfv i glFogiv. Pierwotne nazwy stałych GL_FOG_COORD_SRC i GL_FOG_COORD, które wprowadzono w wersji 1.4 biblioteki OpenGL, brzmiały: GL_FOG_COORDINATE_SOURCE i GL_FOG_COORDINATE. Zmiany nazw zostały wprowadzone w specyfikacji wersji 1.5 OpenGL.

Obliczanie mgły

Współczynnik mgły f obliczany jest na podstawie równania określonego w parametrze GL_FOG_MODE:
  • GL_LINEAR
  • GL_EXP
  • GL_EXP2
Z uwagi na postać równań opisujących współczynnik mgły często spotyka się określenie: mgła liniowa (GL_LINEAR), mgła wykładnicza (GL_EXP) i mgła wykładnicza kwadratowa (GL_EXP2).
Stałe składniki równań współczynnika mgły oznaczają:
  • GL_FOG_DENSITY - gęstość mgły,
  • GL_FOG_START - minimalna odległość oddziaływania mgły,
  • GL_FOG_END - maksymalna odległość oddziaływania mgły.
Zmienna c występująca w każdym równaniu jest regulowana przez właściwość GL_FOG_COORD_SRC. Wartość GL_FRAGMENT_DEPTH oznacza, że c jest równe odległości fragmentu od obserwatora. Jest to wspomniana na wstępie mgła odległościowa, gdzie intensywność efektu mgły zależy wyłącznie od odległości obiektu od obserwatora. Inna spotykana nazwa tak generowanej mgły to mgła głębokościowa.
Gdy właściwość GL_FOG_COORD_SRC jest równa GL_FOG_COORD, zmienna c jest wyliczana na podstawie tzw. współrzędnych mgły określanych osobno dla każdego wierzchołka prymitywu. W ten sposób generowany jest efekt mgły objętościowej, której intensywność można kształtować w dowolny sposób niezależnie od położenia obiektu.
Współrzędne mgły są definiowane dla każdego wierzchołka, podobnie jak składowe kolorów czy też wektory normalne. Służą do tego funkcje z grupy glFogCoord:
C/C++
void glFogCoordf( GLfloat coord );
void glFogCoordd( GLdouble coord )
void glFogCoordfv( const GLfloat * coord )
void glFogCoorddv( const GLdouble * coord )
Współczynnik mgły f, obliczony wg jednego z powyższych równań, po obcięciu do przedziału [0, 1], jest następnie używany do obliczenia wartości składowych kolorów fragmentów. W trybie RGBA kolor wynikowy C fragmentu wyliczany jest z równania:
gdzie Cr jest kolorem początkowym fragmentu, a Cf kolorem mgły określonym w parametrze GL_FOG_COLOR, przy czym obliczenia wykonywane są wyłącznie na składowych RGB fragmentu. W trybie indeksowym analogiczne równanie ma postać:
gdzie i jest wynikowym numerem indeksu koloru fragmentu, ir początkowym numerem indeksu koloru fragmentu, a if numerem indeksu mapy kolorów zawierającym kolor mgły (parametr GL_FOG_INDEX).

Jakość mgły

Biblioteka OpenGL udostępnia także kontrolę nad jakością obliczeń mgły przy użyciu wskazówek renderingu - funkcja glHint z parametrem GL_FOG_HINT. Jeżeli dana implementacja obsługuje powyższą wskazówkę, to opcja GL_FASTEST oznacza obliczanie mgły dla każdego wierzchołka (ang. per vertex fog), a GL oznacza obliczanie mgły dla każdego fragmentu (ang. per pixel fog). Często stosowane nazwy tak generowanej mgły to odpowiednio mgła wierzchołkowa i mgła pikselowa.

Programy przykładowe

Pierwszy program przykładowy (plik mgla_odleglosciowa.cpp) przedstawia efekty wykorzystania mgły odległościowej. Użytkownik ma możliwość modyfikacji wszystkich parametrów mgły, w tym także zmiany minimalnej i maksymalnej odległości oddziaływania mgły liniowej. Efekty działania programu przedstawiono na rysunkach 1 - 3. Szczególnie warto zwrócić uwagę na doskonale widoczne różnice w jakości renderingu mgły wierzchołkowej i mgły pikselowej.

Plik mgla_odleglosciowa.cpp

Rysunek 1. Program Mgła odległościowa - mgła generowana dla wierzchołka
Rysunek 1. Program Mgła odległościowa - mgła generowana dla wierzchołka
Rysunek 2. Program Mgła odległościowa - mgła generowana dla fragmentu
Rysunek 2. Program Mgła odległościowa - mgła generowana dla fragmentu
Rysunek 3. Program Mgła odległościowa - mgła „wykładnicza”
Rysunek 3. Program Mgła odległościowa - mgła „wykładnicza”
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 "colors.h"

// wskaźnik na funkcję glWindowPos2i

PFNGLWINDOWPOS2IPROC glWindowPos2i = NULL;

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

enum
{
    FULL_WINDOW = GL_ALWAYS + 100, // 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;

// kąty obrotu

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;

// identyfikator listy wyświetlania

GLuint CUBE_LIST;

// wskazówki jakości generacji mgły

GLint fog_hint = GL_DONT_CARE;

// początek i koniec oddziaływania mgły liniowej

GLfloat fog_start = 3.0;
GLfloat fog_end = 5.0;

// gęstość mgły

GLfloat fog_density = 0.5;

// rodzaj mgły

GLfloat fog_mode = GL_LINEAR;

// 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()
{
    // 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();
   
    // przesunięcie układu współrzędnych sześcianu do środka bryły odcinania
    glTranslatef( 0.0, 0.0, -( near + far ) / 2 );
   
    // obroty sześcianu
    glRotatef( rotatex, 1.0, 0.0, 0.0 );
    glRotatef( rotatey, 0.0, 1.0, 0.0 );
   
    // niewielkie powiększenie sześcianu
    glScalef( 1.15, 1.15, 1.15 );
   
    // włączenie testu bufora głębokości
    glEnable( GL_DEPTH_TEST );
   
    // włączenie efektu mgły
    glEnable( GL_FOG );
   
    // wskazówki jakości generacji mgły
    glHint( GL_FOG_HINT, fog_hint );
   
    // kolor mgły
    glFogfv( GL_FOG_COLOR, White );
   
    // gęstość mgły
    glFogf( GL_FOG_DENSITY, fog_density );
   
    // rodzaj mgły
    glFogf( GL_FOG_MODE, fog_mode );
   
    // początek i koniec oddziaływania mgły liniowej
    glFogf( GL_FOG_START, fog_start );
    glFogf( GL_FOG_END, fog_end );
   
    // wyświetlenie sześcianu
    glCallList( CUBE_LIST );
   
    // wyłączenie efektu mgły
    glDisable( GL_FOG );
   
    // wyświetlenie wybranych informacji
    char string[ 200 ];
    GLfloat var[ 4 ];
    glColor3fv( Black );
   
    // gęstość mgły
    glGetFloatv( GL_FOG_DENSITY, var );
    sprintf( string, "GL_FOG_DENSITY = %f", var[ 0 ] );
    DrawString( 2, 2, string );
   
    // informacja o wskazówkach generacji mgły
    glGetFloatv( GL_FOG_HINT, var );
    switch(( int ) var[ 0 ] )
    {
    case GL_FASTEST:
        sprintf( string, "GL_FOG_HINT = GL_FASTEST" );
        break;
    case GL_DONT_CARE:
        sprintf( string, "GL_FOG_HINT = GL_DONT_CARE" );
        break;
    case GL_NICEST:
        sprintf( string, "GL_FOG_HINT = GL_NICEST" );
        break;
    }
    DrawString( 2, 16, string );
   
    // początek i koniec oddziaływania mgły liniowej
    glGetFloatv( GL_FOG_START, var );
    glGetFloatv( GL_FOG_END, var + 1 );
    sprintf( string, "GL_FOG_START = %f GL_FOG_END = %f", var[ 0 ], var[ 1 ] );
    DrawString( 2, 30, string );
   
    // rodzaj mgły
    glGetFloatv( GL_FOG_MODE, var );
    switch(( int ) var[ 0 ] )
    {
    case GL_LINEAR:
        sprintf( string, "GL_FOG_MODE = GL_LINEAR" );
        break;
    case GL_EXP:
        sprintf( string, "GL_FOG_MODE = GL_EXP" );
        break;
    case GL_EXP2:
        sprintf( string, "GL_FOG_MODE = GL_EXP2" );
        break;
    }
    DrawString( 2, 44, string );
   
    // 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
    DisplayScene();
}

// obsługa klawiatury

void Keyboard( unsigned char key, int x, int y )
{
    // klawisz +
    if( key == '+' )
         fog_density += 0.05;
    else
   
    // klawisz -
    if( key == '-' && fog_density > 0.05 )
         fog_density -= 0.05;
   
    // narysowanie sceny
    DisplayScene();
}

// obsługa klawiszy funkcyjnych i klawiszy kursora

void SpecialKeys( int key, int x, int y )
{
    switch( key )
    {
        // kursor w górę
    case GLUT_KEY_UP:
        fog_start += 0.1;
        fog_end += 0.1;
        break;
       
        // kursor w dół
    case GLUT_KEY_DOWN:
        fog_start -= 0.1;
        fog_end -= 0.1;
        break;
    }
   
    // odrysowanie okna
    Reshape( glutGet( GLUT_WINDOW_WIDTH ), glutGet( GLUT_WINDOW_HEIGHT ) );
}

// 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 )
    {
        // rodzaj mgły
    case GL_LINEAR:
    case GL_EXP:
    case GL_EXP2:
        fog_mode = value;
        DisplayScene();
        break;
       
        // GL_FOG_HINT
    case GL_FASTEST:
    case GL_DONT_CARE:
    case GL_NICEST:
        fog_hint = 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 GenerateDisplayLists()
{
    // generowanie identyfikatora listy wyświetlania
    CUBE_LIST = glGenLists( 1 );
   
    // lista wyświetlania
    glNewList( CUBE_LIST, GL_COMPILE );
   
    // rysowanie sześcianu RGB - 12 trójkątów
    glBegin( GL_TRIANGLES );
    glColor3fv( Red );
    glVertex3f( - 1.0, - 1.0, 1.0 );
    glColor3fv( Yellow );
    glVertex3f( 1.0, - 1.0, 1.0 );
    glColor3fv( White );
    glVertex3f( 1.0, 1.0, 1.0 );
   
    glColor3fv( Red );
    glVertex3f( - 1.0, - 1.0, 1.0 );
    glColor3fv( White );
    glVertex3f( 1.0, 1.0, 1.0 );
    glColor3fv( Magenta );
    glVertex3f( - 1.0, 1.0, 1.0 );
   
    glColor3fv( Magenta );
    glVertex3f( - 1.0, 1.0, 1.0 );
    glColor3fv( White );
    glVertex3f( 1.0, 1.0, 1.0 );
    glColor3fv( Blue );
    glVertex3f( - 1.0, 1.0, - 1.0 );
   
    glColor3fv( Blue );
    glVertex3f( - 1.0, 1.0, - 1.0 );
    glColor3fv( White );
    glVertex3f( 1.0, 1.0, 1.0 );
    glColor3fv( Cyan );
    glVertex3f( 1.0, 1.0, - 1.0 );
   
    glColor3fv( Cyan );
    glVertex3f( 1.0, 1.0, - 1.0 );
    glColor3fv( White );
    glVertex3f( 1.0, 1.0, 1.0 );
    glColor3fv( Yellow );
    glVertex3f( 1.0, - 1.0, 1.0 );
   
    glColor3fv( Cyan );
    glVertex3f( 1.0, 1.0, - 1.0 );
    glColor3fv( Yellow );
    glVertex3f( 1.0, - 1.0, 1.0 );
    glColor3fv( Lime );
    glVertex3f( 1.0, - 1.0, - 1.0 );
   
    glColor3fv( Lime );
    glVertex3f( 1.0, - 1.0, - 1.0 );
    glColor3fv( Yellow );
    glVertex3f( 1.0, - 1.0, 1.0 );
    glColor3fv( Black );
    glVertex3f( - 1.0, - 1.0, - 1.0 );
   
    glColor3fv( Black );
    glVertex3f( - 1.0, - 1.0, - 1.0 );
    glColor3fv( Yellow );
    glVertex3f( 1.0, - 1.0, 1.0 );
    glColor3fv( Red );
    glVertex3f( - 1.0, - 1.0, 1.0 );
   
    glColor3fv( Black );
    glVertex3f( - 1.0, - 1.0, - 1.0 );
    glColor3fv( Red );
    glVertex3f( - 1.0, - 1.0, 1.0 );
    glColor3fv( Blue );
    glVertex3f( - 1.0, 1.0, - 1.0 );
   
    glColor3fv( Blue );
    glVertex3f( - 1.0, 1.0, - 1.0 );
    glColor3fv( Red );
    glVertex3f( - 1.0, - 1.0, 1.0 );
    glColor3fv( Magenta );
    glVertex3f( - 1.0, 1.0, 1.0 );
   
    glColor3fv( Black );
    glVertex3f( - 1.0, - 1.0, - 1.0 );
    glColor3fv( Blue );
    glVertex3f( - 1.0, 1.0, - 1.0 );
    glColor3fv( Cyan );
    glVertex3f( 1.0, 1.0, - 1.0 );
   
    glColor3fv( Lime );
    glVertex3f( 1.0, - 1.0, - 1.0 );
    glColor3fv( Black );
    glVertex3f( - 1.0, - 1.0, - 1.0 );
    glColor3fv( Cyan );
    glVertex3f( 1.0, 1.0, - 1.0 );
   
    // koniec definicji sześcianu RGB
    glEnd();
   
    // koniec listy wyświetlania
    glEndList();
}

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


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( "Mgła odległościowa" );
    #else
   
    glutCreateWindow( "Mgla odleglosciowa" );
    #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 );
   
    // dołączenie funkcji obsługi klawiszy funkcyjnych i klawiszy kursora
    glutSpecialFunc( SpecialKeys );
   
    // 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 );
   
    // utworzenie podmenu - Rodzaj mgły
    int MenuFogMode = glutCreateMenu( Menu );
    glutAddMenuEntry( "GL_LINEAR", GL_LINEAR );
    glutAddMenuEntry( "GL_EXP", GL_EXP );
    glutAddMenuEntry( "GL_EXP2", GL_EXP2 );
   
    // utworzenie podmenu - GL_FOG_HINT
    int MenuFogHint = glutCreateMenu( Menu );
    glutAddMenuEntry( "GL_FASTEST", GL_FASTEST );
    glutAddMenuEntry( "GL_DONT_CARE", GL_DONT_CARE );
    glutAddMenuEntry( "GL_NICEST", GL_NICEST );
   
    // menu główne
    glutCreateMenu( Menu );
    #ifdef WIN32
   
    glutAddSubMenu( "Rodzaj mgły", MenuFogMode );
    glutAddSubMenu( "GL_FOG_HINT", MenuFogHint );
    glutAddSubMenu( "Aspekt obrazu", MenuAspect );
    glutAddMenuEntry( "Wyjście", EXIT );
    #else
   
    glutAddSubMenu( "Rodzaj mgly", MenuFogMode );
    glutAddSubMenu( "GL_FOG_HINT", MenuFogHint );
    glutAddSubMenu( "Aspekt obrazu", MenuAspect );
    glutAddMenuEntry( "Wyjscie", EXIT );
    #endif
   
    // określenie przycisku myszki obsługującej menu podręczne
    glutAttachMenu( GLUT_RIGHT_BUTTON );
   
    // sprawdzenie i przygotowanie obsługi wybranych rozszerzeń
    ExtensionSetup();
   
    // utworzenie list wyświetlania
    GenerateDisplayLists();
   
    // wprowadzenie programu do obsługi pętli komunikatów
    glutMainLoop();
    return 0;
}
Drugi program przykładowy (plik mgla_objetosciowa.cpp) przedstawia prosty efekt generowania powierzchni, której jednym z elementów jest mgła objętościowa. W programie wykorzystano elementy dema RadeonFogCoord dostępnego pod adresem: http://ati.amd.com/developer/sdk/RadeonSDK/ Html/Samples/OpenGL/RadeonFogCoord.html.
Współrzędne mgły oraz inne elementy opisujące wierzchołki powierzchni przechowywane są w tablicach wierzchołków. W celu uniknięcie zwielokrotniania danych wierzchołków program korzysta z indeksów do tablic wierzchołków. Rozmiar generowanej powierzchni jest stały, stąd wirtualny „lot” nad nią nie jest niestety zbyt długi. Przykładowe efekty działania programu dla trzech parametrów generowania mgły przedstawiają rysunki 4 - 4.

Plik mgla_objetosciowa.cpp

Rysunek 4. Program Mgła objętościowa - mgła GL_LINEAR
Rysunek 4. Program Mgła objętościowa - mgła GL_LINEAR
Rysunek 5. Program Mgła objętościowa - mgła GL_EXP
Rysunek 5. Program Mgła objętościowa - mgła GL_EXP
Rysunek 6. Program Mgła objętościowa - mgła GL_EXP2
Rysunek 6. Program Mgła objętościowa - mgła GL_EXP2
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 <math.h>
#include <time.h>

// wskaźnik na funkcję glWindowPos2i

PFNGLWINDOWPOS2IPROC glWindowPos2i = NULL;

// wskaźnik na funkcję glFogCoordPointer

PFNGLFOGCOORDPOINTERPROC glFogCoordPointer = NULL;

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

enum
{
    EXIT // wyjście
};

// ilość współrzędnych X w siatce powierzchni

const int terrain_width = 100;

// ilość współrzędnych Z w siatce powierzchni

const int terrain_height = 100;

// ilość wygenerowanych trójkątów powierzchni

GLuint terrain_triangles;

// współrzędne wierzchołków powierzchni

GLfloat terrain_vertex[ terrain_width * terrain_height * 4 ];

// współrzędne wektorów normalnych powierzchni

GLfloat terrain_normal[ terrain_width * terrain_height * 3 ];

// współrzędne mgły wierzchołków powierzchni

GLfloat terrain_fog_coord[ terrain_width * terrain_height * 3 ];

// indeks do danych z tablicami wierzchołków

GLuint terrain_index[( terrain_width - 1 ) * terrain_height * 2 * 3 ];

// kolor mgły

GLfloat fog_color[ 4 ] =
{
    0.9, 0.9, 0.9, 1.0
};

// gęstość mgły

GLfloat fog_density = 0.02;

// początek i koniec oddziaływania mgły liniowej

GLfloat fog_start = 1.0;
GLfloat fog_end = 100.0;

// rodzaj mgły

GLint fog_mode = GL_LINEAR;

// wskazówki jakości generacji mgły

GLint fog_hint = GL_DONT_CARE;

// wektory przesunięcia powierzchni

GLfloat translatex = 0.0;
GLfloat translatez = 0.0;

// 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()
{
    // kolor tła - zawartość bufora koloru
    glClearColor( fog_color[ 0 ], fog_color[ 1 ], fog_color[ 2 ], fog_color[ 3 ] );
   
    // 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();
   
    // obrócenie sceny
    glTranslatef( 0.0, - 10.0, 0.0 );
   
    // przesunięcie powierzchni
    glTranslatef( translatex, 0, translatez );
   
    // włączenie testu bufora głębokości
    glEnable( GL_DEPTH_TEST );
   
    // włączenie oświetlenia
    glEnable( GL_LIGHTING );
   
    // włączenie światła GL_LIGHT0
    glEnable( GL_LIGHT0 );
   
    // właściwości materiału określone przez kolor wierzchołków
    glColorMaterial( GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE );
   
    // włączenie obsługi właściwości materiałów
    glEnable( GL_COLOR_MATERIAL );
   
    // włączenie mgły
    glEnable( GL_FOG );
   
    // wskazówki jakości generacji mgły
    glHint( GL_FOG_HINT, fog_hint );
   
    // rodzaj mgły
    glFogi( GL_FOG_MODE, fog_mode );
   
    // kolor mgły
    glFogfv( GL_FOG_COLOR, fog_color );
   
    // gęstość mgły
    glFogf( GL_FOG_DENSITY, fog_density );
   
    // początek i koniec oddziaływania mgły liniowej
    glFogf( GL_FOG_START, fog_start );
    glFogf( GL_FOG_END, fog_end );
   
    // generacja współrzędnych mgły
    glFogi( GL_FOG_COORD_SRC, GL_FOG_COORD );
   
    // włączenie tablic wierzchołków
    glEnableClientState( GL_VERTEX_ARRAY );
    glEnableClientState( GL_NORMAL_ARRAY );
    glEnableClientState( GL_FOG_COORD_ARRAY );
   
    // tablice wierzchołków
    glVertexPointer( 4, GL_FLOAT, 0, terrain_vertex );
    glNormalPointer( GL_FLOAT, 0, terrain_normal );
    glFogCoordPointer( GL_FLOAT, 0, terrain_fog_coord );
   
    // narysowanie powierzchni przy użyciu tablicy indeksów wierzchołków
    glDrawElements( GL_TRIANGLES, terrain_triangles * 3, GL_UNSIGNED_INT, terrain_index );
   
    // wyświetlenie wybranych informacji
    char string[ 200 ];
    GLfloat var[ 4 ];
   
    // informacja o wskazówkach generacji mgły
    glGetFloatv( GL_FOG_HINT, var );
    switch(( int ) var[ 0 ] )
    {
    case GL_FASTEST:
        sprintf( string, "GL_FOG_HINT = GL_FASTEST" );
        break;
    case GL_DONT_CARE:
        sprintf( string, "GL_FOG_HINT = GL_DONT_CARE" );
        break;
    case GL_NICEST:
        sprintf( string, "GL_FOG_HINT = GL_NICEST" );
        break;
    }
    DrawString( 2, 2, string );
   
    // rodzaj mgły
    glGetFloatv( GL_FOG_MODE, var );
    switch(( int ) var[ 0 ] )
    {
    case GL_LINEAR:
        sprintf( string, "GL_FOG_MODE = GL_LINEAR" );
        break;
    case GL_EXP:
        sprintf( string, "GL_FOG_MODE = GL_EXP" );
        break;
    case GL_EXP2:
        sprintf( string, "GL_FOG_MODE = GL_EXP2" );
        break;
    }
    DrawString( 2, 16, string );
   
    // 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
    gluPerspective( 90.0,( double ) width /( double ) height, 1, 200.0 );
   
    // generowanie sceny 3D
    DisplayScene();
}

// obsługa klawiszy funkcyjnych i klawiszy kursora

void SpecialKeys( int key, int x, int y )
{
    switch( key )
    {
        // kursor w górę
    case GLUT_KEY_UP:
        translatez += 0.5;
        break;
       
        // kursor w dół
    case GLUT_KEY_DOWN:
        translatez -= 0.5;
        break;
       
        // kursor w lewo
    case GLUT_KEY_LEFT:
        translatex += 0.5;
        break;
       
        // kursor w prawo
    case GLUT_KEY_RIGHT:
        translatex -= 0.5;
        break;
    }
   
    // odrysowanie okna
    glutPostRedisplay();
}

// obsługa menu podręcznego

void Menu( int value )
{
    switch( value )
    {
        // rodzaj mgły
    case GL_LINEAR:
    case GL_EXP:
    case GL_EXP2:
        fog_mode = value;
        DisplayScene();
        break;
       
        // GL_FOG_HINT
    case GL_FASTEST:
    case GL_DONT_CARE:
    case GL_NICEST:
        fog_hint = value;
        DisplayScene();
        break;
       
        // wyjście
    case EXIT:
        exit( 0 );
    }
}

// przygotowanie tekstury

void GenerateTexture()
{
    // rozmiar tekstury
    const int texture_size = 256;
   
    // dane tekstury
    GLubyte texture[ 3 * texture_size ];
    for( int i = 0; i < texture_size; i++ )
    {
        texture[ i * 3 + 0 ] = 10;
        texture[ i * 3 + 1 ] = 150 - i;
        texture[ i * 3 + 2 ] = 50;
    }
   
    // tryb upakowania bajtów danych tekstury
    glPixelStorei( GL_UNPACK_ALIGNMENT, 1 );
   
    // filtr powiększający
    glTexParameteri( GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
   
    // filtr pomniejszający
    glTexParameteri( GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
   
    // definiowanie tekstury
    glTexImage1D( GL_TEXTURE_1D, 0, 3, texture_size, 0, GL_RGB, GL_UNSIGNED_BYTE, texture );
   
    // włączenie tekstur jednowymiarowych
    glEnable( GL_TEXTURE_1D );
}

// normalizacja wektora

void Normalize( GLfloat * v )
{
    // obliczenie długości wektora
    GLfloat d = sqrt( v[ 0 ] * v[ 0 ] + v[ 1 ] * v[ 1 ] + v[ 2 ] * v[ 2 ] );
   
    // normalizacja wektora
    if( d )
    {
        v[ 0 ] /= d;
        v[ 1 ] /= d;
        v[ 2 ] /= d;
    }
}

// obliczanie wektora normalnego
// normal_sum - tablica sumy współrzędnych wektorów normalnych
//              wszystkich trójkątów posiadających wybrany wierzchołek
// normal_count- tablica zliczająca ilość wektorów normalnych
//               użytych do obliczenia zawartości tablicy normal_sum
// idx1 - indeks pierwszego wierzchołka trójkąta
// idx2 - indeks drugiego wierzchołka trójkąta
// idx3 - indeks trzeciego wierzchołka trójkąta

void ComputeNormals( GLfloat * normal_sum, GLuint * normal_count,
GLuint idx1, GLuint idx2, GLuint idx3 )
{
    // adresy tablicy ze współrzędnymi trzech wierzchołków trójkąta
    GLfloat * v1 = & terrain_vertex[ idx1 * 4 ];
    GLfloat * v2 = & terrain_vertex[ idx2 * 4 ];
    GLfloat * v3 = & terrain_vertex[ idx3 * 4 ];
   
    // obliczenie współrzędnych wektorów
    // w oparciu o współrzędne wierzchołków trójkąta
    GLfloat t1[ 3 ];
    GLfloat t2[ 3 ];
    t1[ 0 ] = v2[ 0 ] - v1[ 0 ];
    t1[ 1 ] = v2[ 1 ] - v1[ 1 ];
    t1[ 2 ] = v2[ 2 ] - v1[ 2 ];
    t2[ 0 ] = v3[ 0 ] - v1[ 0 ];
    t2[ 1 ] = v3[ 1 ] - v1[ 1 ];
    t2[ 2 ] = v3[ 2 ] - v1[ 2 ];
   
    // obliczanie iloczynu wektorowego wektorów vt i v2
    // co daje w wyniku wektor normalny
    GLfloat n[ 3 ];
    n[ 0 ] = t2[ 1 ] * t1[ 2 ] - t1[ 1 ] * t2[ 2 ];
    n[ 1 ] = t2[ 2 ] * t1[ 0 ] - t1[ 2 ] * t2[ 0 ];
    n[ 2 ] = t2[ 0 ] * t1[ 1 ] - t1[ 0 ] * t2[ 1 ];
   
    // normalizacja wektora normalnego
    Normalize( n );
   
    // aktualizacja zawartości tablic normal_sum i normal_count
    // dla pierwszego wierzchołka
    normal_sum[ idx1 * 3 + 0 ] += n[ 0 ];
    normal_sum[ idx1 * 3 + 1 ] += n[ 1 ];
    normal_sum[ idx1 * 3 + 2 ] += n[ 2 ];
    normal_count[ idx1 * 3 + 0 ] ++;
    normal_count[ idx1 * 3 + 1 ] ++;
    normal_count[ idx1 * 3 + 2 ] ++;
   
    // aktualizacja zawartości tablic normal_sum i normal_count
    // dla drugiego wierzchołka
    normal_sum[ idx2 * 3 ] += n[ 0 ];
    normal_sum[ idx2 * 3 + 1 ] += n[ 1 ];
    normal_sum[ idx2 * 3 + 2 ] += n[ 2 ];
    normal_count[ idx2 * 3 + 0 ] ++;
    normal_count[ idx2 * 3 + 1 ] ++;
    normal_count[ idx2 * 3 + 2 ] ++;
   
    // aktualizacja zawartości tablic normal_sum i normal_count
    // dla trzeciego wierzchołka
    normal_sum[ idx3 * 3 + 0 ] += n[ 0 ];
    normal_sum[ idx3 * 3 + 1 ] += n[ 1 ];
    normal_sum[ idx3 * 3 + 2 ] += n[ 2 ];
    normal_count[ idx3 * 3 + 0 ] ++;
    normal_count[ idx3 * 3 + 1 ] ++;
    normal_count[ idx3 * 3 + 2 ] ++;
}

// obliczenie perturbacji (zniekształcenia) kolejnego elementu siatki wysokości
// height - bazowa wysokość poddawana zniekształceniu

GLfloat Perturb( const GLfloat height )
{
    return height + rand() /( GLfloat ) RAND_MAX - 0.5;
}

// generowanie powierzchni

void GenerateTerrain()
{
    // określenie zarodka dla ciągu liczb pseudolosowych
    srand( time( NULL ) );
   
    // tablica z siatką wysokości kolejnych wierzchołków powierzchni
    GLfloat height_grid[ terrain_width * terrain_height ];
    height_grid[ 0 ] = 0.0;
   
    // generowanie pierwszego wiersza siatki wysokości
    for( int j = 1; j < terrain_width; j++ )
    {
        height_grid[ j ] = Perturb( height_grid[ j - 1 ] );
    }
   
    // generowanie pozostałych wierszy siatki wysokości
    for( GLuint i = 1; i < terrain_height; i++ )
    {
        height_grid[ i * terrain_width ] = Perturb( height_grid[( i - 1 ) * terrain_width ] );
        for( int j = 1; j < terrain_width; j++ )
        {
            height_grid[ i * terrain_width + j ] =
            Perturb( 0.5 *( height_grid[ i * terrain_width +( j - 1 ) ] +
            height_grid[( i - 1 ) * terrain_width + j ] ) );
        }
    }
   
    // generowanie współrzędnych wierzchołków powierzchni
    for( int i = 0; i < terrain_height; i++ )
    {
        for( int j = 0; j < terrain_width; j++ )
        {
            terrain_vertex[ i * terrain_width * 4 + j * 4 + 0 ] = j - terrain_width / 2;
            terrain_vertex[ i * terrain_width * 4 + j * 4 + 1 ] = height_grid[ i * terrain_width + j ];
            terrain_vertex[ i * terrain_width * 4 + j * 4 + 2 ] = - i;
            terrain_vertex[ i * terrain_width * 4 + j * 4 + 3 ] = 1.0;
        }
    }
   
    // obliczenie minimalnej wartości w siatce wysokości
    GLfloat min_height = 0.0;
    for( int i = 0; i < terrain_width * terrain_height; i++ )
    {
        if( height_grid[ i ] < min_height )
             min_height = height_grid[ i ];
       
    }
    // obliczenie współrzędnych mgły uzależnionych od wysokości
    for( int i = 0; i < terrain_height; i++ )
    {
        for( int j = 0; j < terrain_width; j++ )
        {
            terrain_fog_coord[ i * terrain_width + j ] =
            150.0 /( height_grid[ i * terrain_width + j ] - min_height ) -
            terrain_vertex[ i * terrain_width * 4 + j * 4 + 2 ] - 10.0;
        }
    }
   
    // tablica sumy współrzędnych wektorów normalnych
    // wszystkich trójkątów posiadających wybrany wierzchołek
    GLfloat normal_sum[ terrain_width * terrain_height * 3 ];
   
    // tablica zliczająca ilość wektorów normalnych
    // użytych do obliczenia zawartości tablicy normal_sum
    GLuint normal_count[ terrain_width * terrain_height * 3 ];
   
    // wartości początkowe tablic normal_sum i normal_count
    for( int i = 0; i < terrain_width * terrain_height * 3; i++ )
    {
        normal_sum[ i ] = 0.0;
        normal_count[ i ] = 0;
    }
   
    // generowanie trójkątów dla każdego elementu siatki wysokości
    for( int i = 1; i < terrain_height; i++ )
    {
        for( int j = 1; j < terrain_width; j++ )
        {
            // pierwszy trójkąt
            terrain_index[ terrain_triangles * 3 + 0 ] =( i - 1 ) * terrain_width +( j - 1 );
            terrain_index[ terrain_triangles * 3 + 1 ] = i * terrain_width + j;
            terrain_index[ terrain_triangles * 3 + 2 ] =( i - 1 ) * terrain_width + j;
            ComputeNormals( normal_sum, normal_count,
            terrain_index[ terrain_triangles * 3 + 0 ],
            terrain_index[ terrain_triangles * 3 + 1 ],
            terrain_index[ terrain_triangles * 3 + 2 ] );
           
            // zwiększenie ilości trójkątów
            terrain_triangles++;
           
            // drugi trójkąt
            terrain_index[ terrain_triangles * 3 + 0 ] =( i - 1 ) * terrain_width +( j - 1 );
            terrain_index[ terrain_triangles * 3 + 1 ] = i * terrain_width +( j - 1 );
            terrain_index[ terrain_triangles * 3 + 2 ] = i * terrain_width + j;
            ComputeNormals( normal_sum, normal_count,
            terrain_index[ terrain_triangles * 3 + 0 ],
            terrain_index[ terrain_triangles * 3 + 1 ],
            terrain_index[ terrain_triangles * 3 + 2 ] );
           
            // zwiększenie ilości trójkątów
            terrain_triangles++;
        }
    }
   
    // generowanie uśrednionych wektorów normalnych
    for( int i = 0; i < terrain_width * terrain_height * 3; i++ )
    {
        terrain_normal[ i ] = normal_sum[ i ] /( GLfloat )( normal_count[ i ] );
    }
}

// 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źnika wybranej funkcji OpenGL 1.4
        glFogCoordPointer =( PFNGLFOGCOORDPOINTERPROC ) wglGetProcAddress
        ( "glFogCoordPointer" );
    }
    else
    // sprawdzenie czy jest obsługiwane rozszerzenie EXT_fog_coord
    if( glutExtensionSupported( "GL_EXT_fog_coord" ) )
    {
        // pobranie wskaźnika wybranej funkcji rozszerzenia EXT_fog_coord
        glFogCoordPointer =( PFNGLFOGCOORDPOINTERPROC ) wglGetProcAddress
        ( "glFogCoordPointerEXT" );
    }
    else
    {
        printf( "Brak rozszerzenia EXT_fog_coord!\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( "Mgła objętościowa" );
    #else
   
    glutCreateWindow( "Mgla objetosciowa" );
    #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 klawiszy funkcyjnych i klawiszy kursora
    glutSpecialFunc( SpecialKeys );
   
    // utworzenie menu podręcznego
    glutCreateMenu( Menu );
   
    // utworzenie podmenu - Rodzaj mgły
    int MenuFogMode = glutCreateMenu( Menu );
    glutAddMenuEntry( "GL_LINEAR", GL_LINEAR );
    glutAddMenuEntry( "GL_EXP", GL_EXP );
    glutAddMenuEntry( "GL_EXP2", GL_EXP2 );
   
    // utworzenie podmenu - GL_FOG_HINT
    int MenuFogHint = glutCreateMenu( Menu );
    glutAddMenuEntry( "GL_FASTEST", GL_FASTEST );
    glutAddMenuEntry( "GL_DONT_CARE", GL_DONT_CARE );
    glutAddMenuEntry( "GL_NICEST", GL_NICEST );
   
    // menu główne
    glutCreateMenu( Menu );
    glutAddSubMenu( "GL_FOG_HINT", MenuFogHint );
    #ifdef WIN32
   
    glutAddSubMenu( "Rodzaj mgły", MenuFogMode );
    glutAddMenuEntry( "Wyjście", EXIT );
    #else
   
    glutAddSubMenu( "Rodzaj mgly", MenuFogMode );
    glutAddMenuEntry( "Wyjscie", EXIT );
    #endif
   
    // określenie przycisku myszki obsługującej menu podręczne
    glutAttachMenu( GLUT_RIGHT_BUTTON );
   
    // sprawdzenie i przygotowanie obsługi wybranych rozszerzeń
    ExtensionSetup();
   
    // przygotowanie tekstury
    GenerateTexture();
   
    // generowanie powierzchni
    GenerateTerrain();
   
    // wprowadzenie programu do obsługi pętli komunikatów
    glutMainLoop();
    return 0;
}
Poprzedni dokument Następny dokument
Tablice wierzchołków Systemy cząstek