SDL / OpenGL

Do grafiki 3D opłaca się stosować profesjonalne biblioteki graficzne. Taką właśnie biblioteką jest OpenGL, którą w prosty sposób możemy zastosować w SDL. W lekcji korzystamy z materiałów kursu NeHe, który jest dostępny w sieci. Kurs ten co prawda dotyczy środowiska Windows, jednakże w prosty sposób da się przetworzyć na środowisko SDL. Oczywiście w materiale stosujemy własne podejście do poruszanych problemów.

 

Przygotowanie projektu SDL / OpenGL

W Code::Blocks uruchamiamy dowolny projekt SDL. Następnie wywołujemy opcję menu Project/Build options. W okienku, które się pojawi, wybieramy zakładkę Linker settings i w prawym polu tekstowym dopisujemy dwie opcje:

 

-lopengl32
-lglu32

 

 

W ten sposób udostępnimy naszemu programowi dwie biblioteki: opengl oraz glu. W pierwszej są zdefiniowane procedury graficzne 3D, natomiast druga udostępnia kilka funkcji pomocniczych, które ułatwiają operowanie obiektami 3D. Ten krok jest bardzo ważny - bez tego wpisu żaden z dalszych programów się nie uruchomi.

 

Nasz program będzie działał wg następującego schematu:

 

Inicjalizacja biblioteki OpenGL

    Rysowanie sceny w pętli

    Wyświetlenie narysowanej sceny

    Obsługa zdarzeń (reakcja na klawisz ESC = zakończenie programu)

Kontynuacja pętli

 

Poniższy program jest szablonem dla aplikacji SDL / OpenGL. Wprowadź go do edytora i zapisz projekt jako szablon:  File/Save project as template. Później będzie można go szybko wybierać z kreatora projektów w Code::Blocks.

Na tym etapie po uruchomieniu programu ekran staje się czarny, pojawia się na nim kursor myszki, a naciśnięcie klawisza ESC kończy jego działanie.

 

Code::Blocks
// Szablon SDL / OpenGL
// (C)2012 Koło Informatyczne
// I LO w Tarnowie
//-------------------------------

#include <SDL/SDL.h>
#include <GL/gl.h>
#include <GL/glu.h>

const int W = 1280;
const int H = 1024;

int main(int argc, char *argv[])
{
   SDL_Event event;

   SDL_Init(SDL_INIT_VIDEO);

   SDL_GL_SetAttribute(SDL_GL_RED_SIZE,    8);
   SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE,   8);
   SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE,  8);
   SDL_GL_SetAttribute(SDL_GL_BUFFER_SIZE,32);

   SDL_SetVideoMode(W, H, 0, SDL_OPENGL | SDL_FULLSCREEN);

   glViewport(0, 0, W, H);

   glMatrixMode(GL_PROJECTION);

   glLoadIdentity();

   gluPerspective(45.0f,(GLfloat)W/(GLfloat)H,0.1f,300.0f);

   glMatrixMode(GL_MODELVIEW);

   glClearColor(0.0f, 0.0f, 0.0f, 0.0f);

   glEnable(GL_DEPTH_TEST);
   glDepthFunc(GL_LEQUAL);

   for(int done = 0; !done;)
   {
     glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
     glLoadIdentity();

     // Tutaj umieszczamy rysowanie sceny
     SDL_GL_SwapBuffers();         // wyświetlamy scenę w oknie podglądu

     SDL_PollEvent(&event);        // sprawdzamy kolejkę zdarzeń
     if(event.key.keysym.sym == SDLK_ESCAPE) done = 1;
   }
}

 

