Koło informatyczne 2013

Do ćwiczeń utwórz nowy projekt OpenGL.

Obiekty zawierające wstęgi i wachlarze

Każdy obiekt możemy narysować, wykorzystując jedynie ściany trójkątne. Jednakże  w wielu przypadkach prowadzi to do pewnego rodzaju marnotrawstwa czasu przy generacji sceny, szczególnie wtedy, gdy trójkąty przylegają do siebie. O wiele bardziej efektywne jest użycie wstęgi lub wachlarza trójkątów - we wstędze i wachlarzu każdy nowy trójkąt jest definiowany przez jeden dodatkowy wierzchołek, a nie przez trzy. Aby zrealizować to zadanie, musimy nieco przedefiniować dane:

 

Tablica V będzie wciąż zawierała definicje wierzchołków. Każdy element tej tablicy składa się z 3 współrzędnych w przestrzeni, które określają położenie wierzchołka figury.

Tablica S będzie zawierała kolejne numery wierzchołków, które należą do danej ściany.

Tablica C będzie zawierała kolory wierzchołków lub ścian.

Tablica F będzie definiowała rodzaj ściany (wachlarz lub wstęga), liczbę wierzchołków i początek sekwencji wierzchołków w tablicy.

 

Załóżmy, że zechcemy stworzyć definicję kostki o boku równym 1. Środek kostki w środku układu współrzędnych.

 

obrazek

Współrzędne wierzchołków będą następujące:

 

v0 = { 0.5f,-0.5f, 0.5f}
v1 = { 0.5f, 0.5f, 0.5f}
v2 = { 0.5f, 0.5f,-0.5f}
v3 = { 0.5f,-0.5f,-0.5f}
v4 = {-0.5f,-0.5f, 0.5f}
v5 = {-0.5f, 0.5f, 0.5f}
v6 = {-0.5f, 0.5f,-0.5f}
v7 = {-0.5f,-0.5f,-0.5f}

 

Ściany zrealizujemy jako dwa wachlarze trójkątów (jest to jedna z możliwości):

 

obrazek       obrazek

Wachlarz trójkątów zwróconych przodem

1, 2, 3, 0, 4, 5, 6, 2

 

Wachlarz trójkątów zwróconych tyłem

7, 4, 0, 3, 2, 6, 5, 4

S0-7  = { 1, 2, 3, 0, 4, 5, 6, 2 }

S8-15 = { 7, 4, 0, 3, 2, 6, 5, 4 }

 

Teraz możemy zdefiniować tablicę F. Będzie się składała z dwóch elementów, gdyż nasza figura zawiera tylko dwa wachlarze:

 

F0 = {wachlarz, 8 wierzchołków, od S0}
F1 = {wachlarz, 8 wierzchołków, od S8}

 

Na początku programu wpisz kod funkcji rysującej figurę:

 

//---------------------------------------------------------------------------
void Figure3D(int n, bool cmode, GLfloat v[][3], int s[], GLfloat c[][3], int f[][3])
{
  for(int i = 0; i < n; i++)
  {
    if(cmode) glColor3fv(c[i]);    // kolor ściany
    glBegin(f[i][0]);              // wachlarz, wstęga, lub coś innego
      for(int j = 0; j < f[i][1]; j++)
      {
        if(!cmode) glColor3fv(c[s[f[i][2]+j]]); // kolor wierzchołka
        glVertex3fv(v[s[f[i][2]+j]]);
      }
    glEnd();
  }
}
//---------------------------------------------------------------------------

 

Zwróć uwagę, że funkcja rysująca naszą figurę jest bardzo ogólna. Struktura danych określa rodzaj prymitywu, który chcemy użyć. Zatem w figurze możemy dowolnie mieszać wstęgi, wachlarze, wielokąty, itp.

