Prezentowane materiały są przeznaczone dla uczniów szkół ponadgimnazjalnych. Autor artykułu: mgr Jerzy Wałaszek, wersja1.0 |
©2011 mgr
Jerzy Wałaszek
|
Dla prostoty umówmy się, że nasze ściany są kwadratami. Ścianę definiujemy za pomocą następujących struktur:
struct vertex3D { double x,y,z; }; struct vertex2D { Sint32 x,y; }; struct face3D { int v1,v2,v3,v4; Uint32 color; };
Struktura vertex3D definiuje punkt w przestrzeni trójwymiarowej. Punkt ten posiada trzy współrzędne rzeczywiste x,y,z.
Struktura vertex2D służy do definiowania punktu na powierzchni graficznej, która składa się z pikseli. Piksele posiadają współrzędne całkowite, dlatego w strukturze wykorzystywany jest typ całkowity do przechowywania współrzędnych piksela, który odpowiada rzutowi punktu na powierzchnię graficzną.
Struktura face3D definiuje ścianę. Zawiera ona numery punktów, które tworzą wierzchołki ściany oraz kolor powierzchni ściany. Umawiamy się, że wierzchołki v1...v4 są podane zgodnie z ruchem wskazówek zegara, gdy patrzymy na przód ściany.
Przez odległość ściany do punktu obserwatora będziemy rozumieli odległość jej środka od punktu obserwatora:
Przypominam, że punkt obserwatora leży na ujemnej połówce osi Z w odległości d od środka układu współrzędnych.
Środek ściany kwadratowej wyznaczymy ze wzorów:
gdzie x1,y1,z1 i x3,y3,z3 są odpowiednio współrzędnymi wierzchołków v1 i v3.
Mając punkt środka ściany, obliczamy kwadrat odległości do punktu obserwatora wg wzoru (do dalszych obliczeń wystarczy nam kwadrat):
Wzór ten można w prosty sposób wyprowadzić z twierdzenia Pitagorasa jako przekątną równoległoboku, co proponuję dociekliwym.
Odległości liczymy dla wszystkich ścian, a następnie sortujemy te ściany względem malejących odległości - na początku mają się znaleźć ściany najbardziej odległe, a na końcu ściany coraz bliższe punktowi obserwatora. Posortowane ściany rysujemy od najdalszej do najbliższej.
Poniższy program demonstruje tę metodę.
Program wykorzystuje bibliotekę newgfx.
Code::Blocks |
// Zasłanianie ścian // (C)2012 Koło Informatyczne // I LO w Tarnowie //------------------------------- #include "newgfx.h" #include <cmath> const double PI=3.1415926535897; // Definicja typów danych struct vertex3D { double x,y,z; }; struct vertex2D { Sint32 x,y; }; struct face3D { int v1,v2,v3,v4; Uint32 color; }; // Definicja wierzchołków figury const int VN = 16; // Liczba wierzchołków vertex3D V[] = {{-1,-1,-0.5},{-1,-1,0.5}, { 0,-1,-0.5},{ 0,-1,0.5}, { 0, 0,-0.5},{ 0, 0,0.5}, { 1, 0,-0.5},{ 1, 0,0.5}, { 1, 1,-0.5},{ 1, 1,0.5}, { 0, 1,-0.5},{ 0, 1,0.5}, {-1, 1,-0.5},{-1, 1,0.5}, {-1, 0,-0.5},{-1, 0,0.5}}; vertex3D VG[VN]; // Definicja ścian figury const int FN = 14; // Liczba ścian face3D F[] ={{ 0, 1, 3, 2,0xffffff}, { 2, 3, 5, 4,0xff0000}, { 4, 5, 7, 6,0xffff00}, { 6, 7, 9, 8,0x00ff00}, { 9, 8,10,11,0x00ffff}, {11,10,12,13,0x00ffff}, {15,14,12,13,0x0000ff}, { 0, 1,15,14,0x0000ff}, { 0, 2, 4,14,0x7f00ff}, {14, 4,10,12,0x7f00ff}, { 4, 6, 8,10,0x7f00ff}, { 3, 1,15, 5,0xff007f}, { 5,15,13,11,0xff007f}, { 7, 5,11, 9,0xff007f}}; // Tablica współrzędnych ekranowych wierzchołków vertex2D VE[VN]; // Tworzy macierz jednostkową void identity(double x[][4]) { for(int i = 0; i < 4; i++) for(int j = 0; j < 4; j++) x[i][j] = i == j ? 1 : 0; } // Mnoży macierze X = X x Y void multiply(double x[][4], double y[][4]) { double c[4][4]; // macierz na wynik tymczasowy int i,j; // mnożymy C = X x Y for(i = 0; i < 4; i++) for(j = 0; j < 4; j++) c[i][j] = x[i][0]*y[0][j] + x[i][1]*y[1][j] + x[i][2]*y[2][j] + x[i][3]*y[3][j]; // Kopiujemy X = C for(i = 0; i < 4; i++) for(j = 0; j < 4; j++) x[i][j] = c[i][j]; } // Dołącza translację do macierzy przekształcenia void translate(double g[][4], double Tx, double Ty, double Tz) { double t[4][4]; identity(t); // tworzymy w t macierz jednostkową t[3][0] = Tx; // wstawiamy przesunięcia t[3][1] = Ty; t[3][2] = Tz; multiply(g,t); // dołączamy translację do przekształcenia } // Dołącza skalowanie do macierzy przekształcenia void scale(double g[][4], double Sx, double Sy, double Sz) { double s[4][4]; identity(s); // tworzymy w t macierz jednostkową s[0][0] = Sx; // wstawiamy skale s[1][1] = Sy; s[2][2] = Sz; multiply(g,s); // dołączamy skalowanie do przekształcenia } // Dołącza rotację do macierzy przekształcenia void rotate_x(double g[][4], double alpha) { double r[4][4],sa,ca; sa = sin(alpha); ca = cos(alpha); identity(r); // tworzymy w t macierz jednostkową r[1][1] = ca; r[1][2] = sa; r[2][1] = -sa; r[2][2] = ca; multiply(g,r); // dołączamy obrót do przekształcenia } void rotate_y(double g[][4], double alpha) { double r[4][4],sa,ca; sa = sin(alpha); ca = cos(alpha); identity(r); // tworzymy w t macierz jednostkową r[0][0] = ca; r[0][2] = -sa; r[2][0] = sa; r[2][2] = ca; multiply(g,r); // dołączamy obrót do przekształcenia } void rotate_z(double g[][4], double alpha) { double r[4][4],sa,ca; sa = sin(alpha); ca = cos(alpha); identity(r); // tworzymy w t macierz jednostkową r[0][0] = ca; r[0][1] = sa; r[1][0] = -sa; r[1][1] = ca; multiply(g,r); // dołączamy obrót do przekształcenia } int main(int argc, char *argv[]) { int waiting = 1; SDL_Surface * screen; SDL_Event event; double kx = PI, ky = 0, kz = 0; // Kąty pozycji na elipsie double alpha = 0; // Kąt obrotu sześcianu double sx = 0, sy = 100, sz = 6000; // Współrzędne środka elipsy double rx = 700, ry = 600, rz = 5900; // Promienie elipsy double tx,ty,tz; // Wektor przesunięcia double d = 1000; // Punkt obserwatora double m; // Mnożnik SDL_Rect r; // Prostokąt wymazywania double G[4][4]; // Macierz przekształcenia if(!SDL_Init(SDL_INIT_VIDEO)) { atexit(SDL_Quit); screen = SDL_SetVideoMode(1280, 1024, 32, SDL_HWSURFACE | SDL_FULLSCREEN); // Ustawiamy prostokąt wymazywania r.x = r.y = 0; r.w = screen->w; r.h = screen->h; do { // Obliczamy wektor przesunięcia tx = sx + rx * cos(kx); ty = sy + ry * sin(ky); tz = sz + rz * cos(kz); // modyfikujemy kąty kx += 0.005; ky += 0.0075; kz += 0.0125; if(kx > PI+PI) kx = 0; if(ky > PI+PI) ky = 0; if(kz > PI+PI) kz = 0; identity(G); // zerujemy przekształcenie scale(G,500,700,900); // obracamy oś obrotu figury na oś OX rotate_z(G,-PI/4); rotate_y(G,-PI/4); rotate_x(G,-PI/4); rotate_x(G,alpha); // dokonujemy obrotu o kąt alpha rotate_x(G,PI/4); // obracamy oś obrotu do poprzedniego położenia rotate_y(G,PI/4); rotate_z(G,PI/4); translate(G,tx,ty,tz+500); // dołączamy translację, przesuwając obiekt na tor ruchu alpha += 0.05; if(alpha > PI+PI) alpha = 0; // Obliczamy współrzędne ekranowe for(int i = 0; i < VN; i++) { // wyliczamy współrzędne po przekształceniu VG[i].x = G[0][0]*V[i].x + G[1][0]*V[i].y + G[2][0]*V[i].z + G[3][0]; VG[i].y = G[0][1]*V[i].x + G[1][1]*V[i].y + G[2][1]*V[i].z + G[3][1]; VG[i].z = G[0][2]*V[i].x + G[1][2]*V[i].y + G[2][2]*V[i].z + G[3][2]; m = d / (VG[i].z + d); // obliczamy mnożnik VE[i].x = (screen->w >> 1) + m * VG[i].x; VE[i].y = (screen->h >> 1) + m * VG[i].y; } // Tablica odległości ścian double FDist[FN]; // Tablica numerów ścian int FNT[FN]; // Obliczamy odległości ścian for(int i = 0; i < FN; i++) { FNT[i] = i; double x,y,z; x = (VG[F[i].v1].x + VG[F[i].v3].x) / 2; y = (VG[F[i].v1].y + VG[F[i].v3].y) / 2; z = (VG[F[i].v1].z + VG[F[i].v3].z) / 2; FDist[i] = x*x + y*y + (z+d)*(z+d); } // Sortujemy ściany wg odległości for(int j = 0; j < FN - 1; j++) { int pmin = j; for( int i = j+1; i < FN; i++) if(FDist[i] > FDist[pmin]) pmin = i; // Wymieniamy odległości w tablicy FDist[] double xd = FDist[pmin]; FDist[pmin] = FDist[j]; FDist[j] = xd; // Wymieniamy numery ścian w tablicy FNT[] int xn = FNT[pmin]; FNT[pmin] = FNT[j]; FNT[j] = xn; } if(SDL_MUSTLOCK(screen)) SDL_LockSurface(screen); // Rysujemy ściany wg kolejności ich numerów w FNT for(int i = 0; i < FN; i++) { Sint32 fig[100]; // Tworzymy łamaną fig[0] = 5; fig[1] = VE[F[FNT[i]].v1].x; fig[2] = VE[F[FNT[i]].v1].y; fig[3] = VE[F[FNT[i]].v2].x; fig[4] = VE[F[FNT[i]].v2].y; fig[5] = VE[F[FNT[i]].v3].x; fig[6] = VE[F[FNT[i]].v3].y; fig[7] = VE[F[FNT[i]].v4].x; fig[8] = VE[F[FNT[i]].v4].y; fig[9] = VE[F[FNT[i]].v1].x; fig[10] = VE[F[FNT[i]].v1].y; fig[11] = 0; // Łamaną wypełniamy kolorem ściany gfxFillPoly(screen,fig,F[FNT[i]].color); } if(SDL_MUSTLOCK(screen)) SDL_UnlockSurface(screen); SDL_UpdateRect(screen, 0, 0, 0, 0); // Uaktualniamy ekran if(SDL_MUSTLOCK(screen)) SDL_LockSurface(screen); // Wymazujemy szkielet figury SDL_FillRect(screen, &r, 0); if(SDL_MUSTLOCK(screen)) SDL_UnlockSurface(screen); // Czekamy na klawisz ESCAPE if (SDL_PollEvent(&event)) if ((event.type == SDL_QUIT) || ((event.type == SDL_KEYDOWN) && (event.key.keysym.sym == SDLK_ESCAPE))) waiting = 0; } while(waiting); } return 0; } |
I Liceum Ogólnokształcące |
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