Kod Objaśnienia
#include <SDL/SDL.h>
#include <GL/gl.h>
#include <GL/glu.h>
Na początku programu wstawiamy dyrektywy dołączenia plików nagłówkowych, w których znajdują się definicje funkcji oraz typów danych używanych przez SDL oraz OpenGL. Nazwy wszystkich funkcji biblioteki OpenGL rozpoczynają się od liter gl, a nazwy funkcji biblioteki użytkowej glu rozpoczynają się od liter glu.
const int W = 1280;
const int H = 1024;
Stała W definiuje szerokość ekranu w pikselach.
Stała H definiuje wysokość ekranu w pikselach.
int main(int argc, char *argv[])
{
Główna funkcja programu
  SDL_Event event;
Struktura event służy do obsługi zdarzeń w trakcie działania programu.
  SDL_Init(SDL_INIT_VIDEO);
Inicjujemy podsystem wideo biblioteki SDL.
  SDL_GL_SetAttribute(SDL_GL_RED_SIZE,    8);
  SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE,   8);
  SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE,  8);
  SDL_GL_SetAttribute(SDL_GL_BUFFER_SIZE,32);
Ustawiamy stan początkowy biblioteki OpenGL poprzez określenie kilku atrybutów. Pierwsze trzy funkcje definiują rozmiar pól barw składowych na 8 bitów. Ostatnia funkcja określa rozmiar piksela w bitach. Takie ustawienia dają nam pełną głębię kolorów - 16mln barw.
  SDL_SetVideoMode(W, H, 0, SDL_OPENGL | SDL_FULLSCREEN);
Ustawiamy pożądany tryb wyświetlania. Pierwsze dwa parametry określają szerokość i wysokość ekranu. Trzeci parametr jest ignorowany, ponieważ rozmiar piksela dla OpenGL ustawiamy w atrybutach powyżej. Czwarty parametr określa, że SDL będzie tworzyło obraz poprzez bibliotekę OpenGL na pełnym ekranie.
  glViewport(0, 0, W, H);
To wywołanie określa okno podglądu sceny. Pierwsze dwa parametry to współrzędne x i y lewego dolnego narożnika (standardowo 0, 0). Następnie mamy szerokość i wysokość okna podglądu. W naszym przypadku okno podglądu obejmie cały obszar ekranu. Układ współrzędnych sceny jest umieszczany na środku okna podglądu.
   glMatrixMode(GL_PROJECTION);

   glLoadIdentity();

   gluPerspective(45.0f,(GLfloat)W/(GLfloat)H,0.1f,300.0f);
Wybieramy do przetwarzania macierz projekcji, która odpowiada za sposób prezentacji naszej sceny w obrębie okna podglądu. Dokładniejsze wyjaśnienie zastosowania tej macierzy zostanie podane później.
Kolejna funkcja ustawia w macierzy projekcji macierz jednostkową. Jeśli wyobrazimy sobie kolejne przekształcenia jako mnożenia, to macierz jednostkowa odpowiada mnożeniu przez 1, zatem nie wprowadza żadnych przekształceń. Zawsze macierz jednostkową ustawiamy na początku przekształceń, aby zdefiniować neutralny stan początkowy.
Ostatnia funkcja definiuje perspektywę patrzenia na scenę - dzięki perspektywie obiekty sceny będą wyglądały jak w świecie rzeczywistym - bliższe będą większe, a dalsze mniejsze. Pierwszy parametr określa kąt patrzenia poprzez okno podglądu - to jak judasz przy drzwiach - większy kąt daje szersze pole widzenia, mniejszy zmniejsza je. Kąty w OpenGL podajemy w stopniach. Drugi parametr określa tzw. aspekt okna podglądu, czyli iloraz jego szerokości przez wysokość. Wg tego aspektu będą rysowane obiekty sceny. Zapewni on ich odpowiednie proporcje. Ostatnie dwa parametry określają głębię rysowania. W naszym przypadku element sceny będzie rysowany, jeśli znajdzie się w odległości pomiędzy 0.1 a 300 jednostek.
   glMatrixMode(GL_MODELVIEW);
Przełączamy się na macierz widoku modelu. Macierz ta kontroluje układ współrzędnych, wg którego są wykonywane przekształcenia oraz operacje graficzne.
   glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
Funkcja określa kolor tła. Pierwsze trzy parametry definiują składowe R, G i B. Wartość 0 oznacza brak danej składowej. Wartość 1 oznacza maksymalne natężenia wybranej składowej. Ten schemat obowiązuje dla pozostałych funkcji definiujących kolor w OpenGL.
   glEnable(GL_DEPTH_TEST);
   glDepthFunc(GL_LEQUAL);
