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

Okrawanie i obcinanie

[lekcja] Rozdział 5. Okrawanie obszaru renderingu, płaszczyzny obcinające i przykładowy program.
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:
C/C++
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:
C/C++
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:
C/C++
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:
C/C++
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.
Rysunek 1. Program Okrawanie i obcinanie
Rysunek 1. Program Okrawanie i obcinanie

Plik okrawanie i obcinanie.cpp

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

#include <GL/glut.h>
#include <stdlib.h>

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

enum
{
    FULL_WINDOW, // aspekt obrazu - całe okno
    ASPECT_1_1, // aspekt obrazu 1:1
    WIRE_SPHERE, // kula
    WIRE_CONE, // stożek
    WIRE_CUBE, // sześcian
    WIRE_TORUS, // torus
    WIRE_DODECAHEDRON, // dwunastościan
    WIRE_TEAPOT, // czajnik
    WIRE_OCTAHEDRON, // ośmiościan
    WIRE_TETRAHEDRON, // czworościan
    WIRE_ICOSAHEDRON, // dwudziestościan
    EXIT // wyjście
};

// aspekt obrazu

int aspect = FULL_WINDOW;

// rodzaj obiektu

int object = WIRE_SPHERE;

// rozmiary bryły obcinania

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;

// współczynnik skalowania

GLfloat scale = 1.0;

// kąty obrotu

GLfloat rotatex = 0.0;
GLfloat rotatey = 0.0;

// przesunięcie

GLfloat translatex = 0.0;
GLfloat translatey = 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;

// współczynniki równań płaszczyzn obcinających

GLdouble plane_eq0[ 4 ] =
{
    1.0, 0.0, 0.0, 0.0
};

GLdouble plane_eq1[ 4 ] =
{
    - 1.0, 0.0, 0.0, 0.0
};

// funkcja generująca scenę 3D

void Display()
{
    // kolor tła - zawartość bufora koloru
    glClearColor( 1.0, 1.0, 1.0, 1.0 );
   
    // czyszczenie bufora koloru
    glClear( GL_COLOR_BUFFER_BIT );
   
    // pobranie rozmiarów okna
    int width = glutGet( GLUT_WINDOW_WIDTH );
    int height = glutGet( GLUT_WINDOW_HEIGHT );
   
    // określnie współrzędnych prostokąta okrawającego
    glScissor( 50, 50, width - 100, height - 100 );
   
    // włączenie okrawania obszaru renderingu
    glEnable( GL_SCISSOR_TEST );
   
    // wybór macierzy modelowania
    glMatrixMode( GL_MODELVIEW );
   
    // macierz modelowania = macierz jednostkowa
    glLoadIdentity();
   
    // przesunięcie układu współrzędnych obiektu do środka bryły odcinania
    glTranslatef( 0, 0, -( near + far ) / 2 );
   
    // przesunięcie obiektu - ruch myszką
    glTranslatef( translatex, translatey, 0.0 );
   
    // skalowanie obiektu - klawisze "+" i "-"
    glScalef( scale, scale, scale );
   
    // obroty obiektu - klawisze kursora
    glRotatef( rotatex, 1.0, 0.0, 0.0 );
    glRotatef( rotatey, 0.0, 1.0, 0.0 );
   
    // definicjie płaszczyzn odcinania
    glClipPlane( GL_CLIP_PLANE0, plane_eq0 );
    glClipPlane( GL_CLIP_PLANE1, plane_eq1 );
   
    // dwukrotne rysowanie obiektu sceny
    for( int i = 0; i < 2; i++ )
    {
        // kolor krawędzi obiektu
        glColor3f( i, 0.0, 1.0 - i );
       
        // włączenie płaszczyzny obcinającej
        glEnable( GL_CLIP_PLANE0 + i );
       
        // wyłączenie płaszczyzny obcinającej
        glDisable( GL_CLIP_PLANE1 - i );
       
        // rysowanie obiektu
        switch( object )
        {
            // kula
        case WIRE_SPHERE:
            glutWireSphere( 1.0, 20, 10 );
            break;
           
            // stożek
        case WIRE_CONE:
            glutWireCone( 1.0, 2.0, 20, 10 );
            break;
           
            // sześcian
        case WIRE_CUBE:
            glutWireCube( 1.0 );
            break;
           
            // torus
        case WIRE_TORUS:
            glutWireTorus( 0.2, 1, 10, 20 );
            break;
           
            // dwunastościan
        case WIRE_DODECAHEDRON:
            glutWireDodecahedron();
            break;
           
            // czajnik
        case WIRE_TEAPOT:
            glutWireTeapot( 1.0 );
            break;
           
            // ośmiościan
        case WIRE_OCTAHEDRON:
            glutWireOctahedron();
            break;
           
            // czworościan
        case WIRE_TETRAHEDRON:
            glutWireTetrahedron();
            break;
           
            // dwudziestościan
        case WIRE_ICOSAHEDRON:
            glutWireIcosahedron();
            break;
        }
    }
   
    // skierowanie poleceń do wykonania
    glFlush();
   
    // zamiana buforów koloru
    glutSwapBuffers();
}

