![]() |
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