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:
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
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:
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ą:
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:
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
#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"
PFNGLWINDOWPOS2IPROC glWindowPos2i = NULL;
enum
{
FULL_WINDOW = GL_ALWAYS + 100,
ASPECT_1_1,
EXIT
};
int aspect = FULL_WINDOW;
#ifdef near
#undef near
#endif
#ifdef far
#undef far
#endif
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;
GLfloat rotatex = 0.0;
GLfloat rotatey = 0.0;
int button_state = GLUT_UP;
int button_x, button_y;
GLuint CUBE_LIST;
GLint fog_hint = GL_DONT_CARE;
GLfloat fog_start = 3.0;
GLfloat fog_end = 5.0;
GLfloat fog_density = 0.5;
GLfloat fog_mode = GL_LINEAR;
void DrawString( GLint x, GLint y, char * string )
{
glWindowPos2i( x, y );
int len = strlen( string );
for( int i = 0; i < len; i++ )
glutBitmapCharacter( GLUT_BITMAP_9_BY_15, string[ i ] );
}
void DisplayScene()
{
glClearColor( 1.0, 1.0, 1.0, 1.0 );
glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
glMatrixMode( GL_MODELVIEW );
glLoadIdentity();
glTranslatef( 0.0, 0.0, -( near + far ) / 2 );
glRotatef( rotatex, 1.0, 0.0, 0.0 );
glRotatef( rotatey, 0.0, 1.0, 0.0 );
glScalef( 1.15, 1.15, 1.15 );
glEnable( GL_DEPTH_TEST );
glEnable( GL_FOG );
glHint( GL_FOG_HINT, fog_hint );
glFogfv( GL_FOG_COLOR, White );
glFogf( GL_FOG_DENSITY, fog_density );
glFogf( GL_FOG_MODE, fog_mode );
glFogf( GL_FOG_START, fog_start );
glFogf( GL_FOG_END, fog_end );
glCallList( CUBE_LIST );
glDisable( GL_FOG );
char string[ 200 ];
GLfloat var[ 4 ];
glColor3fv( Black );
glGetFloatv( GL_FOG_DENSITY, var );
sprintf( string, "GL_FOG_DENSITY = %f", var[ 0 ] );
DrawString( 2, 2, string );
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 );
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 );
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 );
glFlush();
glutSwapBuffers();
}
void Reshape( int width, int height )
{
glViewport( 0, 0, width, height );
glMatrixMode( GL_PROJECTION );
glLoadIdentity();
if( aspect == ASPECT_1_1 )
{
if( width < height && width > 0 )
glFrustum( left, right, bottom * height / width, top * height / width, near, far );
else
if( width >= height && height > 0 )
glFrustum( left * width / height, right * width / height, bottom, top, near, far );
}
else
glFrustum( left, right, bottom, top, near, far );
DisplayScene();
}
void Keyboard( unsigned char key, int x, int y )
{
if( key == '+' )
fog_density += 0.05;
else
if( key == '-' && fog_density > 0.05 )
fog_density -= 0.05;
DisplayScene();
}
void SpecialKeys( int key, int x, int y )
{
switch( key )
{
case GLUT_KEY_UP:
fog_start += 0.1;
fog_end += 0.1;
break;
case GLUT_KEY_DOWN:
fog_start -= 0.1;
fog_end -= 0.1;
break;
}
Reshape( glutGet( GLUT_WINDOW_WIDTH ), glutGet( GLUT_WINDOW_HEIGHT ) );
}
void MouseButton( int button, int state, int x, int y )
{
if( button == GLUT_LEFT_BUTTON )
{
button_state = state;
if( state == GLUT_DOWN )
{
button_x = x;
button_y = y;
}
}
}
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();
}
}
void Menu( int value )
{
switch( value )
{
case GL_LINEAR:
case GL_EXP:
case GL_EXP2:
fog_mode = value;
DisplayScene();
break;
case GL_FASTEST:
case GL_DONT_CARE:
case GL_NICEST:
fog_hint = value;
DisplayScene();
break;
case FULL_WINDOW:
aspect = FULL_WINDOW;
Reshape( glutGet( GLUT_WINDOW_WIDTH ), glutGet( GLUT_WINDOW_HEIGHT ) );
break;
case ASPECT_1_1:
aspect = ASPECT_1_1;
Reshape( glutGet( GLUT_WINDOW_WIDTH ), glutGet( GLUT_WINDOW_HEIGHT ) );
break;
case EXIT:
exit( 0 );
}
}
void GenerateDisplayLists()
{
CUBE_LIST = glGenLists( 1 );
glNewList( CUBE_LIST, GL_COMPILE );
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 );
glEnd();
glEndList();
}
void ExtensionSetup()
{
const char * version =( char * ) glGetString( GL_VERSION );
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 );
}
if( major > 1 || minor >= 4 )
{
glWindowPos2i =( PFNGLWINDOWPOS2IPROC ) wglGetProcAddress( "glWindowPos2i" );
}
else
if( glutExtensionSupported( "GL_ARB_window_pos" ) )
{
glWindowPos2i =( PFNGLWINDOWPOS2IPROC ) wglGetProcAddress
( "glWindowPos2iARB" );
}
else
{
printf( "Brak rozszerzenia ARB_window_pos!\n" );
exit( 0 );
}
}
int main( int argc, char * argv[] )
{
glutInit( & argc, argv );
glutInitDisplayMode( GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH );
glutInitWindowSize( 500, 500 );
#ifdef WIN32
glutCreateWindow( "Mgła odległościowa" );
#else
glutCreateWindow( "Mgla odleglosciowa" );
#endif
glutDisplayFunc( DisplayScene );
glutReshapeFunc( Reshape );
glutKeyboardFunc( Keyboard );
glutSpecialFunc( SpecialKeys );
glutMouseFunc( MouseButton );
glutMotionFunc( MouseMotion );
glutCreateMenu( Menu );
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 );
int MenuFogMode = glutCreateMenu( Menu );
glutAddMenuEntry( "GL_LINEAR", GL_LINEAR );
glutAddMenuEntry( "GL_EXP", GL_EXP );
glutAddMenuEntry( "GL_EXP2", GL_EXP2 );
int MenuFogHint = glutCreateMenu( Menu );
glutAddMenuEntry( "GL_FASTEST", GL_FASTEST );
glutAddMenuEntry( "GL_DONT_CARE", GL_DONT_CARE );
glutAddMenuEntry( "GL_NICEST", GL_NICEST );
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
glutAttachMenu( GLUT_RIGHT_BUTTON );
ExtensionSetup();
GenerateDisplayLists();
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
#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>
PFNGLWINDOWPOS2IPROC glWindowPos2i = NULL;
PFNGLFOGCOORDPOINTERPROC glFogCoordPointer = NULL;
enum
{
EXIT
};
const int terrain_width = 100;
const int terrain_height = 100;
GLuint terrain_triangles;
GLfloat terrain_vertex[ terrain_width * terrain_height * 4 ];
GLfloat terrain_normal[ terrain_width * terrain_height * 3 ];
GLfloat terrain_fog_coord[ terrain_width * terrain_height * 3 ];
GLuint terrain_index[( terrain_width - 1 ) * terrain_height * 2 * 3 ];
GLfloat fog_color[ 4 ] =
{
0.9, 0.9, 0.9, 1.0
};
GLfloat fog_density = 0.02;
GLfloat fog_start = 1.0;
GLfloat fog_end = 100.0;
GLint fog_mode = GL_LINEAR;
GLint fog_hint = GL_DONT_CARE;
GLfloat translatex = 0.0;
GLfloat translatez = 0.0;
void DrawString( GLint x, GLint y, char * string )
{
glWindowPos2i( x, y );
int len = strlen( string );
for( int i = 0; i < len; i++ )
glutBitmapCharacter( GLUT_BITMAP_9_BY_15, string[ i ] );
}
void DisplayScene()
{
glClearColor( fog_color[ 0 ], fog_color[ 1 ], fog_color[ 2 ], fog_color[ 3 ] );
glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
glMatrixMode( GL_MODELVIEW );
glLoadIdentity();
glTranslatef( 0.0, - 10.0, 0.0 );
glTranslatef( translatex, 0, translatez );
glEnable( GL_DEPTH_TEST );
glEnable( GL_LIGHTING );
glEnable( GL_LIGHT0 );
glColorMaterial( GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE );
glEnable( GL_COLOR_MATERIAL );
glEnable( GL_FOG );
glHint( GL_FOG_HINT, fog_hint );
glFogi( GL_FOG_MODE, fog_mode );
glFogfv( GL_FOG_COLOR, fog_color );
glFogf( GL_FOG_DENSITY, fog_density );
glFogf( GL_FOG_START, fog_start );
glFogf( GL_FOG_END, fog_end );
glFogi( GL_FOG_COORD_SRC, GL_FOG_COORD );
glEnableClientState( GL_VERTEX_ARRAY );
glEnableClientState( GL_NORMAL_ARRAY );
glEnableClientState( GL_FOG_COORD_ARRAY );
glVertexPointer( 4, GL_FLOAT, 0, terrain_vertex );
glNormalPointer( GL_FLOAT, 0, terrain_normal );
glFogCoordPointer( GL_FLOAT, 0, terrain_fog_coord );
glDrawElements( GL_TRIANGLES, terrain_triangles * 3, GL_UNSIGNED_INT, terrain_index );
char string[ 200 ];
GLfloat var[ 4 ];
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 );
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 );
glFlush();
glutSwapBuffers();
}
void Reshape( int width, int height )
{
glViewport( 0, 0, width, height );
glMatrixMode( GL_PROJECTION );
glLoadIdentity();
gluPerspective( 90.0,( double ) width /( double ) height, 1, 200.0 );
DisplayScene();
}
void SpecialKeys( int key, int x, int y )
{
switch( key )
{
case GLUT_KEY_UP:
translatez += 0.5;
break;
case GLUT_KEY_DOWN:
translatez -= 0.5;
break;
case GLUT_KEY_LEFT:
translatex += 0.5;
break;
case GLUT_KEY_RIGHT:
translatex -= 0.5;
break;
}
glutPostRedisplay();
}
void Menu( int value )
{
switch( value )
{
case GL_LINEAR:
case GL_EXP:
case GL_EXP2:
fog_mode = value;
DisplayScene();
break;
case GL_FASTEST:
case GL_DONT_CARE:
case GL_NICEST:
fog_hint = value;
DisplayScene();
break;
case EXIT:
exit( 0 );
}
}
void GenerateTexture()
{
const int texture_size = 256;
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;
}
glPixelStorei( GL_UNPACK_ALIGNMENT, 1 );
glTexParameteri( GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
glTexParameteri( GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
glTexImage1D( GL_TEXTURE_1D, 0, 3, texture_size, 0, GL_RGB, GL_UNSIGNED_BYTE, texture );
glEnable( GL_TEXTURE_1D );
}
void Normalize( GLfloat * v )
{
GLfloat d = sqrt( v[ 0 ] * v[ 0 ] + v[ 1 ] * v[ 1 ] + v[ 2 ] * v[ 2 ] );
if( d )
{
v[ 0 ] /= d;
v[ 1 ] /= d;
v[ 2 ] /= d;
}
}
void ComputeNormals( GLfloat * normal_sum, GLuint * normal_count,
GLuint idx1, GLuint idx2, GLuint idx3 )
{
GLfloat * v1 = & terrain_vertex[ idx1 * 4 ];
GLfloat * v2 = & terrain_vertex[ idx2 * 4 ];
GLfloat * v3 = & terrain_vertex[ idx3 * 4 ];
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 ];
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 ];
Normalize( n );
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 ] ++;
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 ] ++;
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 ] ++;
}
GLfloat Perturb( const GLfloat height )
{
return height + rand() /( GLfloat ) RAND_MAX - 0.5;
}
void GenerateTerrain()
{
srand( time( NULL ) );
GLfloat height_grid[ terrain_width * terrain_height ];
height_grid[ 0 ] = 0.0;
for( int j = 1; j < terrain_width; j++ )
{
height_grid[ j ] = Perturb( height_grid[ j - 1 ] );
}
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 ] ) );
}
}
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;
}
}
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 ];
}
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;
}
}
GLfloat normal_sum[ terrain_width * terrain_height * 3 ];
GLuint normal_count[ terrain_width * terrain_height * 3 ];
for( int i = 0; i < terrain_width * terrain_height * 3; i++ )
{
normal_sum[ i ] = 0.0;
normal_count[ i ] = 0;
}
for( int i = 1; i < terrain_height; i++ )
{
for( int j = 1; j < terrain_width; j++ )
{
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 ] );
terrain_triangles++;
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 ] );
terrain_triangles++;
}
}
for( int i = 0; i < terrain_width * terrain_height * 3; i++ )
{
terrain_normal[ i ] = normal_sum[ i ] /( GLfloat )( normal_count[ i ] );
}
}
void ExtensionSetup()
{
const char * version =( char * ) glGetString( GL_VERSION );
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 );
}
if( major > 1 || minor >= 4 )
{
glWindowPos2i =( PFNGLWINDOWPOS2IPROC ) wglGetProcAddress( "glWindowPos2i" );
}
else
if( glutExtensionSupported( "GL_ARB_window_pos" ) )
{
glWindowPos2i =( PFNGLWINDOWPOS2IPROC ) wglGetProcAddress
( "glWindowPos2iARB" );
}
else
{
printf( "Brak rozszerzenia ARB_window_pos!\n" );
exit( 0 );
}
if( major > 1 || minor >= 4 )
{
glFogCoordPointer =( PFNGLFOGCOORDPOINTERPROC ) wglGetProcAddress
( "glFogCoordPointer" );
}
else
if( glutExtensionSupported( "GL_EXT_fog_coord" ) )
{
glFogCoordPointer =( PFNGLFOGCOORDPOINTERPROC ) wglGetProcAddress
( "glFogCoordPointerEXT" );
}
else
{
printf( "Brak rozszerzenia EXT_fog_coord!\n" );
exit( 0 );
}
}
int main( int argc, char * argv[] )
{
glutInit( & argc, argv );
glutInitDisplayMode( GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH );
glutInitWindowSize( 500, 500 );
#ifdef WIN32
glutCreateWindow( "Mgła objętościowa" );
#else
glutCreateWindow( "Mgla objetosciowa" );
#endif
glutDisplayFunc( DisplayScene );
glutReshapeFunc( Reshape );
glutSpecialFunc( SpecialKeys );
glutCreateMenu( Menu );
int MenuFogMode = glutCreateMenu( Menu );
glutAddMenuEntry( "GL_LINEAR", GL_LINEAR );
glutAddMenuEntry( "GL_EXP", GL_EXP );
glutAddMenuEntry( "GL_EXP2", GL_EXP2 );
int MenuFogHint = glutCreateMenu( Menu );
glutAddMenuEntry( "GL_FASTEST", GL_FASTEST );
glutAddMenuEntry( "GL_DONT_CARE", GL_DONT_CARE );
glutAddMenuEntry( "GL_NICEST", GL_NICEST );
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
glutAttachMenu( GLUT_RIGHT_BUTTON );
ExtensionSetup();
GenerateTexture();
GenerateTerrain();
glutMainLoop();
return 0;
}