Resztę kodu wprowadź do funkcji Timera:

 

  // Tutaj umieszczamy program dla OpenGL

  static GLfloat alphax = 0;
  static GLfloat alphay = 0;
  static GLfloat alphaz = 0;

  static GLfloat V[][3] = {{ 0.5f,-0.5f, 0.5f},  // v0
                           { 0.5f, 0.5f, 0.5f},  // v1
                           { 0.5f, 0.5f,-0.5f},  // v2
                           { 0.5f,-0.5f,-0.5f},  // v3
                           {-0.5f,-0.5f, 0.5f},  // v4
                           {-0.5f, 0.5f, 0.5f},  // v5
                           {-0.5f, 0.5f,-0.5f},  // v6
                           {-0.5f,-0.5f,-0.5f}}; // v7
                           
  static int S[] = { 1, 2, 3, 0, 4, 5, 6, 2,   // 0..7  wachlarz 1
                     7, 4, 0, 3, 2, 6, 5, 4};  // 8..15 wachlarz 2

  static GLfloat C[][3] = {{1.0f,0.0f,0.0f},   // kolor v0
                           {0.0f,1.0f,0.0f},   // kolor v1
                           {0.0f,0.0f,1.0f},   // kolor v2
                           {1.0f,0.0f,1.0f},   // kolor v3
                           {0.0f,1.0f,1.0f},   // kolor v4
                           {1.0f,1.0f,0.0f},   // kolor v5
                           {1.0f,1.0f,1.0f},   // kolor v6
                           {0.5f,1.0f,0.0f}};  // kolor v7

  static int F[][3] = {{GL_TRIANGLE_FAN,8,0},  // wachlarz 1
                       {GL_TRIANGLE_FAN,8,8}}; // wachlarz 2

  glTranslatef(0.0f,0.0f,-3.0f);
  glRotatef(alphax,1.0f,0.0f,0.0f);
  glRotatef(alphay,0.0f,1.0f,0.0f);
  glRotatef(alphaz,0.0f,0.0f,1.0f);

  Figure3D(2,false,V,S,C,F); // rysujemy figurę

  alphax += 0.5; if(alphax >= 360) alphax = 0;
  alphay += 0.2; if(alphay >= 360) alphay = 0;
  alphaz += 0.3; if(alphaz >= 360) alphaz = 0;

  // Koniec kodu dla OpenGL

 

obrazek

 

W ramach ćwiczeń zaprojektuj sześcian, który będzie zbudowany z dwóch wachlarzy oraz jednej wstęgi trójkątów. A może wykorzystasz czworoboki i wstęgę czworoboków. Wykonaj te ćwiczenia, gdyż uczą one tworzenia figur, co się bardzo nam przyda.

 

Kula

Proste obiekty 3D można definiować ręcznie, na piechotę. Jednakże przy bardziej skomplikowanych staje się to już nieopłacalne. Takim obiektem jest kula. Zadanie zrealizujemy w dwóch krokach. Najpierw określimy wierzchołki na powierzchni kula, a następnie sposób powiązania tych wierzchołków w ściany.

Aby otrzymać kolejne punkty na powierzchni kuli, postępujemy następująco:

 

obrazek

 

Pierwszy i ostatni wierzchołek znajduje się na osi OZ. Zakładamy, że r  = 1. Współrzędne są następujące:

 

vp = {0.0f, 0.0f, 1.0f}
vk = {0.0f, 0.0f,-1.0f}

 

Teraz na osi OZ wyznaczamy kolejne punkty z, które posłużą nam do określenia płaszczyzn tnących naszą kulę. Liczba tych punktów definiuje ilość warstw. Oznaczmy ją przez N.

 

obrazek

obrazek
   

Naszą kulę przecinamy płaszczyzną równoległą do OXY i przechodzącą przez wyznaczony punkt zi. Przecięcie to wyznacza na powierzchni kuli okrąg o promieniu ri.

 

obrazek

 

 

Na tym okręgu wyznaczamy M równoodległych od siebie punktów wg wzoru:

 

obrazek

 