Pierwsza funkcja włącza rysowanie ścian ze sprawdzaniem głębokości. Druga funkcja określa sposób testowania głębokości pikseli ścian. Przy rysowaniu ścian OpenGL odnotowuje w buforze głębokości odległość ich pikseli na osi z. Jeśli nowy piksel posiada równą lub mniejszą odległość, to zostanie narysowany. W przeciwnym razie znajduje się za już narysowanym wcześniej pikselem i nie będzie rysowany. Bufor głębokości pozwala realistycznie oddawać kształty rysowanych obiektów.
for(int done = 0; !done;)
   {
     glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
     glLoadIdentity();

     // Tutaj umieszczamy rysowanie sceny

     SDL_GL_SwapBuffers();

     SDL_PollEvent(&event);
     if(event.key.keysym.sym == SDLK_ESCAPE) done = 1;
   }
  return 0;
}
Pętla rysowania sceny. Na początku czyścimy bufor obrazu oraz bufor głębokości. Następnie zerujemy bieżące przekształcenie ustawiając w macierzy widoku modelu macierz jednostkową.

W miejscu komentarza będziemy umieszczali kod rysujący ściany naszych obiektów.

Po narysowaniu sceny wywołujemy funkcję, która prześle ją na ekran monitora.

Ostatnim elementem jest sprawdzenie kolejki zdarzeń. Jeśli użytkownik nacisnął klawisz ESC, to program kończy pętlę i w konsekwencji kończy również swoje działanie.

 

Rysowanie trójkąta i kwadratu

Układ współrzędnych OpenGL wygląda następująco:

 

 

Oś OZ jest skierowana w kierunku obserwatora. Za pomocą macierzy widoku modelu dokonujemy przekształceń tego układu współrzędnych (przesunięcia, obroty, skalowanie). Jako pierwsze ćwiczenie przesuniemy układ współrzędnych o 6 jednostek (przez jednostkę w OpenGL rozumiemy pewną umowną wielkość, która nie zależy od aktualnej rozdzielczości ekranu) w głąb przestrzeni i narysujemy trójkąt. Trójkąt będzie zbudowany z 3 punktów, które w przesuniętym układzie posiadają współrzędne:

 

(0,1,0) - górny punkt
(1,-1,0) - dolny prawy
(-1,-1,0) - dolny lewy

 

Pod komentarzem umieść następujący kod:

 

glTranslatef(0.0f,0.0f,-6.0f);
     
glBegin(GL_TRIANGLES);          // rysujemy trójkąt

 glVertex2f( 0.0f, 1.0f);
 glVertex2f( 1.0f,-1.0f);
 glVertex2f(-1.0f,-1.0f);

glEnd();

 

Przyjrzyjmy się dokładnie temu kodowi:

 

Kod Objaśnienia
glTranslatef(0.0f,0.0f,-6.0f);
Wywołanie tej funkcji przesuwa bieżący układ współrzędnych o 6 jednostek w głąb przestrzeni. Parametrami są przesunięcia względem obecnego układu współrzędnych kolejno wzdłuż osi x, y i z. Ponieważ dwa pierwsze parametry mają wartość 0, to układ współrzędnych przemieści się tylko wzdłuż osi z o 6 jednostek.
glBegin(GL_TRIANGLES);
Ta funkcja włącza tryb rysowania w bieżącym układzie współrzędnych, który teraz jest przesunięty o 6 jednostek w głąb z powodu poprzedniej funkcji. Parametr glBegin() określa rodzaj rysowanych obiektów - tutaj będą to trójkąty.
 glVertex2f( 0.0f, 1.0f);
 glVertex2f( 1.0f,-1.0f);
 glVertex2f(-1.0f,-1.0f);
