Jeśli chcesz kogoś zadziwić (zainteresować, zaintrygować), to na pewno pokażesz napisany przez siebie program. Jeśli jednak chcesz wprawić kogoś w kompletne osłupienie, program ów powinien zawierać animację czegoś, i to najlepiej w przestrzeni. Jeśli ponadto włożysz w ten program trochę serca i dobrych pomysłów, powstanie dzieło, które rzuci na kolana każdego, kto nań spojrzy, dzieło, które może być namiastką tak ostatnio modnej rzeczywistości wirtualnej. A jak to zrobić ?
W powszechnie dostępnej w naszym kraju literaturze ( np. [1] ) znajdujemy pewne rozwiązania tego problemu - chyba jednak niezbyt szczęśliwe. Nawet w najlepszej, moim zdaniem, pozycji na polskim rynku, jaką jest [2], nie wszystko zostało jasno przedstawione. Nie wchodząc w szczegóły ww. prac można postawić tezę, że przekształcenia brył w przestrzeni to zagadnienie bardzo proste, wręcz elementarne, jeśli zostanie w tym celu użyta macierzowa reprezentacja punktu i - co za tym idzie - całej bryły.
Obroty bryły wokół dowolnej osi, skalowanie, czyli zmiana rozmiaru, translacja (przesunięcie) oraz eliminacja niewidocznych krawędzi, bo o te elementy transformacji przestrzennej chodzi, najprościej i najskuteczniej jest wykonać, przyjmując następujące założenia :
- Punkt P będzie macierzą o wymiarach 4 x 1 (cztery rzędy i jedna kolumna) o wartości, jak na Rys.1.
- Bryła będzie tablicą (lub listą) punktów (zmienna "figura1" w przykładzie), które w każdym kroku transformacji przestrzennej będą podlegały iloczynowi macierzy P x B (pętla "for" w części głównej programu), gdzie B to macierz złożenia przekształceń o wymiarach 4 x 4 (o tym za moment).
- Na ekranie zostanie wyświetlony rzut bryły na jedną z płaszczyzn (rzut na płaszczyznę XY wymaga jedynie pominięcia współrzędnej Z podczas kreślenia krawędzi - procedura "krawedz").
- Jeśli krawędzi niewidocznych z pozycji obserwatora nie należy rysować lub na którejś ze ścian ma się coś pojawić, trzeba jeszcze tylko sprawdzić, które ściany są w tym momencie niewidoczne - krawędź łącząca dwie niewidoczne ściany jest również niewidoczna. Jeśli zaś chodzi o widoczność ścian, wyznacza się ją na podstawie iloczynu wektorów (Rys.1), za które przyjmuje się krawędzie badanych ścian - zwrot tych wektorów należy ustalić zgodnie z ruchem wskazówek zegara, gdy patrzy się na ścianę z zewnątrz bryły, prostopadle do niej (Rys.2). Gdy wynik iloczynu wektorów, który oczywiście też będzie wektorem, jest zwrócony do obserwatora (zwrot zgodny z regułą śruby prawoskrętnej, czyli wielkość Z dodatnia), oznacza to, że ściana jest niewidoczna - w przeciwnym wypadku jest widoczna.
Macierzą złożenia przekształceń B może być któraś z macierzy przekształceń przestrzennych lub iloczyn tych macierzy. Przedstawia je Rys.1, gdzie:
- S to macierz skalowania, czyli zmiany rozmiaru - pozwala symulować zbliżanie się lub oddalanie obiektu czy zmianę odległości od obserwatora,
- T to macierz translacji, czyli przesunięcia,
- Rx, Ry i Rz - macierze obrotu wokół odpowiednich osi.
Złożenia tych przekształceń wystarczą, aby uzyskać na ekranie wspaniałe i zadziwiające efekty wizualne.
|
|
| Rys.1 | Rys.2 |
Literatura :
- [1] A. Marciniak "Turbo Pascal 5.5"
- [2] Ian 0. Angell "Wprowadzenie do grafiki komputerowej"
Andrzej Postrzednik - PC Kurier 17/1995r.
{ ********************************* }
{ * MACIERZOWE PRZEKSZTAŁCENIA 3D * }
{ * Demo - obrót czworościanu * }
{ ********************************* }
Program P3D; Uses Graph, Crt;
Type Macierz = Array [1..4, 1..4] Of Real;
Var T, Rx, Ry, Rz, S, A, B : Macierz;
Figura1 : Array [0..3] Of Macierz;
Rys1 : Array [0..3] Of Macierz;
Ekr, K : Integer;
{ Mnożenie Macierzy C = A * B }
Procedure Iloczyn_Macierzy(M,N,P: Integer;
A,B : Macierz;
Var C : Macierz);
Var I,J,K : Integer;
Suma : Real;
Begin
For I := 1 To M Do
For J := 1 To P Do
Begin
Suma := 0;
For K := 1 To N Do
Suma := Suma + A[I,K] * B[K,J];
C[I,J] := Suma;
End;
{}
End;
{ Inicjalizacja Macierzy Przekształceń }
Procedure Ustal_Macierz_T(Tx,Ty,Tx: Integer);
Var I,J : Integer;
Begin
For I := 1 To 4 Do
Begin
For J := 1 To 4 Do T[I,J] := 0;
T[I,I] := 1;
End;
T[1,4] := -Tx;
T[2,4] := -Ty;
T[3,4] := -Tz;
End;
Procedure Ustal_Macierz_S(Sx,Sy,Sz : Real);
Var I,J : Integer;
Begin
For I := 1 To 4 Do
Begin
For J := 1 To 4 Do S[I,J] := 0;
S[I,I} := 1;
End;
S[1,1] := Sx;
S[2,2] := Sy;
S[3,3] := Sz;
End;
Procedure Ustal_Macierz_Rx(Alfa: Real);
Var I,J : Integer;
Kat : Real;
Begin
For I := 1 To 4 Do
Begin
For J := 1 To 4 Do Rx[I,J] := 0;
Rx[I,I] := 1;
End;
Kat := Alfa * Pi/180;
Rx[2,2] := Cos(Kat);
Rx[3,3] := Rx[2,2]
Rx[2,3] := Sin(Kat);
Rx[3,2] := Rx[2,3];
End;
Procedure Ustal_Macierz_Ry(Alfa: Real);
Var I,J : Integer;
Kat : Real;
Begin
For I := 1 To 4 Do
Begin
For J := 1 To 4 Do Ry[I,J] := 0;
Ry[I,I] := 1;
End;
Kat := Alfa * Pi/180;
Ry[1,1] := Cos(Kat);
Ry[3,3] := Ry[1,1];
Ry[3,1] := Sin(Kat);
Ry[1,3] := Ry[3,1];
End;
Procedure Ustal_Macierz_Rz(Alfa: Real);
Var I,J : Integer;
Kat : Real;
Begin
For I := 1 To 4 Do
Begin
For J := 1 To 4 Do Rz[I,J] := 0;
Rz[I,I] := 1;
End;
Kat := Alfa * Pi/180;
Rz[1,1] := Cos(Kat);
Rz[2,2] := Rz[1,1];
Rz[1,2] := Sin(Kat);
Rz[2,1] := Rz[1,2];
End;
{ Rysuje zrzutowany na płaszczyznę XY odcinek }
Procedure Krawedz(Var Fig3D: Array Of Macierz;
N1,N2 : Byte; X,Y : Integer);
Var X1,Y1,X2,Y2 : Integer;
Begin
X1 := Round(Fig3D[N1][1,1]);
Y1 := Round(Fig3D[N1][2,1]);
X2 := Round(Fig3D[N2][1,1]);
Y2 := Round(Fig3D[N2][2,1]);
Line(X1 + X, Y1 + Y, X2 + X, Y2 + Y);
End;
{ Określa widoczność ściany na podstawie }
{ numerów wierzchołków liczonych prawoskrętnie }
Procedure Widocznosc(Fig3D: Array Of Macierz;
P1,P2,P3 : Byte): Boolean;
|
Var I : Integer;
Ax,Bx,Ay,By : Real;
Begin
Ax := Fig3D[P2][1,1] - Fig3D[P1][1,1];
Ay := Fig3D[P2][2,1] - Fig3D[P1][2,1];
Bx := Fig3D[P3][1,1] - Fig3D[P2][1,1];
By := Fig3d[P3][2,1] - Fig3D[P2][2,1];
If Ax * Ay < 0 Then Widocznosc := True
Else Widocznosc := False;
End;
{ Ogólna procedura inicjalizująca }
{ wsp. przestrzenne }
Procedure Ustal_Wspolrzedne(Var Fig3D:
Array Of Macierz; N: Byte;
X,Y,Z : Real);
Begin
Fig3D[N][1,1] := X;
Fig3D[N][2,1] := Y;
Fig3D[N][3,1] := Z;
Fig3D[N][4,1] := 1;
End;
{ Konkretne współrzędne początkowe dla }
{ bryły FIGURA1 }
Procedure Ustal_Figure1;
Begin
Ustal_Wspolrzedne(Figura1, 0, 50, -30, -20);
Ustal_Wspolrzedne(Figura1, 1, -50, -30, -20);
Ustal_Wspolrzedne(Figura1, 2, 0, 30, -20);
Ustal_Wspolrzedne(Figua1, 3, 0, 0, 40);
End;
{ Konkretne współrzędne początkowe dla rysunku }
{ na jednej ze ścian }
Procedure Ustal_Rys1;
Begin
Ustal_Wspolrzedne(Rys1, 0, 10, 10, -20);
Ustal_Wspolrzedne(Rys1, 1, -10, 10, -20);
Ustal_Wspolrzedne(Rys1, 2, -10, -10, -20);
Ustal_Wspolrzedne)Rys1, 3, 10, -10, -20);
End;
Procedure Wyswietl_Rys1(X,Y: Integer);
Begin
Krawedz(Rys1, 0, 1, X, Y);
Krawedz(Rys1, 1, 2, X, Y);
Krawedz(Rys1, 2, 3, X, Y);
Krawedz(Rys1, 3, 0, X, Y);
End;
Procedure Wyświetl_Figure1(X,Y: Integer);
Var I,J : Integer;
Sc1,Sc2,Sc3,Sc4 : Boolean;
Begin
{* Określenie widoczności ściany *}
Sc1 := Wodocznosc(Figura1, 0, 2, 1);
Sc2 := Widocznosc(Figura1, 0, 3, 2);
Sc3 := Widocznosc(Figura1, 1, 3, 0);
Sc4 := Widocznosc(Figura1, 3, 1, 2);
{ Wyświetlenie ilustracji na widocznych }
{ scianach }
If Sc1 Then Wyswietl_Rys1(X,Y);
{ Krawędzie pomiędzy poszczególnymi }
{ ścianami }
If Sc1 Or Sc2 Then Krawedz(Figura1, 0, 2, X,Y);
If Sc1 Or Sc3 Then Krawedz(Figura1, 0, 1, X,Y);
If Sc1 Or Sc4 Then Krawedz(Figura1, 1, 2, X,Y);
If Sc2 Or Sc3 Then Krawedz(Figura1, 0, 3, X,Y);
If Sc3 Or Sc4 Then Krawedz(Figura1, 1, 3, X,Y);
If Sc2 Or Sc4 Then Krawedz(Figura1, 2, 3, X,Y);
End;
Procedure OpenGraph;
Var Karta,Tryb : Integer;
Begin
Karta := VGA;
Tryb := VGAMed;
InitGraph(Karta,Tryb,'C:\TP\Bgi');
End;
{ *** Część Główna Programu *** }
Begin
Ustal_Figure1;
Ustal_Rys1;
Ustal_Macierz_T(1,0,0);
Ustal_Macierz_s(1.01, 1.01. 1.01);
Ustal_Macierz_Rz(-5);
Ustal_Macierz_Ry(10);
Ustal_Macierz_Rx(6);
Iloczyn_Macierzy(4 ,4, 4, Ry, Rx, B);
Iloczyn_Macierzy(4, 4, 4, B, S, B);
Iloczyn_Macierzy(4, 4, 4, B, T, B);
Ekr := 0;
OpenGraph;
Repeat
{ wymiana ekranów }
SetVisualPage(Ekr Mod 2);
SetActivePage((Ekr + 1) Mod 2);
Inc(Ekr);
ClearDevice;
Wyswietl_Figure1(420,310);
Delay(10);
{ ** Przekształcaj Bryłę ** }
For K := 0 To 3 Do
Begin
Iloczyn_Macierzy(4,4,1,B,Figura1[K],A);
Figura[K] := A;
End;
{ ** Przekształcaj Rysunek Na Ścianie ** }
For K := 0 To 3 Do
Begin
Iloczyn_Macierzy(4,4,1,B,Rys1[K],A);
Rys1[K] := A;
End;
Until KeyPressed;
CloseGraph;
End.
|