Współrzędna zj dla wszystkich punktów wynosi zi. Dla kolejnych warstw zi otrzymamy punkty leżące na kuli. Poniższy program testuje te zasadę.

 

  // Tutaj umieszczamy program dla OpenGL

  static bool init_data = false;

  const int N = 20; // liczba warstw w osi OZ
  const int M = 22; // liczba punktów na okręgach
  const double PI = 3.14159265358979;

  static GLfloat V[N * M + 2][3];

  if(!init_data)
  {
    init_data = true;

    V[0][0] = V[N*M+1][0] = V[0][1] = V[N*M+1][1] = 0.0f;
    V[0][2] = 1.0f;
    V[N*M+1][2] = -1.0f;

    for(int i = 1; i <= N; i++)
    {
      GLfloat z = cos(i * PI / (N + 1));
      GLfloat r = sin(i * PI / (N + 1));
      for(int j = 0; j < M; j++)
      {
         GLfloat x = r * cos(2 * PI * j / M);
         GLfloat y = r * sin(2 * PI * j / M);
         int idx = 1 + (i - 1) * M + j;
         V[idx][0] = x;
         V[idx][1] = y;
         V[idx][2] = z;
      }
    }
  }

  static GLfloat alphax = 0;
  static GLfloat alphay = 0;
  static GLfloat alphaz = 0;

  glTranslatef(0.0f,0.0f,-3.0f);
  glRotatef(alphax,1.0f,0.0f,0.0f);
  glRotatef(alphay,0.0f,1.0f,0.0f);
  glRotatef(alphaz,0.0f,0.0f,1.0f);

  glBegin(GL_POINTS);

    for(int i = 0; i < N * M + 2; i++)
      glVertex3fv(V[i]);
      
  glEnd();

  alphax += 0.5; if(alphax >= 360) alphax = 0;
  alphay += 0.2; if(alphay >= 360) alphay = 0;
  alphaz += 0.3; if(alphaz >= 360) alphaz = 0;

  // Koniec kodu dla OpenGL

 

obrazek

 

Jeśli masz okulary anaglifowe, to wypróbuj ten program:

 

  // Tutaj umieszczamy program dla OpenGL

  static bool init_data = false;

  const int N = 20; // liczba warstw w osi OZ
  const int M = 22; // liczba punktów na okręgach
  const double PI = 3.14159265358979;

  static GLfloat V[N * M + 2][3];

  if(!init_data)
  {
    init_data = true;

    V[0][0] = V[N*M+1][0] = V[0][1] = V[N*M+1][1] = 0.0f;
    V[0][2] = 1.0f;
    V[N*M+1][2] = -1.0f;

    for(int i = 1; i <= N; i++)
    {
      GLfloat z = cos(i * PI / (N + 1));
      GLfloat r = sin(i * PI / (N + 1));
      for(int j = 0; j < M; j++)
      {
         GLfloat x = r * cos(2 * PI * j / M);
         GLfloat y = r * sin(2 * PI * j / M);
         int idx = 1 + (i - 1) * M + j;
         V[idx][0] = x;
         V[idx][1] = y;
         V[idx][2] = z;
      }
    }
  }

  static GLfloat alphax = 0;
  static GLfloat alphay = 0;
  static GLfloat alphaz = 0;

  glPointSize(4.0f);

  for(int j = -1; j <= 1; j+=2)
  {
    glPushMatrix();                  // zachowujemy macierz przekształcenia

    glTranslatef(-0.1*j,0.0f,-3.0f);

    glColor3f((j==-1),0.5*(j==1),0.5*(j==1));

    glRotatef(alphax,1.0f,0.0f,0.0f);
    glRotatef(alphay,0.0f,1.0f,0.0f);
    glRotatef(alphaz,0.0f,0.0f,1.0f);

    glBegin(GL_POINTS);

      for(int i = 0; i < N * M + 2; i++)
        glVertex3fv(V[i]);

    glEnd();

    glPopMatrix();                  // odtwarzamy macierz przekształcenia
  }
  alphax += 0.5; if(alphax >= 360) alphax = 0;
  alphay += 0.2; if(alphay >= 360) alphay = 0;
  alphaz += 0.3; if(alphaz >= 360) alphaz = 0;

  // Koniec kodu dla OpenGL

 

Program rysuje punkty kuli dwa razy z przesunięciem. Raz na czerwono, a raz na zielono-niebiesko. W wyniku dostajemy obraz anaglifowy kuli, który po zastosowaniu odpowiednich okularów daje wrażenie trójwymiarowe.

 

obrazek

 

Otrzymane punkty możemy połączyć w ściany na kilka różnych sposobów. Pierwszy polega na wykorzystaniu dwóch wachlarzy trójkątów oraz odpowiedniej liczby wstęg czworokątów. Zasadę tę obrazuje poniższy rysunek:

 

obrazek

 