Za pomocą kolejnych wywołań funkcji glVertex() określamy współrzędne wierzchołków trójkąta. Funkcji glVertex() jest wiele odmian, które przyjmują różne rodzaje parametrów. Funkcja glVertex2f() przyjmuje dwa parametry - współrzędne x i y wierzchołka, natomiast współrzędną z automatycznie ustawia na zero - pamiętajmy jednakże, że współrzędne te odnoszą się do bieżącego układu współrzędnych, który został na początku przesunięty o 6 jednostek w głąb przestrzeni 3D. Wczytane wierzchołki są zapamiętywane w wewnętrznej kolejce OpenGL.
glEnd();
Po wywołaniu tej funkcji OpenGL przetwarza wczytane wierzchołki zgodnie z przekształceniem w macierzy widoku modelu - zostanie dla nich zastosowane przesunięcie o 6 jednostek w głąb przestrzeni. Po tej operacji OpenGL wyświetla zdefiniowany obiekt zgodnie z przekształceniem w macierzy projekcji. Innymi słowy, nasz trójkąt pojawi się na ekranie.
 

 

Poeksperymentuj z różnymi parametrami funkcji glTranslatef(). Ostatni parametr reguluje odległość obiektu od płaszczyzny obrazu. Im będzie on mniejszy (bardziej ujemny), tym dalej znajdzie się nasz trójkąt i z uwagi na perspektywę ustawioną przez funkcję gluPerspective() stanie się on coraz mniejszy. Dodajmy teraz kwadrat obok trójkąta. Aby nie zmieniać definicji wierzchołków trójkąta, dokonamy wstępnego przesunięcia o 6 jednostek w głąb oraz o 1,5 jednostki w lewo. Narysujemy nasz trójkąt, po czym przesuniemy układ współrzędnych o 3 jednostki w prawo i narysujemy kwadrat. Zastąp poprzedni kod następującym:

 

    glTranslatef(-1.5f,0.0f,-6.0f);    // przesuwamy układ współrzędnych

    glBegin(GL_TRIANGLES);        // rysujemy trójkąt

      glVertex2f( 0.0f, 1.0f);
      glVertex2f( 1.0f,-1.0f);
      glVertex2f(-1.0f,-1.0f);

    glEnd();

    glTranslatef(3.0f,0.0f,0.0f); // zmieniamy pozycję układu współrzędnych

    glBegin(GL_QUADS);            // rysujemy kwadrat

      glVertex2f( 1.0f, 1.0f);
      glVertex2f( 1.0f,-1.0f);
      glVertex2f(-1.0f,-1.0f);
      glVertex2f(-1.0f, 1.0f);

    glEnd();

 

Kod Objaśnienia
glTranslatef(-1.5f,0.0f,-6.0f);
Pierwsze przesunięcie ustawia nowy układ współrzędnych o 6 jednostek w głąb oraz o 1,5 jednostki w lewo.
glBegin(GL_TRIANGLES);

  glVertex2f( 0.0f, 1.0f);
  glVertex2f( 1.0f,-1.0f);
  glVertex2f(-1.0f,-1.0f);

glEnd();
Teraz nasz trójkąt zostanie narysowany nieco na lewo od poprzedniej wersji programu. Zwróć uwagę, że wierzchołki posiadają takie same współrzędne, ale cały układ współrzędnych jest teraz w innym miejscu.
glTranslatef(3.0f,0.0f,0.0f);
Po narysowaniu trójkąta przesuwamy układ współrzędnych o 3 jednostki w prawo. Głębokość zostaje taka sama: 6 jednostek.
glBegin(GL_QUADS);

 glVertex2f( 1.0f, 1.0f);
 glVertex2f( 1.0f,-1.0f);
 glVertex2f(-1.0f,-1.0f);
 glVertex2f(-1.0f, 1.0f);

glEnd();
Rysujemy kwadrat. Zwróć uwagę, iż w wywołaniu funkcji glBegin() umieściliśmy inną stałą, która informuje OpenGL, iż nowa figura posiada 4 wierzchołki.

Kolejne 4 wywołania definiują kolejne wierzchołki kwadratu. Zauważ, że są one podane w kolejności zgodnej z ruchem wskazówek zegara. Ta własność stanie się istotna później, gdy będziemy rysować bardziej skomplikowaną grafikę.

Po wywołaniu glEnd() OpenGL rysuje nasz kwadrat po prawej stronie trójkąta.

 

 

Ruch