// zmiana wielkości okna

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

// obsługa klawiatury

void Keyboard( unsigned char key, int x, int y )
{
    // klawisz +
    if( key == '+' )
         scale += 0.1;
    else
   
    // klawisz -
    if( key == '-' && scale > 0.1 )
         scale -= 0.1;
   
    // odrysowanie okna
    Reshape( glutGet( GLUT_WINDOW_WIDTH ), glutGet( GLUT_WINDOW_HEIGHT ) );
}

// obsługa klawiszy funkcyjnych i klawiszy kursora

void SpecialKeys( int key, int x, int y )
{
    switch( key )
    {
        // kursor w lewo
    case GLUT_KEY_LEFT:
        rotatey -= 1;
        break;
       
        // kursor w górę
    case GLUT_KEY_UP:
        rotatex -= 1;
        break;
       
        // kursor w prawo
    case GLUT_KEY_RIGHT:
        rotatey += 1;
        break;
       
        // kursor w dół
    case GLUT_KEY_DOWN:
        rotatex += 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 )
    {
        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();
    }
}

// obsługa menu podręcznego

void Menu( int value )
{
    switch( value )
    {
        // obszar renderingu - całe okno
    case FULL_WINDOW:
        aspect = FULL_WINDOW;
        Reshape( glutGet( GLUT_WINDOW_WIDTH ), glutGet( GLUT_WINDOW_HEIGHT ) );
        break;
       
        // obszar renderingu - aspekt 1:1
    case ASPECT_1_1:
        aspect = ASPECT_1_1;
        Reshape( glutGet( GLUT_WINDOW_WIDTH ), glutGet( GLUT_WINDOW_HEIGHT ) );
        break;
       
        // kula
    case WIRE_SPHERE:
        object = WIRE_SPHERE;
        Display();
        break;
       
        // cylinder
    case WIRE_CONE:
        object = WIRE_CONE;
        Display();
        break;
       
        // sześcian
    case WIRE_CUBE:
        object = WIRE_CUBE;
        Display();
        break;
       
        // torus
    case WIRE_TORUS:
        object = WIRE_TORUS;
        Display();
        break;
       
        // dwunastościan
    case WIRE_DODECAHEDRON:
        object = WIRE_DODECAHEDRON;
        Display();
        break;
       
        // czajnik
    case WIRE_TEAPOT:
        object = WIRE_TEAPOT;
        Display();
        break;
       
        // ośmiościan
    case WIRE_OCTAHEDRON:
        object = WIRE_OCTAHEDRON;
        Display();
        break;
       
        // czworościan
    case WIRE_TETRAHEDRON:
        object = WIRE_TETRAHEDRON;
        Display();
        break;
       
        // dwudziestościan
    case WIRE_ICOSAHEDRON:
        object = WIRE_ICOSAHEDRON;
        Display();
        break;
       
        // wyjście
    case EXIT:
        exit( 0 );
    }
}

int main( int argc, char * argv[] )
{
    // inicjalizacja biblioteki GLUT
    glutInit( & argc, argv );
   
    // inicjalizacja bufora ramki
    glutInitDisplayMode( GLUT_DOUBLE | GLUT_RGB );
   
    // rozmiary głównego okna programu
    glutInitWindowSize( 500, 500 );
   
    // utworzenie głównego okna programu
    glutCreateWindow( "Okrawanie i obcinanie" );
   
    // dołączenie funkcji generującej scenę 3D
    glutDisplayFunc( Display );
   
    // 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 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 - obiekt
    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
   
    // menu główne
    glutCreateMenu( Menu );
    glutAddSubMenu( "Aspekt obrazu", MenuAspect );
    glutAddSubMenu( "Obiekt", MenuObject );
    #ifdef WIN32
   
    glutAddMenuEntry( "Wyjście", EXIT );
    #else
   
    glutAddMenuEntry( "Wyjscie", EXIT );
    #endif
   
    // określenie przycisku myszki obsługującego menu podręczne
    glutAttachMenu( GLUT_RIGHT_BUTTON );
   
    // wprowadzenie programu do obsługi pętli komunikatów
    glutMainLoop();
    return 0;
}
Poprzedni dokument Następny dokument
Przekształcenia geometryczne Elementarne obiekty geometryczne