Punkt początkowy łączymy wachlarzem z punktami, które leżą na pierwszym okręgu warstwy podziałowej. Następnie punkty tego okręgu łączymy z odpowiednimi punktami drugiego okręgu za pomocą wstęgi czworoboków. Operację tę kontynuujemy aż do przedostatniego okręgu. Na koniec zamykamy kulę wachlarzem, który łączy ostatni punkt z punktami na ostatnim okręgu podziałowym.

Załóżmy, że mamy N warstw, a każda wyznacza na kuli okrąg podzielony na M punktów. Obliczmy niezbędne rozmiary poszczególnych tablic.

 

V → 2 + N x M
Liczba wszystkich wierzchołków jest równa liczbie warstw przemnożonej przez liczbę punktów na każdej warstwie plus dwa wierzchołki na czubkach kuli (jeden z przodu, drugi z tyłu).

C → 2 + N x M
Jeśli określamy kolory osobne dla każdego wierzchołka, to tablica kolorów musi mieć ten sam rozmiar co V.

S → 2 x (M + 2)  + (N - 1) x (2M + 2)
Tablica definiująca ściany wg tworzących je wierzchołków będzie zawierała definicje dwóch wachlarzy trójkątów oraz N-1 wstęg czworokątów. Wachlarz wymaga jednego wierzchołka startowego oraz M + 1 wierzchołków na okręgu, przy czym pierwszy i ostatni wierzchołek muszą posiadać ten sam numer, aby wachlarz był zamknięty. Daje nam to M+2 elementy. Ponieważ kula będzie się składała z 2 wachlarzy, otrzymujemy 2 x (M + 2).

 

obrazek

 

Dalej uwzględniamy boczne wstęgi czworokątów. Każda wstęga wykorzystuje wszystkie punkty dwóch sąsiednich okręgów. Punkty startowy i końcowy muszą być takie same, aby wstęga się zamknęła na kuli. Dlatego Potrzebujemy 2M + 2 elementy. Wstęg jest N - 1, zatem potrzeba będzie na nie (N - 1) x (2M + 2) elementy.

F → N + 1
Tablica F zawiera informację o dwóch wachlarzach trójkątów oraz o N - 1 wstęgach czworokątów. Zatem musi mieć rozmiar N + 1.

 

Jeśli stosujemy ukrywanie ścian odwróconych tyłem, to musimy pamiętać o odpowiedniej kolejności wierzchołków dla każdej ściany. Po tych wstępnych rozważaniach możemy już napisać program, który będzie tworzył kulę o promieniu 1 i środku w punkcie (0,0,0).

 

// Tutaj umieszczamy program dla OpenGL

  static bool init_data = false;

  const int N = 10; // liczba warstw w osi OZ
  const int M = 20; // liczba punktów na okręgach

  const double PI = 3.14159265358979;

  static GLfloat V[N * M + 2][3]; // wierzchołki
  static GLfloat C[N * M + 2][3]; // kolory wierzchołków
  static int S[2*(M+2) + (N-1)*(2*M+2)]; // ściany
  static int F[N+1][3];           // ściany

  if(!init_data)
  {
    init_data = true;
    int idx, i, j, k;

    // Tworzymy wierzchołki na powierzchni kuli w V
   
    V[0][0] = V[N*M+1][0] = V[0][1] = V[N*M+1][1] = 0.0f;
    V[0][2] = 1.0f;
    V[N*M+1][2] = -1.0f;

    C[N*M+1][0] = C[0][0] = C[N*M+1][1] = C[0][1] = C[N*M+1][2] = C[0][2] = 1.0f;

    for(i = 1; i <= N; i++)
    {
      GLfloat z = cos(i * PI / (N + 1));
      GLfloat r = sin(i * PI / (N + 1));
      for(j = 0; j < M; j++)
      {
         GLfloat x = r * cos(2 * PI * j / M);
         GLfloat y = r * sin(2 * PI * j / M);
         idx = 1 + (i - 1) * M + j;
 
         V[idx][0] = x;
         V[idx][1] = y;
         V[idx][2] = z;

         C[idx][0] = fabs(x);
         C[idx][1] = fabs(1-y);
         C[idx][2] = fabs(1-z);
      }
    }

    // Tworzymy definicje ścian w S i F
    // Przedni wachlarz
    idx = 0;
    F[0][2] = 0;                  // Pozycja w S
    S[idx++] = 0;
    for(i = M; i >= 0; i--) S[idx++] = 1 + (i % M);
    F[0][0] = GL_TRIANGLE_FAN;    // Typ ściany
    F[0][1] = M + 2;              // Liczba wierzchołków

   // boczne wstęgi czworoboków

    for(i = 0; i < N-1; i++)
    {
       F[i+1][2] = idx;           // Pozycja w S
       for(j = 0; j <= M; j++)
       {
         S[idx++] = 1 + (i+1) * M + (j % M);
         S[idx++] = 1 + i * M + (j % M);
       }
       F[i+1][0] = GL_QUAD_STRIP; // Typ ściany
       F[i+1][1] = 2*M+2;         // Liczba wierzchołków
    }

  // Tylni wachlarz

    F[N][2] = idx;                // Pozycja w S
    S[idx++] = N * M+1;
    for(i = 0; i <= M; i++) S[idx++] = 1 + (N - 1) * M + (i % M);
    F[N][0] = GL_TRIANGLE_FAN;    // Typ ściany
    F[N][1] = M + 2;              // Liczba wierzchołków
  }

  static GLfloat alphax = 0;
  static GLfloat alphay =1;
  static GLfloat alphaz = 0;

  glTranslatef(0.0f,0.0f,-3.0f);
  glRotatef(alphax,1.0f,0.0f,0.0f);
  glRotatef(alphay,0.0f,1.0f,0.0f);
  glRotatef(alphaz,0.0f,0.0f,1.0f);