Dodamy teraz ruch do naszych obiektów. Będą się one poruszały cyklicznie pomiędzy punktami -3 i -10. Na szybkim komputerze program może działać bardzo szybko - w takim przypadku zmniejsz odpowiednio parametr dz (później dodamy tzw. timery, czyli funkcje wykonywane co wybrany okres czasu - pozwalają one uniezależnić szybkość animacji od szybkości komputera, na razie jednak nie komplikujmy zbytnio kodu). Zmień program następująco:

 

    GLfloat z  = -3.0f;  // przesunięcie na osi z
    GLfloat dz = -0.1;   // przyrost przesunięcia na osi z

    for(int done = 0; !done;)
    {
      glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
      glLoadIdentity();

      // Tutaj umieszczamy rysowanie sceny

      glTranslatef(-1.5f,0.0f,z); // przemieszczamy o z jednostek w głąb

      glBegin(GL_TRIANGLES);

        glVertex2f( 0.0f, 1.0f);
        glVertex2f( 1.0f,-1.0f);
        glVertex2f(-1.0f,-1.0f);

      glEnd();

      glTranslatef(3.0f,0.0f,0.0f);

      glBegin(GL_QUADS);

        glVertex2f( 1.0f, 1.0f);
        glVertex2f( 1.0f,-1.0f);
        glVertex2f(-1.0f,-1.0f);
        glVertex2f(-1.0f, 1.0f);

      glEnd();

      z += dz;  // modyfikujemy przesunięcie dla następnego obiegu pętli

      if((z < -10) || (z > -3)) dz = -dz;
      SDL_GL_SwapBuffers();         // wyświetlamy scenę w oknie podglądu

      SDL_PollEvent(&event);        // sprawdzamy kolejkę zdarzeń
      if(event.key.keysym.sym == SDLK_ESCAPE) done = 1;
    }

 

Obroty

Do wykonywania obrotu układu współrzędnych używamy funkcji glRotatef(). Posiada ona cztery parametry. Pierwszy z nich określa kąt w stopniach, o jaki zostanie obrócony bieżący układ współrzędnych. Kolejne trzy parametry określają współrzędne wektora, który będzie osią obrotu.

 

 

Początek wektora zawsze znajduje się w początku bieżącego układu współrzędnych. Po wykonaniu obrotu osie nowego układu zostają odpowiednio zorientowane w przestrzeni. Jeśli teraz będziemy rysować w takim obróconym układzie współrzędnych, to nasz obiekt na ekranie będzie wyglądał na obrócony. Poniższy program dodaje obrót wokół osi z o kat k. Szybkość obrotu regulujemy wielkością parametru dk.

 

   GLfloat z = -3.0f, dz = -0.05;
   GLfloat k = 0, dk = 1;

   for(int done = 0; !done;)
   {
     glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
     glLoadIdentity();

     // Tutaj umieszczamy rysowanie sceny

     glTranslatef(0.0f,0.0f,z);      // przemieszczamy o z jednostek w głąb
     glRotatef(k, 0.0f, 0.0f, 1.0f); // obrót wokół osi z
     glTranslatef(-1.5f,0.0f,0.0f);  // przemieszczamy o 1.5 jednostki w lewo
                                     // w obróconym układzie współrzędnych
     glBegin(GL_TRIANGLES);

       glVertex2f( 0.0f, 1.0f);
       glVertex2f( 1.0f,-1.0f);
       glVertex2f(-1.0f,-1.0f);

     glEnd();

     glTranslatef(3.0f,0.0f,0.0f);  // przesuwamy się o 3 jednostki w prawo
                                    // we wciąż obróconym układzie współrzędnych

     glBegin(GL_QUADS);

       glVertex2f( 1.0f, 1.0f);
       glVertex2f( 1.0f,-1.0f);
       glVertex2f(-1.0f,-1.0f);
       glVertex2f(-1.0f, 1.0f);

     glEnd();

     z += dz;  // modyfikujemy przesunięcie dla następnego obiegu pętli
     if((z < -10) || (z > -3)) dz = -dz;

     k += dk;  // modyfikujemy kąt obrotu
     if(k > 360) k = 1;
     SDL_GL_SwapBuffers();         // wyświetlamy scenę w oknie podglądu

     SDL_PollEvent(&event);        // sprawdzamy kolejkę zdarzeń
     if(event.key.keysym.sym == SDLK_ESCAPE) done = 1;
   }

 

 

