Poza opisanymi wczesniej technikami definiowania sceny 3D biblioteka posiada jeszcze dwa ciekawe mechanizmy. Pierwszy z nich to okrawanie obrazu (ang. scissoring), drugim sa płaszczyzny obcinajace (ang. clipping plane).
Okrawanie obrazu
Okrawanie obrazu, czyli zmniejszenie okna renderingu, sprowadza sie do zdefiniowania tzw. prostokata okrawajacy (ang. scissor box), w granicach którego bedzie wykonywany rendering. Słuzy do tego funkcja:
void glScissor( GLint x, GLint y, GLsizei width, GLsizei height )
Parametry x i y okreslaja współrzedne lewego dolnego rogu prostokata okrawajacego, a width i height odpowiednio jego szerokosc i wysokosc. Początkowo prostokat okrawajacy pokrywa cały obszar renderingu.
Standardowo okrawanie obszaru renderingu jest wyłaczone. Do modyfikacji tego i wielu innych parametrów maszyny stanów OpenGL słuzy para funkcji:
void glEnable( GLenum cap )
void glDisable( GLenum cap )
Parametr cap okresla jaki parametr maszyny stanów OpenGL ma byc odpowiednio właczony (glEnable) lub wyłaczony (glDisable). W przypadku okrawania parametr ten ma wartosc GL SCISSOR TEST.
Płaszczyzny obcinające
Płaszczyzny obcinajace stanowia uzupełnienie podstawowych płaszczyzn odcinajacych składajacych sie na bryłe odcinania, a podstawowa ich cecha jest brak ograniczen co do orientacji w przestrzeni sceny 3D. Definiowanie płaszczyzny odcinania umozliwia funkcja:
void glClipPlane( GLenum plane, const GLdouble * equation )
Parametr plane okresla numer definiowanej płaszczyzny i moze przyjmowac wartosci okreslone stałymi GL_CLIP_PLANEi, gdzie i zawiera sie od 0 do pomniejszonej o 1 ilosci płaszczyzn obcinania obsługiwanej przez dana implementacje biblioteki OpenGL. Ilosc dostepnych płaszczyzn okresla zmienna stanu GL MAX CLIP PLANES, przy czym specyfikacja OpenGL okresla, ze nie moze ich byc mniej niz szesc.
Typowe pliki nagłówkowe gl.h definiuja szesc stałych: GL CLIP PLANE0, GL CLIP PLANE1, GL CLIP PLANE2, GL CLIP PLANE3, GL CLIP PLANE4 i GL CLIP PLANE5, ale mozna takze uzywac prostej arytmetyki: GL CLIP PLANEi = GL CLIP PLANE0 + i.
Drugi parametr equation zawiera wskaznik na tablice ze współczynnikami (A,B,C,D) równania płaszczyzny w postaci:
Ax + By + Cz + D = 0
Dla tak okreslonego równania płaszczyzny wektor (A,B,C) jest jej wektorem normalnym (prostopadłym). Płaszczyzna bedzie obcinała te wszystkie elementy sceny, które znajda sie po stronie przeciwnej do wskazywanej przez zwrot tego wektora.
Współczynniki równania płaszczyzny obcinajacej podlegaja takim samym przekształceniom jak współrzedne wierzchołków prymitywów. Jeżeli zatem płaszczyzna taka ma pozostac niezmieniona niezbedne jest wcześniejsze odłozenie macierzy modelowania na stos.
Standardowo wszystkie płaszczyzny obcinania sa wyłaczone. Ich aktywacja wymaga wywołania funkcji glEnable z parametrem GL CLIP PLANEi, gdzie i jest oczywiscie numerem płaszczyzny. Biblioteka OpenGL umozliwia takze pobranie współczynników równania płaszczyzny dla wybranej płaszczyzny obcinanaia. Słuzy do tego funkcja:
void glGetClipPlane( GLenum plane, GLdouble * equation )
której parametr plane okresla numer płaszczyzny, a equation zawiera wskaźnik na tablice, gdzie zostana umieszczone współczynniki jej równania. Domyślnie współczynniki równania płaszczyzny obcinajacej wynosza (0, 0, 0, 0).
Program przykładowy
Program przykładowy (plik okrawanie i obcinanie.cpp) jest niewielka modyfikacja jednego z poprzednich programów. Dwie zdefiniowane płaszczyzny obcinajace róznia sie jedynie zwrotem wektora normalnego. Ich naprzemienne właczanie i wyłaczanie oraz dwukrotne rysowanie obiektu umozliwia uzyskanie efektu przedstawionego na rysunku 1, czyli dwubarwnosci. Ponadto wielkosc okna renderingu w programie jest ograniczona, co widac na przykładzie ucha czajnika na powyzszym rysunku.
Plik okrawanie i obcinanie.cpp
#include <GL/glut.h>
#include <stdlib.h>
enum
{
FULL_WINDOW,
ASPECT_1_1,
WIRE_SPHERE,
WIRE_CONE,
WIRE_CUBE,
WIRE_TORUS,
WIRE_DODECAHEDRON,
WIRE_TEAPOT,
WIRE_OCTAHEDRON,
WIRE_TETRAHEDRON,
WIRE_ICOSAHEDRON,
EXIT
};
int aspect = FULL_WINDOW;
int object = WIRE_SPHERE;
const GLdouble left = - 10.0;
const GLdouble right = 10.0;
const GLdouble bottom = - 10.0;
const GLdouble top = 10.0;
const GLdouble near = 50.0;
const GLdouble far = 70.0;
GLfloat scale = 1.0;
GLfloat rotatex = 0.0;
GLfloat rotatey = 0.0;
GLfloat translatex = 0.0;
GLfloat translatey = 0.0;
int button_state = GLUT_UP;
int button_x, button_y;
GLdouble plane_eq0[ 4 ] =
{
1.0, 0.0, 0.0, 0.0
};
GLdouble plane_eq1[ 4 ] =
{
- 1.0, 0.0, 0.0, 0.0
};
void Display()
{
glClearColor( 1.0, 1.0, 1.0, 1.0 );
glClear( GL_COLOR_BUFFER_BIT );
int width = glutGet( GLUT_WINDOW_WIDTH );
int height = glutGet( GLUT_WINDOW_HEIGHT );
glScissor( 50, 50, width - 100, height - 100 );
glEnable( GL_SCISSOR_TEST );
glMatrixMode( GL_MODELVIEW );
glLoadIdentity();
glTranslatef( 0, 0, -( near + far ) / 2 );
glTranslatef( translatex, translatey, 0.0 );
glScalef( scale, scale, scale );
glRotatef( rotatex, 1.0, 0.0, 0.0 );
glRotatef( rotatey, 0.0, 1.0, 0.0 );
glClipPlane( GL_CLIP_PLANE0, plane_eq0 );
glClipPlane( GL_CLIP_PLANE1, plane_eq1 );
for( int i = 0; i < 2; i++ )
{
glColor3f( i, 0.0, 1.0 - i );
glEnable( GL_CLIP_PLANE0 + i );
glDisable( GL_CLIP_PLANE1 - i );
switch( object )
{
case WIRE_SPHERE:
glutWireSphere( 1.0, 20, 10 );
break;
case WIRE_CONE:
glutWireCone( 1.0, 2.0, 20, 10 );
break;
case WIRE_CUBE:
glutWireCube( 1.0 );
break;
case WIRE_TORUS:
glutWireTorus( 0.2, 1, 10, 20 );
break;
case WIRE_DODECAHEDRON:
glutWireDodecahedron();
break;
case WIRE_TEAPOT:
glutWireTeapot( 1.0 );
break;
case WIRE_OCTAHEDRON:
glutWireOctahedron();
break;
case WIRE_TETRAHEDRON:
glutWireTetrahedron();
break;
case WIRE_ICOSAHEDRON:
glutWireIcosahedron();
break;
}
}
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 );
Display();
}
void Keyboard( unsigned char key, int x, int y )
{
if( key == '+' )
scale += 0.1;
else
if( key == '-' && scale > 0.1 )
scale -= 0.1;
Reshape( glutGet( GLUT_WINDOW_WIDTH ), glutGet( GLUT_WINDOW_HEIGHT ) );
}
void SpecialKeys( int key, int x, int y )
{
switch( key )
{
case GLUT_KEY_LEFT:
rotatey -= 1;
break;
case GLUT_KEY_UP:
rotatex -= 1;
break;
case GLUT_KEY_RIGHT:
rotatey += 1;
break;
case GLUT_KEY_DOWN:
rotatex += 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 )
{
translatex += 1.1 *( right - left ) / glutGet( GLUT_WINDOW_WIDTH ) *( x - button_x );
button_x = x;
translatey += 1.1 *( top - bottom ) / glutGet( GLUT_WINDOW_HEIGHT ) *( button_y - y );
button_y = y;
glutPostRedisplay();
}
}
void Menu( int value )
{
switch( value )
{
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 WIRE_SPHERE:
object = WIRE_SPHERE;
Display();
break;
case WIRE_CONE:
object = WIRE_CONE;
Display();
break;
case WIRE_CUBE:
object = WIRE_CUBE;
Display();
break;
case WIRE_TORUS:
object = WIRE_TORUS;
Display();
break;
case WIRE_DODECAHEDRON:
object = WIRE_DODECAHEDRON;
Display();
break;
case WIRE_TEAPOT:
object = WIRE_TEAPOT;
Display();
break;
case WIRE_OCTAHEDRON:
object = WIRE_OCTAHEDRON;
Display();
break;
case WIRE_TETRAHEDRON:
object = WIRE_TETRAHEDRON;
Display();
break;
case WIRE_ICOSAHEDRON:
object = WIRE_ICOSAHEDRON;
Display();
break;
case EXIT:
exit( 0 );
}
}
int main( int argc, char * argv[] )
{
glutInit( & argc, argv );
glutInitDisplayMode( GLUT_DOUBLE | GLUT_RGB );
glutInitWindowSize( 500, 500 );
glutCreateWindow( "Okrawanie i obcinanie" );
glutDisplayFunc( Display );
glutReshapeFunc( Reshape );
glutKeyboardFunc( Keyboard );
glutSpecialFunc( SpecialKeys );
glutMouseFunc( MouseButton );
glutMotionFunc( MouseMotion );
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 MenuObject = glutCreateMenu( Menu );
glutAddMenuEntry( "Kula", WIRE_SPHERE );
#ifdef WIN32
glutAddMenuEntry( "Stożek", WIRE_CONE );
glutAddMenuEntry( "Sześcian", WIRE_CUBE );
glutAddMenuEntry( "Torus", WIRE_TORUS );
glutAddMenuEntry( "Dwunastościan", WIRE_DODECAHEDRON );
glutAddMenuEntry( "Czajnik", WIRE_TEAPOT );
glutAddMenuEntry( "Ośmiościan", WIRE_OCTAHEDRON );
glutAddMenuEntry( "Czworościan", WIRE_TETRAHEDRON );
glutAddMenuEntry( "Dwudziestościan", WIRE_ICOSAHEDRON );
#else
glutAddMenuEntry( "Stozek", WIRE_CONE );
glutAddMenuEntry( "Szescian", WIRE_CUBE );
glutAddMenuEntry( "Torus", WIRE_TORUS );
glutAddMenuEntry( "Dwunastoscian", WIRE_DODECAHEDRON );
glutAddMenuEntry( "Czajnik", WIRE_TEAPOT );
glutAddMenuEntry( "Osmioscian", WIRE_OCTAHEDRON );
glutAddMenuEntry( "Czworoscian", WIRE_TETRAHEDRON );
glutAddMenuEntry( "Dwudziestoscian", WIRE_ICOSAHEDRON );
#endif
glutCreateMenu( Menu );
glutAddSubMenu( "Aspekt obrazu", MenuAspect );
glutAddSubMenu( "Obiekt", MenuObject );
#ifdef WIN32
glutAddMenuEntry( "Wyjście", EXIT );
#else
glutAddMenuEntry( "Wyjscie", EXIT );
#endif
glutAttachMenu( GLUT_RIGHT_BUTTON );
glutMainLoop();
return 0;
}