//  glPolygonMode(GL_FRONT,GL_LINE);

  Figure3D(N+1,false,V,S,C,F);    // Rysujemy figurę

  alphax += 0.5; if(alphax >= 360) alphax = 0;
  alphay += 0.2; if(alphay >= 360) alphay = 0;
  alphaz += 0.3; if(alphaz >= 360) alphaz = 0;

  // Koniec kodu dla OpenGL

 

obrazek  obrazek

 

Zmieniając w powyższym kodzie N i M możemy otrzymywać ciekawe figury geometryczne:

 

obrazek obrazek obrazek

N = 1, M = 20

N = 2, M = 20

N = 20, M = 3

 

Wypróbuj też inne wartości i wyciągnij odpowiednie wnioski. A jak uzyskać taki efekt?

 

obrazek

 


 

Drugi sposób tworzenia kuli będzie polegał na stworzeniu ciągu wstęg trójkątów, które będą łączyły kolejne wierzchołki od punktu startowego kuli do punktu końcowego.

 

obrazek

Rozważmy potrzebne rozmiary tablic.

 

V → 2 + N x M
Liczba wszystkich wierzchołków jest taka sama jak w poprzedniej metodzie. Dwa wierzchołki: startowy i końcowy plus M wierzchołków razy N warstw.

C → 2 + N x M
Jeśli określamy kolory osobne dla każdego wierzchołka, to tablica kolorów musi mieć ten sam rozmiar co V.

S → M * (2 * N + 2)
W S będzie zdefiniowane M wstęg trójkątów. W każdej wstędze mamy punkt startowy i końcowy oraz po dwa punkty z każdej warstwy.

F → M
Tablica F zawiera informację o M wstęgach..

 

Wprowadź kod do funkcji Timera:

 