W powyższym programie wymień wywołanie funkcji glRotatef() kolejno na:

 
glRotatef(k, 1.0f, 0.0f, 0.0f);  // obrót wokół osi x
glRotatef(k, 0.0f, 1.0f, 0.0f);  // obrót wokół osi y
glRotatef(k, 1.0f, 1.0f, 1.0f);  // odkryj sam, wokół jakiej osi wykonywany jest obrót

 

Załóżmy teraz, że chcemy osobno obracać trójkąt wokół osi y przechodzącej przez jego środek, a kwadrat wokół osi x, która też przechodzi przez jego środek. Zasada będzie następująca:

 

Przemieszczamy układ współrzędnych do miejsca, gdzie będziemy rysowali trójkąt.

Obracamy ten układ o kat k wokół wektora (0,1,0) - oś y.

Rysujemy trójkąt - będzie on obrócony.

Obracamy układ o kąt -k wokół tego samego wektora. Otrzymamy układ współrzędnych nie obrócony.

Przesuwamy go do miejsca rysowania kwadratu i obracamy o kąt k wokół wektora (1,0,0) - oś x.

Rysujemy kwadrat.

 

Oto odpowiedni kod:

 

   GLfloat z = -3.0f, dz = -0.05;
   GLfloat k = 0, dk = 1;

   for(int done = 0; !done;)
   {
     glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
     glLoadIdentity();

     // Tutaj umieszczamy rysowanie sceny

     glTranslatef(-1.5f,0.0f,z);     // do miejsca rysowania trójkata
     glRotatef(k,0.0f,1.0f,0.0f);    // obrót wokół osi y

     glBegin(GL_TRIANGLES);          // rysujemy obrócony trójkąt

       glVertex2f( 0.0f, 1.0f);
       glVertex2f( 1.0f,-1.0f);
       glVertex2f(-1.0f,-1.0f);

     glEnd();

     glRotatef(-k,0.0f,1.0f,0.0f);  // usuwamy z przekształcenia poprzedni obrót
     glTranslatef(3.0f,0.0f,0.0f);  // do miejsca rysowania kwadratu
     glRotatef( k,1.0f,0.0f,0.0f);  // obrót wokół osi x

     glBegin(GL_QUADS);             // rysujemy kwadrat

       glVertex2f( 1.0f, 1.0f);
       glVertex2f( 1.0f,-1.0f);
       glVertex2f(-1.0f,-1.0f);
       glVertex2f(-1.0f, 1.0f);

     glEnd();

     z += dz;  // modyfikujemy przesunięcie dla następnego obiegu pętli
     if((z < -10) || (z > -3)) dz = -dz;

     k += dk;  // modyfikujemy kąt obrotu
     if(k > 360) k = 1;

     SDL_GL_SwapBuffers();         // wyświetlamy scenę w oknie podglądu

     SDL_PollEvent(&event);        // sprawdzamy kolejkę zdarzeń
     if(event.key.keysym.sym == SDLK_ESCAPE) done = 1;
   }

Kolor

W OpenGL kolor definiowany jest w modelu RGB. Oznacza to, że barwa tworzona jest z trzech kolorów podstawowych:

Różna ilość natężenia kolorów podstawowych daje różne barwy. Stopień natężenia barwy podstawowej jest określany przez wartość od 0 (brak barwy) do 1 (pełna intensywność).

Kolor wprowadzamy wywołując funkcję glColor3f() przed zdefiniowaniem wierzchołka. Parametrami tej funkcji są trzy liczby określające stopień natężenia barw podstawowych R, G i B. Zmień w programie sposób rysowania trójkąta na:

 

    glBegin(GL_TRIANGLES);           // rysujemy obrócony trójkąt

      glColor3f(  1.0f, 0.0f, 0.0f); // kolor czerwony
      glVertex2f( 0.0f, 1.0f);
      glVertex2f( 1.0f,-1.0f);
      glVertex2f(-1.0f,-1.0f);

    glEnd();

 

