Prezentowane materiały są przeznaczone dla uczniów szkół ponadgimnazjalnych. Autor artykułu: mgr Jerzy Wałaszek, wersja1.0 |
©2011 mgr
Jerzy Wałaszek
|
I = | 1 | 0 | 0 | 0 | ||
0 | 1 | 0 | 0 | |||
0 | 0 | 1 | 0 | |||
0 | 0 | 0 | 1 |
Code::Blocks |
// 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; } |
Code::Blocks |
// 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]; } |
T = | 1 | 0 | 0 | 0 | ||
0 | 1 | 0 | 0 | |||
0 | 0 | 1 | 0 | |||
Tx | Ty | Tz | 1 |
gdzie:
Tx - przesunięcie na osi OX,
Ty - przesunięcie na osi OY
Tz - przesunięcie na osi OZ
Nasza procedura dołącza macierz przesunięcia do macierzy przekształcenia G, czyli wykonuje operację: G = G x T
Code::Blocks |
// 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 } |
S = | Sx | 0 | 0 | 0 | ||
0 | Sy | 0 | 0 | |||
0 | 0 | Sz | 0 | |||
0 | 0 | 0 | 1 |
gdzie:
Sx - skala na osi OX
Sy - skala na osi OY
Sz - skala na osi OZ
Procedura dołączenia macierzy skalowania do macierzy przekształcenia: G = G x S
Code::Blocks |
// 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 } |
Rx = | 1 | 0 | 0 | 0 | ||
0 | cosα | sinα | 0 | |||
0 | -sinα | cosα | 0 | |||
0 | 0 | 0 | 1 |
Ry = | cosα | 0 | -sinα | 0 | ||
0 | 1 | 0 | 0 | |||
sinα | 0 | cosα | 0 | |||
0 | 0 | 0 | 1 |
Rz = | cosα | sinα | 0 | 0 | ||
-sinα | cosα | 0 | 0 | |||
0 | 0 | 1 | 0 | |||
0 | 0 | 0 | 1 |
gdzie:
α - kąt obrotu względem wybranej osi
Code::Blocks |
// 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 } |
Program wykorzystuje bibliotekę newgfx.
Code::Blocks |
// Przekształcenia 3D // (C)2012 Koło Informatyczne // I LO w Tarnowie //------------------------------- #include "newgfx.h" #include <cmath> #include <list> const double PI=3.1415926535897; // Definicja typów danych struct vertex3D { double x,y,z; }; struct vertex2D { Sint32 x,y; }; struct line3D { int v1,v2; }; // Definicja wierzchołków figury const int VN = 8; // Liczba wierzchołków vertex3D V[] = {{-100, 100, 100},{ 100, 100, 100},{ 100,-100, 100},{-100,-100, 100}, {-100, 100,-100},{ 100, 100,-100},{ 100,-100,-100},{-100,-100,-100}}; // Definicja krawędzi figury const int LN = 12; // Liczba krawędzi line3D L[] = {{0,1},{1,2},{2,3},{3,0},{4,5},{5,6},{6,7},{7,4},{0,4},{1,5},{2,6},{3,7}}; // 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 = 1000; // Współrzędne środka elipsy double rx = 700, ry = 600, rz = 900; // 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,1,2,3); rotate_z(G,-PI/4); rotate_y(G,-PI/4); rotate_x(G,-PI/4); rotate_x(G,alpha); rotate_z(G,PI/4); rotate_y(G,PI/4); rotate_x(G,PI/4); translate(G,tx,ty,tz+500); // dołączamy translację alpha += 0.01; if(alpha > PI+PI) alpha = 0; // Obliczamy współrzędne ekranowe for(int i = 0; i < VN; i++) { double x,y,z; // wyliczamy współrzędne po przekształceniu x = G[0][0]*V[i].x + G[1][0]*V[i].y + G[2][0]*V[i].z + G[3][0]; y = G[0][1]*V[i].x + G[1][1]*V[i].y + G[2][1]*V[i].z + G[3][1]; z = G[0][2]*V[i].x + G[1][2]*V[i].y + G[2][2]*V[i].z + G[3][2]; m = d / (z + d); // obliczamy mnożnik VE[i].x = (screen->w >> 1) + m * x; VE[i].y = (screen->h >> 1) - m * y; } if(SDL_MUSTLOCK(screen)) SDL_LockSurface(screen); // Rysujemy szkielet figury w kolorze białym for(int i = 0; i < LN; i++) { gfxClipWuLine(screen,VE[L[i].v1].x,VE[L[i].v1].y,VE[L[i].v2].x,VE[L[i].v2].y,0xffffff); gfxClipWuLine(screen,VE[L[i].v1].x+1,VE[L[i].v1].y,VE[L[i].v2].x+1,VE[L[i].v2].y,0xffffff); gfxClipWuLine(screen,VE[L[i].v1].x,VE[L[i].v1].y+1,VE[L[i].v2].x,VE[L[i].v2].y+1,0xffffff); gfxClipWuLine(screen,VE[L[i].v1].x+1,VE[L[i].v1].y+1,VE[L[i].v2].x+1,VE[L[i].v2].y+1,0xffffff); } 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; } |
Drobna zmiana w programie umożliwia tworzenie animacji stereoskopowej, którą obserwujemy przy pomocy specjalnych okularów ze szkłem czerwonym (oko lewe) i niebieskozielonym (oko prawe). Koniecznie zaktualizuj bibliotekę newgfx, ponieważ wymagane są nowe funkcje graficzne.
Code::Blocks |
// Przekształcenia 3D // (C)2012 Koło Informatyczne // I LO w Tarnowie //------------------------------- #include "newgfx.h" #include <cmath> #include <list> const double PI=3.1415926535897; // Definicja typów danych struct vertex3D { double x,y,z; }; struct vertex2D { Sint32 x,y; }; struct line3D { int v1,v2; }; // Definicja wierzchołków figury const int VN = 10; // Liczba wierzchołków vertex3D V[] = {{-1,0,-1},{1,0,-1},{1,0,1},{-1,0,1},{0,1.43,0},{-2,-0.5,-2},{2,-0.5,-2},{2,-0.5,2},{-2,-0.5,2},{0,3,0}}; // Definicja krawędzi figury const int LN = 17; // Liczba krawędzi line3D L[] = {{0,1},{1,2},{2,3},{3,0},{0,4},{1,4},{2,4},{3,4},{0,5},{1,6},{2,7},{3,8},{5,6},{6,7},{7,8},{8,5},{4,9}}; // Tablica współrzędnych ekranowych wierzchołków vertex2D VEL[VN],VER[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 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 int espan = 200; // Rozstaw oczu 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.002; ky += 0.001; kz += 0.005; 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,100,100,100); rotate_z(G,-PI/4); rotate_y(G,-PI/4); rotate_x(G,-PI/4); rotate_x(G,alpha); rotate_z(G,PI/4); rotate_y(G,PI/4); rotate_x(G,PI/4); translate(G,tx-espan,ty,tz+500); // dołączamy translację alpha += 0.01; if(alpha > PI+PI) alpha = 0; // Obliczamy współrzędne ekranowe dla oka prawego for(int i = 0; i < VN; i++) { double x,y,z; // wyliczamy współrzędne po przekształceniu x = G[0][0]*V[i].x + G[1][0]*V[i].y + G[2][0]*V[i].z + G[3][0]; y = G[0][1]*V[i].x + G[1][1]*V[i].y + G[2][1]*V[i].z + G[3][1]; z = G[0][2]*V[i].x + G[1][2]*V[i].y + G[2][2]*V[i].z + G[3][2]; m = d / (z + d); // obliczamy mnożnik VER[i].x = (screen->w >> 1) + m * x; VER[i].y = (screen->h >> 1) - m * y; } translate(G,espan<<1,0,0); // przemieszczamy widok w prawo // Obliczamy współrzędne ekranowe dla oka lewego for(int i = 0; i < VN; i++) { double x,y,z; // wyliczamy współrzędne po przekształceniu x = G[0][0]*V[i].x + G[1][0]*V[i].y + G[2][0]*V[i].z + G[3][0]; y = G[0][1]*V[i].x + G[1][1]*V[i].y + G[2][1]*V[i].z + G[3][1]; z = G[0][2]*V[i].x + G[1][2]*V[i].y + G[2][2]*V[i].z + G[3][2]; m = d / (z + d); // obliczamy mnożnik VEL[i].x = (screen->w >> 1) + m * x; VEL[i].y = (screen->h >> 1) - m * y; } if(SDL_MUSTLOCK(screen)) SDL_LockSurface(screen); for(int i = 0; i < screen->w; i += 50) gfxVLine(screen,i,0,0x007f7f,screen->h-1); for(int i = 10; i < screen->w; i += 50) gfxVLine(screen,i,0,0xff0000,screen->h-1); for(int i = 0; i < screen->h; i += 50) gfxHLine(screen,0,i,0x5f5f5f,screen->w-1); // Rysujemy szkielet figury w kolorze czerwonym dla oka lewego // i zielonym dla oka prawego. Linie pokrywające się są rysowane // w kolorze żółtym. for(int i = 0; i < LN; i++) { gfxClipORLine(screen,VER[L[i].v1].x,VER[L[i].v1].y,VER[L[i].v2].x,VER[L[i].v2].y,0x007f7f); gfxClipORLine(screen,VEL[L[i].v1].x,VEL[L[i].v1].y,VEL[L[i].v2].x,VEL[L[i].v2].y,0xff0000); } 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; } |
Poeksperymentuj z tym programem, zmieniaj wartość d oraz span.
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