// Tutaj umieszczamy program dla OpenGL

  static bool init_data = false;

  const int N = 10; // liczba warstw w osi OZ
  const int M = 20; // liczba punktów na okręgach

  const double PI = 3.14159265358979;

  static GLfloat V[N * M + 2][3]; // wierzchołki
  static GLfloat C[N * M + 2][3]; // kolory wierzchołków
  static int S[M * (2 * N + 2)];  // ściany
  static int F[M][3];             // ściany

  if(!init_data)
  {
    init_data = true;
    int idx, i, j, k;

    V[0][0] = V[N*M+1][0] = V[0][1] = V[N*M+1][1] = 0.0f;
    V[0][2] = 1.0f;
    V[N*M+1][2] = -1.0f;
    C[N*M+1][0] = C[0][0] = C[N*M+1][1] = C[0][1] = C[N*M+1][2] = C[0][2] = 1.0f;
    for(i = 1; i <= N; i++)
    {
      GLfloat z = cos(i * PI / (N + 1));
      GLfloat r = sin(i * PI / (N + 1));
      for(j = 0; j < M; j++)
      {
         GLfloat x = r * cos(2 * PI * j / M);
         GLfloat y = r * sin(2 * PI * j / M);
         idx = 1 + (i - 1) * M + j;
         V[idx][0] = x;
         V[idx][1] = y;
         V[idx][2] = z;
         C[idx][0] = fabs(x);
         C[idx][1] = fabs(1-y);
         C[idx][2] = fabs(1-z);
      }
    }

    // Definiujemy ściany S i F

    idx = 0;
    for(i = 0; i < M; i++)
    {
      F[i][2] = idx;       // Pozycja w S
      S[idx++] = 0;        // Wierzchołek startowy

      for(j = 0; j < N; j++)
      {
        S[idx++] = 1 + j * M + (i + 1) % M;
        S[idx++] = 1 + j * M + i;
      }

      S[idx++] = N*M+1;   // Wierzchołek końcowy

      F[i][0] = GL_TRIANGLE_STRIP;
      F[i][1] = 2*N+2;
    }
  }

  static GLfloat alphax = 0;
  static GLfloat alphay =1;
  static GLfloat alphaz = 0;

  glTranslatef(0.0f,0.0f,-3.0f);
  glRotatef(alphax,1.0f,0.0f,0.0f);
  glRotatef(alphay,0.0f,1.0f,0.0f);
  glRotatef(alphaz,0.0f,0.0f,1.0f);

// glPolygonMode(GL_FRONT,GL_LINE);

  Figure3D(M,false,V,S,C,F);

  alphax += 0.5; if(alphax >= 360) alphax = 0;
  alphay += 0.2; if(alphay >= 360) alphay = 0;
  alphaz += 0.3; if(alphaz >= 360) alphaz = 0;

  // Koniec kodu dla OpenGL

 

obrazek  obrazek

 

Zastanów się, jak uzyskać poniższe efekty:

 

obrazek  obrazek

 


 

Kolejny program prezentujemy jako swego rodzaju ciekawostkę. Tworzy on animację przekształcenia sześcianu w kulę. Wykorzystuje proste zależności wektorowe oraz symetrie kuli.

 