Fragment rysujący kwadrat zmień na:

 

    glBegin(GL_QUADS);               // rysujemy kwadrat

      glColor3f(  0.0f, 1.0f, 0.0f); // kolor zielony
      glVertex2f( 1.0f, 1.0f);
      glVertex2f( 1.0f,-1.0f);
      glVertex2f(-1.0f,-1.0f);
      glVertex2f(-1.0f, 1.0f);

    glEnd();

 

Zamiast standardowych białych figur otrzymasz teraz czerwony trójkąt i zielony kwadrat.

 

 

Jeśli kolory będą się zmieniać przy każdym wierzchołku ściany, to OpenGL zastosuje płynne przejście barw. Dokonaj w programie następującej zmiany:

 

    glBegin(GL_TRIANGLES);           // rysujemy obrócony trójkąt

      glColor3f(  1.0f, 0.0f, 0.0f); // kolor czerwony
      glVertex2f( 0.0f, 1.0f);
      glColor3f(  0.0f, 1.0f, 0.0f); // kolor zielony
      glVertex2f( 1.0f,-1.0f);
      glColor3f(  0.0f, 0.0f, 1.0f); // kolor niebieski
      glVertex2f(-1.0f,-1.0f);

    glEnd();

 

oraz

 

    glBegin(GL_QUADS);               // rysujemy kwadrat

      glColor3f(  1.0f, 1.0f, 0.0f); // kolor żółty
      glVertex2f( 1.0f, 1.0f);
      glColor3f(  1.0f, 0.0f, 1.0f); // kolor buraczkowy
      glVertex2f( 1.0f,-1.0f);
      glColor3f(  0.0f, 1.0f, 1.0f); // kolor jasnoniebieski
      glVertex2f(-1.0f,-1.0f);
      glColor3f(  1.0f, 1.0f, 1.0f); // kolor biały
      glVertex2f(-1.0f, 1.0f);

    glEnd();

 

Po uruchomieniu otrzymasz tęczowe barwy na trójkącie i kwadracie:

 

 

Bryły trójwymiarowe

Dotychczas rysowaliśmy pojedyncze ściany, które leżały na płaszczyźnie zawierającej osie OX i OY układu współrzędnych. Do definiowania wierzchołków wykorzystywaliśmy funkcję glVertex2f(), która przyjmuje dwie współrzędne x, y, a trzecią współrzędną z automatycznie zeruje. Jeśli chcemy narysować ścianę w przestrzeni trójwymiarowej, to wierzchołki muszą posiadać trzy współrzędne x, y, z. Do definiowania takich wierzchołków służy podobna funkcja glVertex3f(), która przyjmuje trzy parametry: x, y i z. Najpierw musimy zaprojektować modele naszych figur. Przyjmijmy, że środek układu współrzędnych będzie znajdował się w środku modelu (układ współrzędnych można przecież przesuwać w przestrzeni przed narysowaniem modelu - robiliśmy to wielokrotnie w naszych programach).

Ostrosłup będzie składał się z czterech trójkątnych ścian A (przednia), B (z prawej strony), C (z tyłu) i D (z lewej strony):

 

 

Zmień kod rysowania trójkąta na (najlepiej wprowadzaj kolejne ściany osobno i uruchamiaj program obserwując wynik - jest to bardzo pouczające):

 

    glBegin(GL_TRIANGLES);           // rysujemy obrócony ostrosłup

      // ściana A - z przodu
      glColor3f(  1.0f, 0.0f, 0.0f);
      glVertex3f( 0.0f, 1.0f, 0.0f);
      glVertex3f( 1.0f,-1.0f, 1.0f);
      glVertex3f(-1.0f,-1.0f, 1.0f);

      // ściana B - po prawej
      glColor3f(  0.0f, 1.0f, 0.0f);
      glVertex3f( 0.0f, 1.0f, 0.0f);
      glVertex3f( 1.0f,-1.0f,-1.0f);
      glVertex3f( 1.0f,-1.0f, 1.0f);

      // ściana C - z tyłu
      glColor3f(  0.0f, 0.0f, 1.0f);
      glVertex3f( 0.0f, 1.0f, 0.0f);
      glVertex3f(-1.0f,-1.0f,-1.0f);
      glVertex3f( 1.0f,-1.0f,-1.0f);

      // ściana D - z lewej
      glColor3f(  1.0f, 1.0f, 0.0f);
      glVertex3f( 0.0f, 1.0f, 0.0f);
      glVertex3f(-1.0f,-1.0f, 1.0f);
      glVertex3f(-1.0f,-1.0f,-1.0f);

    glEnd();

 