// Tutaj umieszczamy program dla OpenGL

  const int N = 32;                // rozmiar tablicy - 1 dla pierwszej ściany
  const int L = (N+1)*(N+1);       // długość tablicy

  static GLfloat P[N+1][N+1][3];   // wierzchołki pierwszej ściany
  static GLfloat V[6*L][3];        // wierzchołki
  static GLfloat C[6*L][3];        // kolory
  static int S[6*N*(N+N+2)];       // ściany
  static int F[6*N][3];            // ściany

  static int count = 0;            // licznik klatek
  static int n  = 0;               // liczba podziałów ściany
  static int dn = 1;               // przyrost dla n

  if(!count)
  {
    count = 25;  // szybkośc morfingu, mniej = szybciej

    int test = n + dn;

    if(test > N || test < 1) dn = -dn;
    n += dn;
    int l = (n+1) * (n+1);

    GLfloat p = sqrt(3)/3;        // do wyznaczenie początkowych wierzchołków

    GLfloat z = p;                // dla pierwszej ściany z jest stałe

    for(int i = 0; i <= n; i++)   // wyznaczamy punkty na kuli
    {
      GLfloat y = p - i*2*p/n;

      for(int j = 0; j <= n; j++)
      {
        GLfloat x = -p + j*2*p/n;
        double m = sqrt(x*x + y*y + z*z);
        m = 1 + (1 - m) / m;
        P[i][j][0] = m * x;
        P[i][j][1] = m * y;
        P[i][j][2] = m * z;
      }
    }

    int idx = 0;
    for(int i = 0; i < 6; i++)    // wyznaczone punkty przekształcamy na punkty ścian
    {
      for(int j = 0; j <= n; j++)
        for(int k = 0; k <= n; k++)
        {
          switch(i)
          {
            case 0: V[idx][0] = P[j][k][0];  // x <- x  // ściana przednia, bez zmian
                    V[idx][1] = P[j][k][1];  // y <- y
                    V[idx][2] = P[j][k][2];  // z <- z
                    break;
            case 1: V[idx][0] = P[j][k][0];  // x <- x  // ściana górna
                    V[idx][1] = P[j][k][2];  // y <- z
                    V[idx][2] =-P[j][k][1];  // z <--y
                    break;
            case 2: V[idx][0] = P[j][k][0];  // x <- x  // ściana tylna
                    V[idx][1] =-P[j][k][1];  // y <--y
                    V[idx][2] =-P[j][k][2];  // z <--z
                    break;
            case 3: V[idx][0] = P[j][k][0];  // x <- x  // ściana spodnia
                    V[idx][1] =-P[j][k][2];  // y <--z
                    V[idx][2] = P[j][k][1];  // z <- y
                    break;
            case 4: V[idx][0] = P[j][k][2];  // x <- z  // ściana prawa
                    V[idx][1] = P[j][k][1];  // y <- y
                    V[idx][2] =-P[j][k][0];  // z <--x
                    break;
            case 5: V[idx][0] =-P[j][k][2];  // x <--z  // ściana lewa
                    V[idx][1] = P[j][k][1];  // y <- y
                    V[idx][2] = P[j][k][0];  // z <- x
                    break;
          }
          C[idx][0] = (1 + V[idx][0]) / 2;
          C[idx][1] = (1 + V[idx][1]) / 2;
          C[idx][2] = (1 + V[idx][2]) / 2;
          idx++;
        }
    }
    idx = 0;
    for(int i = 0; i < 6; i++)     // punkty łączymy w ściany
    {
      for(int j = 0; j < n; j++)
      {
        F[i*n+j][0] = GL_QUAD_STRIP;
        F[i*n+j][2] = idx;
        for(int k = 0; k <= n; k++)
        {
          S[idx++] = i * l + (j + 1) * (n+1) + k;
          S[idx++] = i * l + j * (n+1) + k;
        }
        F[i*n+j][1] = idx - F[i*n+j][2];
      }
    }
  }

  count--;

  static GLfloat alphax = 0;
  static GLfloat alphay = 0;
  static GLfloat alphaz = 0;

  glTranslatef(0.0f,0.0f,-3.0f);
  glRotatef(alphax,1,0,0);
  glRotatef(alphay,0,1,0);
  glRotatef(alphaz,0,0,1);

  glPolygonMode(GL_FRONT,GL_LINE);

  Figure3D(6*n,false,V,S,C,F);

  alphax += 0.5; if(alphax >= 360) alphax = 0;
  alphay += 0.2; if(alphay >= 360) alphay = 0;
  alphaz += 0.3; if(alphaz >= 360) alphaz = 0;

  // Koniec kodu dla OpenGL

 

obrazek  obrazek  obrazek

 