Sześcian będzie zbudowany z 6 ścian kwadratowych od A do F:

 

 

Zmień kod rysowania kwadratu na:

 

    glBegin(GL_QUADS);               // rysujemy sześcian

      // ściana A - z przodu
      glColor3f(  0.0f, 0.5f, 0.0f);
      glVertex3f(-1.0f, 1.0f, 1.0f);
      glVertex3f( 1.0f, 1.0f, 1.0f);
      glVertex3f( 1.0f,-1.0f, 1.0f);
      glVertex3f(-1.0f,-1.0f, 1.0f);

      // ściana B - z prawej
      glColor3f(  0.5f, 0.0f, 1.0f);
      glVertex3f( 1.0f, 1.0f, 1.0f);
      glVertex3f( 1.0f, 1.0f,-1.0f);
      glVertex3f( 1.0f,-1.0f,-1.0f);
      glVertex3f( 1.0f,-1.0f, 1.0f);

      // ściana C - z tyłu
      glColor3f(  0.5f, 0.5f, 1.0f);
      glVertex3f( 1.0f, 1.0f,-1.0f);
      glVertex3f(-1.0f, 1.0f,-1.0f);
      glVertex3f(-1.0f,-1.0f,-1.0f);
      glVertex3f( 1.0f,-1.0f,-1.0f);

      // ściana D - z lewej
      glColor3f(  0.0f, 1.0f, 0.5f);
      glVertex3f(-1.0f, 1.0f, 1.0f);
      glVertex3f(-1.0f, 1.0f,-1.0f);
      glVertex3f(-1.0f,-1.0f,-1.0f);
      glVertex3f(-1.0f,-1.0f, 1.0f);

      // ściana E - z góry
      glColor3f(  1.0f, 0.5f, 0.5f);
      glVertex3f(-1.0f, 1.0f,-1.0f);
      glVertex3f( 1.0f, 1.0f,-1.0f);
      glVertex3f( 1.0f, 1.0f, 1.0f);
      glVertex3f(-1.0f, 1.0f, 1.0f);

      // ściana F - z dołu
      glColor3f(  1.0f, 1.0f, 0.5f);
      glVertex3f(-1.0f,-1.0f, 1.0f);
      glVertex3f( 1.0f,-1.0f, 1.0f);
      glVertex3f( 1.0f,-1.0f,-1.0f);
      glVertex3f(-1.0f,-1.0f,-1.0f);

    glEnd();

 

 

W ramach ćwiczeń postaraj się pokolorować ściany różnymi gradientami.

Zbliż do siebie sześcian i ostrosłup (jak to zrobić?) i zwróć uwagę na przenikanie się ścian.

Dodaj dodatkowy obrót względem osi pionowej znajdującej się pomiędzy ostrosłupem a sześcianem.

 



List do administratora Serwisu Edukacyjnego Nauczycieli I LO

Twój email: (jeśli chcesz otrzymać odpowiedź)
Temat:
Uwaga: ← tutaj wpisz wyraz  ilo , inaczej list zostanie zignorowany

Poniżej wpisz swoje uwagi lub pytania dotyczące tego rozdziału (max. 2048 znaków).

Liczba znaków do wykorzystania: 2048

 

W związku z dużą liczbą listów do naszego serwisu edukacyjnego nie będziemy udzielać odpowiedzi na prośby rozwiązywania zadań, pisania programów zaliczeniowych, przesyłania materiałów czy też tłumaczenia zagadnień szeroko opisywanych w podręcznikach.



   I Liceum Ogólnokształcące   
im. Kazimierza Brodzińskiego
w Tarnowie

©2017 mgr Jerzy Wałaszek

Dokument ten rozpowszechniany jest zgodnie z zasadami licencji
GNU Free Documentation License.