Następny program dokonuje animacji przekształcenia sześcianu w kulę. Działa on na podobnej zasadzie jak poprzedni.

 

  // Tutaj umieszczamy program dla OpenGL

  static bool init_data = false;

  const int N = 16;                // rozmiar tablicy - 1 dla pierwszej ściany
  const int L = (N+1)*(N+1);       // długość tablicy

  static GLfloat P[N+1][N+1][3];   // wierzchołki pierwszej ściany
  static GLfloat V[6*L][3];        // wierzchołki
  static GLfloat C[6*L][3];        // kolory
  static int S[6*N*(N+N+2)];       // ściany
  static int F[6*N][3];            // ściany

  static int count = 50;           // licznik klatek animacji
  static int ncount = 0;           // licznik klatek morfingu
  static double M = 0.01;          // współczynnik morfingu

  if(!init_data)
  {
    init_data = true;

    GLfloat p = sqrt(3)/3;

    GLfloat z = p;

    for(int i = 0; i <= N; i++)    // tworzymy punkty siatki przedniej ściany
    {
      GLfloat y = p - i*2*p/N;

      for(int j = 0; j <= N; j++)
      {
        GLfloat x = -p + j*2*p/N;
        P[i][j][0] = x;
        P[i][j][1] = y;
        P[i][j][2] = z;
      }
    }

    int idx = 0;
    for(int i = 0; i < 6; i++)     // tworzymy punkty siatki pozostałych ścian
    {
      for(int j = 0; j <= N; j++)
        for(int k = 0; k <= N; k++)
        {
          switch(i)
          {
            case 0: V[idx][0] = P[j][k][0];  // x <- x
                    V[idx][1] = P[j][k][1];  // y <- y
                    V[idx][2] = P[j][k][2];  // z <- z
                    break;
            case 1: V[idx][0] = P[j][k][0];  // x <- x
                    V[idx][1] = P[j][k][2];  // y <- z
                    V[idx][2] =-P[j][k][1];  // z <--y
                    break;
            case 2: V[idx][0] = P[j][k][0];  // x <- x
                    V[idx][1] =-P[j][k][1];  // y <--y
                    V[idx][2] =-P[j][k][2];  // z <--z
                    break;
            case 3: V[idx][0] = P[j][k][0];  // x <- x
                    V[idx][1] =-P[j][k][2];  // y <--z
                    V[idx][2] = P[j][k][1];  // z <- y
                    break;
            case 4: V[idx][0] = P[j][k][2];  // x <- z
                    V[idx][1] = P[j][k][1];  // y <- y
                    V[idx][2] =-P[j][k][0];  // z <--x
                    break;
            case 5: V[idx][0] =-P[j][k][2];  // x <--z
                    V[idx][1] = P[j][k][1];  // y <- y
                    V[idx][2] = P[j][k][0];  // z <- x
                    break;
          }
          C[idx][0] = 1-j / (double)N;
          C[idx][1] = i / 5.0;
          C[idx][2] = k / (double)N;
          idx++;
        }
    }

    idx = 0;
    for(int i = 0; i < 6; i++)      // tworzymy ściany sześcianu
    {
      for(int j = 0; j < N; j++)
      {
        F[i*N+j][0] = GL_QUAD_STRIP;
        F[i*N+j][2] = idx;
        for(int k = 0; k <= N; k++)
        {
          S[idx++] = i * L + (j + 1) * (N+1) + k;
          S[idx++] = i * L + j * (N+1) + k;
        }
        F[i*N+j][1] = idx - F[i*N+j][2];
      }
    }
  }

  count--;
  if(!count)       // dokonujemy stopniowego przekształcenia sześcianu w kulę
  {
    count = 1;
    ncount++;
    if(ncount == 500)
    {
      ncount = 0;
      count  = 50;
      init_data = false;
    }
    for(int i = 0; i < 6*L; i++)
    {
      double x,y,z,m;
      x = V[i][0];      // współrzędne wektora
      y = V[i][1];
      z = V[i][2];
      m = sqrt(x*x + y*y + z*z); // długość wektora
      m = 1 + M * (1 - m) / m;   // współczynnik mnożenia
      V[i][0] = m * x;           // zmieniamy długość wektora
      V[i][1] = m * y;
      V[i][2] = m * z;
    }
  }

  static GLfloat alphax = 0;
  static GLfloat alphay = 0;
  static GLfloat alphaz = 0;

  glTranslatef(0.0f,0.0f,-3.0f);
  glRotatef(alphax,1,0,0);
  glRotatef(alphay,0,1,0);
  glRotatef(alphaz,0,0,1);

  glPolygonMode(GL_FRONT,GL_LINE);

  Figure3D(6*N,false,V,S,C,F);

  alphax += 0.5; if(alphax >= 360) alphax = 0;
  alphay += 0.2; if(alphay >= 360) alphay = 0;
  alphaz += 0.3; if(alphaz >= 360) alphaz = 0;

  // Koniec kodu dla OpenGL

 

obrazek  obrazek  obrazek

 

Z przedstawionych powyżej rozwiązań wynika, że obiekty 3D można tworzyć na wiele różnych sposobów. Zawsze wybieramy ten sposób, który w danej sytuacji będzie dla nas najbardziej wygodny.

 

Nowe funkcje

glPushMatrix() – zapamiętuje macierz przekształcenia na wewnętrznym stosie.

glPopMatrix() – odtwarza zapamiętaną wcześniej macierz przekształcenia.

 


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

©2024 mgr Jerzy Wałaszek

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

Pytania proszę przesyłać na adres email: i-lo@eduinf.waw.pl

W artykułach serwisu są używane cookies. Jeśli nie chcesz ich otrzymywać,
zablokuj je w swojej przeglądarce.
Informacje dodatkowe