Z pogranicza

Programowanie 3D cz. II

  • 8 komentarzy
  • 1783 odsłony
  • Oceń ten tekst jako pierwszy
Jest to druga część artykułu dotyczącego programowania grafiki 3D. Mam nadzieje że tym razem mi się nie rozjedzie :-P
Dzisiaj zajmiemy się obracaniem pojedynczych punktów i Translacją.

TRANSLACJA.
Translacja jest to po prostu przesunięcie punktów o wektor, czyli kiedy mamy punkt(P) i chcemy go przesunąć o wektor(V) to
Po prostu dodajemy NP=P+V; NP jest to nowy punkt.
Dodawanie wektora:
  NP.x:=P.X+V.X;
  NP.y:=P.y+V.Y;
  NP.z:=P.z+V.z;

To chyba większość osób potrafi, dlatego daruję sobie kod w pascalu.
Translacje stosujemy wtedy gdy chcemy zmienić położenie jakiegoś obiektu. Zazwyczaj środek KAŻDEGO obiektu 3D jest w pozycji obserwatora(0,0,0). Dlaczego? ponieważ ułatwia to obracanie figury dookoła własnej osi. Dopiero po wykonaniu obrotu, używamy translacji o wektor - w ten sposób odsuwamy obiekt od siebie <lol> i na koniec przekształcamy go na 2D i rysujemy.


SKALOWANIE:
 Stosujemy wedle woli :-D jest to banalna operacja pomnożenie każdej współrzędnej punktu przez skale.
 NP.x:=P.X*K;
 NP.y:=P.y*K;
 NP.z:=P.z*K;

  K=(0;1) wtedy obiekt pomniejszamy
  K&gt;1 wtedy powiększamy obiekt.

 Dla poprawnego działania środek figury musi być w pozycji obserwatora.


OBROTY.
Cos(x) - x to kąt o jaki chcemy obrócić dookoła osi X, pozostałe są podobnie odczytywane.

Dookoła osi X:
 NX = X
 NY = Y * cos(x) - Z * sin(x)
 NZ = Y * sin(x) + Z * cos(x)

Dookoła osi Y:
 NX = X * cos(y) + Z * sin(y)
 NY = Y
 NZ = Z * cos(y) - X * sin(y)

Dookoła osi Z:
 NX = X * cos(z) - y * sin(z)
 NY = X * sin(z) + y * cos(z)
 NZ = Z


Mam nadzieje że nie przekręciłem żadnego z wzorów :D Sprawdzałem 5 razy :-P
Jak widać bardzo dużo tu sinusów i cosinusów, dlatego wyliczymy je do przygotowanej wcześniej tablicy.
Obrót o pełne koło u nas nie będzie wynosił 360 stopni ale 256 :D Taka mała redukcja.
procedure Create_SinCos_Table;
var i:longint;
begin
  for i:=0 to 255 do TSin[i] := sin(i*PI/128);
  for i:=0 to 255 do TCos[i] := cos(i*PI/128);
end;


Oczywiście wcześniej deklarujemy dwie tablice:
 TSin, TCos : array[0..255] of single;  


W razie problemów z SINGLE należy na początku programu jeszcze przed słowem program umieścić coś takiego:
{$N+,G+}  dokładnie tak, nie może być żadnych odstępów w tekście!!
Mamy już tablice sinusów i cosinusów więc przejdziemy do zrobienia procedury obracającej punkt dookoła osi X,Y, Z
!!! Uwaga !!! Jest różnica w jakiej kolejności obracamy tzn. X, Y, Z czy np. Y, Z, X &lt;- otrzymamy trochę inny
wynik <lol>

procedure RotXYZ(var x, y, z :single; anx,any,anz : byte);
var tx,ty,tz:single;
 begin
  { dookoła X }
  tx:=X;
  TY:=Y*TCos[anx] - Z*Tsin[anx];
  TZ:=Y*TSin[anx] + Z*TCos[anx];
 
  { dookoła Y }
  X := TX * TCos[any] + TZ * TSin[any];
  Y := TY;
  Z := TZ * TCos[any] - TX * TSin[any];
 
  { dookoła Z }
  TX := X * TCos[anz] - y * TSin[anz];
  TY := X * TSin[anz] + y * TCos[anz];
 
  X:=TX;
  Y:=TY;
 end;


Chyba nie ma w tym nic trudnego kiedy zna się wzory??  może tylko małe objaśnienie.
Procedura przyjmuje  współrzędne X, Y, Z punktu 3D a następnie kąty obrotu dookoła poszczególnych osi.
Należy uważać z jej użyciem ponieważ zastępuje starą pozycje nową! czyli mamy:

x:=10; y:=2; z:=10;
RotXYZ(x,y,z,0,10,33);
{ tutaj X, Y, Z to nie jest już ten punkt na początku tylko obrócony o kąt 10 stopni dookoła osi Y i 33 dookoła osi Z :D }

Dla dociekliwych podam jeszcze kod w asemblerze:
procedure RotXYZ2(var x, y, z :single; anx,any,anz : byte); assembler;
var
 sinx,cosx:single;
 siny,cosy:single;
 sinz,cosz:single;
asm
  { wyciagaj siny i cosy}
  mov ax,seg tsin
  mov es,ax
  lea si,tsin
  lea di,tcos
 
  xor bx,bx
  mov bl,anx
  shl bx,2
  fld dword ptr es:[si+bx]
  fld dword ptr es:[di+bx]
  fstp cosx
  fstp sinx
 
  xor bx,bx
  mov bl,any
  shl bx,2
  fld dword ptr es:[si+bx]
  fld dword ptr es:[di+bx]
  fstp cosy
  fstp siny
 
 
  xor bx,bx
  mov bl,anz
  shl bx,2
  fld dword ptr es:[si+bx]
  fld dword ptr es:[di+bx]
  fstp cosz
  fstp sinz
 
  { dookoła X }
  les si,x
  les di,y
  les bx,z
 
  fld dword ptr es:[di]
  fmul cosx
  fld dword ptr es:[bx]
  fmul sinx
  fsub               { Y }
 
  fld dword ptr es:[di]
  fmul sinx
  fld dword ptr es:[bx]
  fmul cosx
  fadd               { Z }
 
  fstp dword ptr es:[bx]
  fstp dword ptr es:[di]
 
  { dookoła Y }
 
  fld dword ptr es:[si]
  fmul cosy
  fld dword ptr es:[bx]
  fmul siny
  fadd                  { X }
 
  fld dword ptr es:[bx]
  fmul cosy
  fld dword ptr es:[si]
  fmul siny
  fsub                  { Z }
 
  fstp dword ptr es:[BX]
  fstp dword ptr es:[si]
 
  { dookoła Z }
  fld dword ptr [si]
  fmul cosz
  fld dword ptr [di]
  fmul sinz
  fsub                 { x }
 
  fld dword ptr [si]
  fmul sinz
  fld dword ptr [di]
  fmul cosz
  fadd                { y }
 
  fstp dword ptr es:[di]
  fstp dword ptr es:[si]
 end;


Jest on znacznie dłuższy i czas wykonania ma podobny :D raczej trochę na plus ale nie zauważa się tego zbytnio he he. Umiemy już obracać punkt dookoła obserwatora i wyświetlać go na ekranie, to już jest warte podsumowania <lol> Napiszemy program obracający punkt w 3D, jak wiadomo obracamy go dookoła kamery a jak zrobić żeby punkt był widoczny dalej??
tzn. żebyśmy mogli obejrzeć jak kreśli orbitę?? wystarczy przed obliczeniem punktu 2D dodać do Z punktu 3D wartość np 100 jednostek i tyle :D w rezultacie oddalimy go.
{$N+,G+}
program as;
 
var
 x,y,z :single;
 x2,y2 :integer;
 TSin, TCos : array[0..255] of single;
 
procedure Create_SinCos_Table;
var i:longint;
begin
  for i:=0 to 255 do TSin[i] := sin(i*PI/128);
  for i:=0 to 255 do TCos[i] := cos(i*PI/128);
end;
 
procedure RotXYZ(var x, y, z :single; anx,any,anz : byte);
var tx,ty,tz:single;
 begin
  { dookoła X }
  tx:=X;
  TY:=Y*TCos[anx] - Z*Tsin[anx];
  TZ:=Y*TSin[anx] + Z*TCos[anx];
 
  { dookoła Y }
  X := TX * TCos[any] + TZ * TSin[any];
  Y := TY;
  Z := TZ * TCos[any] - TX * TSin[any];
 
  { dookoła Z }
  TX := X * TCos[anz] - y * TSin[anz];
  TY := X * TSin[anz] + y * TCos[anz];
 
  X:=TX;
  Y:=TY;
 end;
 
 
{procedura rysuje pixel w trybie 320x200 256 kolorów }
procedure plot(x,y:integer; k:byte); assembler;
 asm
  mov ax,0a000h
  mov es,ax
  mov di,x
  mov ax,y
  shl ax,6
  add di,ax
  shl ax,2
  add di,ax
  mov al,k
  stosb
 end;
 
{ procedura włancza tryb 320x200 256 kolorów }
procedure Start; assembler;
 asm
  mov ax,0013h
  int 10h
 end;
 
begin
 start;
  x:=0;
  y:=10;
  z:=0;
 Create_SinCos_Table;
 repeat
  RotXYZ(x,y,z,1,1,1);
  x2:=round(x*256/(z+50)+160);
  y2:=round(y*256/(z+50)+100);
  if (x2&gt;0) and (x2<320) and
     (y2>0) and (y2&lt;200) then
                             plot(x2,y2,100);
 until (port[$60]=1);
end.

Jeśli uruchomisz program zobaczysz ładne, pochylone koło :D punkt w każdej kolejce jest obracany o 1 stopień od poprzedniej
pozycji i to kreśli tą orbitę! Dzięki linijką:
  x2:=round(x*256/(z+50)+160);
  y2:=round(y*256/(z+50)+100);

Widzimy naszą pętle dalej: (Z+50) oddala obiekt od obserwatora. Jeśli nie rozumiesz to zmień te dwie linijki na następujące:
  x2:=round(x*256/(z)+160);
  y2:=round(y*256/(z)+100);

i uruchom program :D

Prymitywne obroty mamy już za sobą <lol> Teraz przejdziemy do profesjonalnych zagadnień. Zaczniemy od macierzy. Jest to dwu wymiarowa tablica 3x3 (czasami 4x4) pozwala ona zredukować ilość mnożeń. Np aby obrócić obiekt dookoła 3 osi potrzebowaliśmy 12 mnożeń, dzięki macierzą wystarczy tylko 9 :D czyli o 1/4 mniej heh.. Dobra ale teraz żeby przygotować taką macierz musimy użyć 16 mnożeń. To teraz ktoś może wybuchnąć śmiechem: przygotowanie macierzy 16 mnożeń, obrót za jego pomocą jeszcze 9 czyli w sumie 25!! W metodzie prymitywnej było tylko 12.... Już tłumacze, macierz używana jest do masowych obrotów o ten sam kąt, np. jeśli chcemy obrócić 50 punktów o ten sam kąt to wystarczy przygotować tylko raz macierz a następnie obracać do woli za jego pomocą, czyli rzeczywista ilość mnożeń to: 9*n + 16  - n to ilość punktów, natomiast dla metody prymitywnej 12*n. Policzymy teraz dla ilu punktów stosowanie macierzy jest opłacalne, rozwiążemy układ równań:   12n &gt; 9n + 16
  12n &gt; 9n + 16
   3n &gt; 16
    n &gt; 16/3
    n &gt; 5,(3)

Z tego wynika że 6 punktów jest już opłacalne :-P. Macierz 3D ma następującą postać.
 | Xx Xy Xz |
 | Yx Yy Yz |
 | Zx Zy Zz |


Każda kolumna dotyczy jednej z osi czyli np. Osi X dotyczą (Xx,Yx,Zx) i co nam to daje?
Rozszyfrujmy to: Xx - mały x oznacza którą oś liczymy(x) duży X to na której STAREJ współrzędnej musimy wykonać działanie
żeby wyliczyć nowego(x), jeśli to nie jest dość jasne to patrz poniżej:
 N3d.x := p3d.x*Xx + p3D.y*Yx + p3D.z*Zx;
 N3d.y := p3d.x*Xy + p3D.y*Yy + p3D.z*Zy;
 N3d.z := p3d.y*Xz + p3D.y*Yz + p3D.z*Zz;

Chyba jest to jaśniejsze?? To zrobimy teraz macierz obrotu X:
 X = X
 Y = Y * cos(x) - Z * sin(x)
 Z = Y * sin(x) + Z * cos(x)

Jak widać X pozostaje bez zmian dlatego: Xx=1; Yx=0; Zx=0; czytaj Do nowego X wchodzi cały stary X i nie wchodzą stare Y, Z
Dalej liczymy Y we wzorze nie ma X czyli Xy =0; Y jest pomnożony przez cos[x], czyli Yy=cos(x), Z natomiast mnożony jest przez sin(x) a następnie odjęty więc Zy= -sin(x);
Podobnie trzeba rozbić oś Z;
Wynik:
 cx &lt;- cosinus konta X   
 cy &lt;- cosinus konta y   
 cz &lt;- j.w ale z        
 
 sx &lt;- sinus konta X   
 sy &lt;- sinus konta y   
 sz &lt;- j.w ale z        

Macierz obrotu X
 | 1  0   0  |
 | 0  cx  sx |
 | 0 -sx  cx |


Macierz obrotu Y
 | cy   0 -sy |
 |  0   1   0 |
 | sy   0  cy |


Macierz obrotu Z
 | cz   sz  0 |
 |-sz   cz  0 |
 | sy   0   1 |


Mamy już macierze obrotów dookoła poszczególnych osi. Jak teraz zrobić macie obrotu X, Y, Z ?? Nic trudnego <lol> mnożymy macierze X * Y * Z; Jak się mnoży macierze? Już pisze:

 | a  b  c |   | J  K  L |   | aj+bm+cp,    ak+bn+cq,    al+bo+cr |
 | d  e  f | x | m  n  o | = | dj+em+fp,    dk+en+fq,    dl+eo+fr |
 | g  h  i |   | p  q  r |   | gj+hm+ip,    gk+hn+iq,    gl+ho+ir |


Mnożenie macierzy X*Y
 | 1  0   0  |   | cy   0 -sy |   |    cy,    0,    -sy  |
 | 0  cx  sx | x |  0   1   0 | = | sx*sy,   cx,   sx*cy |
 | 0 -sx  cx |   | sy   0  cy |   | cx*sy,  -sx,   cx*cy |

Mnożenie macierzy XY*Z
 |    cy,    0,    -sy  |   |  cz,  sz,  0 |   
 | sx*sy,   cx,   sx*cy | x | -sz,  cy,  0 | = 
 | cx*sy,  -sx,   cx*cy |   |   0,   0,  1 |   
 
 |          cy*cz,          cy*sz,    -sy | 
=| sx*sy*cz-cx*sz, sx*sy*sz+cx*cz,  sx*cy |
 | cx*sy*cz+sx*sz, cx*sy*sz-sx*cz,  cx*cy |


To coś na końcu to macierz obrotu dookoła 3 osi <lol>
Zdefiniujmy teraz nasz macierz:
type
  matrix = array[0..2,0..2] of single;


Teraz przyda się procedura ustawiająca macierz obrotu:

{
  | maca[0,0]    maca[1,0]    maca[2,0]   |
  | maca[0,1]    maca[1,1]    maca[2,1]   |
  | maca[0,2]    maca[1,2]    maca[2,2]   |
}
 
procedure matrixRotateXYZ(var maca:matrix; anglex,angley,anglez: byte);
begin 
 maca[0,0]:=TCos[angley]*TCos[anglez];   
 maca[1,0]:=Tcos[angley]*TSin[anglez];
 maca[2,0]:=-TSin[angley];
 
 maca[0,1]:=Tsin[anglex]*tsin[angley]*tcos[anglez]-tcos[anglex]*tsin[anglez];
 maca[1,1]:=tsin[anglex]*tsin[angley]*tsin[anglez]+tcos[anglex]*tcos[anglez];
 maca[2,1]:=tsin[anglex]*tcos[angley];
 
 maca[0,2]:=tcos[anglex]*tsin[angley]*tcos[anglez]+tsin[anglex]*tsin[anglez];
 maca[1,2]:=tcos[anglex]*tsin[angley]*tsin[anglez]-tsin[anglex]*tcos[anglez];
 maca[2,2]:=tcos[anglex]*tcos[angley]; 
end;


Mamy już macierz obrotu, teraz żeby go użyć wystarczy do dowolny punkt przemnożyć przez niego:

                | A  B  C | 
[ X, Y, Z ]  x  | D  E  F |  =  [ Xa+Yd+Zg, Xb+Ye+Zh, Xe+Yf+Zi ]
                | G  H  I |
 
przykładzik:
procedure Transform(var maca:matrix; x,y,z:single; var nx, ny, nz:single);
 begin
  nx:=X*maca[0,0] +Y*maca[0,1] +Z*maca[0,2];
  ny:=X*maca[1,0] +Y*maca[1,1] +Z*maca[1,2];
  nz:=X*maca[2,0] +Y*maca[2,1] +Z*maca[2,2];
 end;


Myślę że to nie było trudne...

MACIERZ 4x4;
Można rozbudować macierz 3x3 o translacje i skalowanie poprzez poszerzenie:

 | Xx Xy Xz Tx |
 | Yx Yy Yz Ty |
 | Zx Zy Zz Tz |
 | Sx Sy Sz  0 |


Tx, Ty, Tz &lt;- oznacza translacje dla poszczególnych współrzędnych.
Sx, Sy, Sz &lt;- skala dla poszczególnych osi.
0          &lt;- nieużywane :D

procedure Setmatrix(var maca:matrix; anglex,angley,anglez: byte; Tx,Ty,Tz, Sx, Sy, Sz:single);
begin 
 maca[0,0]:=TCos[angley]*TCos[anglez];   
 maca[1,0]:=Tcos[angley]*TSin[anglez];
 maca[2,0]:=-TSin[angley];
 maca[3,0]:=Tx;
 
 maca[0,1]:=Tsin[anglex]*tsin[angley]*tcos[anglez]-tcos[anglex]*tsin[anglez];
 maca[1,1]:=tsin[anglex]*tsin[angley]*tsin[anglez]+tcos[anglex]*tcos[anglez];
 maca[2,1]:=tsin[anglex]*tcos[angley];
 maca[3,1]:=Ty;
 
 maca[0,2]:=tcos[anglex]*tsin[angley]*tcos[anglez]+tsin[anglex]*tsin[anglez];
 maca[1,2]:=tcos[anglex]*tsin[angley]*tsin[anglez]-tsin[anglex]*tcos[anglez];
 maca[2,2]:=tcos[anglex]*tcos[angley]; 
 maca[3,2]:=Tz;
 
 maca[0,3]:=Sx;
 maca[1,3]:=Sy; 
 maca[2,3]:=Sz;
end;


Powyższa procka ustawi cały macierz. Teraz musimy trochę zmienić prockę transform, ponieważ musimy uwzględnić skalowanie i przesunięcie.

procedure Transform(var maca:matrix; x,y,z:single; var nx, ny, nz:single);
 begin
  nx:=((X*maca[0,0] +Y*maca[0,1] +Z*maca[0,2])*maca[0,3])+maca[3,0];
  ny:=((X*maca[1,0] +Y*maca[1,1] +Z*maca[1,2])*maca[1,3])+maca[3,1];
  nz:=((X*maca[2,0] +Y*maca[2,1] +Z*maca[2,2])*maca[2,3])+maca[3,2];
 end;


Program podsumowujący całość:
{$N+,G+}
program as;
 
const 
 { sześcian jak tego nie widzisz to sobie narysuj układ współrzędnych 3D i zaznacz te punkty }
 fig : array[0..7,1..3] of single=((-5,-5,-5),(-5, 5,-5),
                                   ( 5, 5,-5),( 5,-5,-5),
                                   (-5,-5, 5),(-5, 5, 5),
                                   ( 5, 5, 5),( 5,-5, 5));
 
type
 matrix = array[0..3,0..3] of single; { macierz 4x4 }
 
var
 temp : array[0..7,1..3] of single;   { tu przechowujemy punkty po przekształceniu }
 m:matrix;                            { nasz macierz } 
 
 i : word;
 x2,y2:integer;
 anx,any,anz:byte;                    { kąty obrotu }
 TSin, TCos : array[0..255] of single;{ Tablice sin, cos }
 
{ liczy sin, cos do tablic }
procedure Create_SinCos_Table;
var i:longint;
begin
  for i:=0 to 255 do TSin[i] := sin(i*PI/128);
  for i:=0 to 255 do TCos[i] := cos(i*PI/128);
end;
 
{ ustawai macierz }
procedure Setmatrix(var maca:matrix; anglex,angley,anglez: byte; Tx,Ty,Tz, Sx, Sy, Sz:single);
begin 
 maca[0,0]:=TCos[angley]*TCos[anglez];   
 maca[1,0]:=Tcos[angley]*TSin[anglez];
 maca[2,0]:=-TSin[angley];
 maca[3,0]:=Tx;
 
 maca[0,1]:=Tsin[anglex]*tsin[angley]*tcos[anglez]-tcos[anglex]*tsin[anglez];
 maca[1,1]:=tsin[anglex]*tsin[angley]*tsin[anglez]+tcos[anglex]*tcos[anglez];
 maca[2,1]:=tsin[anglex]*tcos[angley];
 maca[3,1]:=Ty;
 
 maca[0,2]:=tcos[anglex]*tsin[angley]*tcos[anglez]+tsin[anglex]*tsin[anglez];
 maca[1,2]:=tcos[anglex]*tsin[angley]*tsin[anglez]-tsin[anglex]*tcos[anglez];
 maca[2,2]:=tcos[anglex]*tcos[angley]; 
 maca[3,2]:=Tz;
 
 maca[0,3]:=Sx;
 maca[1,3]:=Sy; 
 maca[2,3]:=Sz;
end;
 
 
{ przekształca o macierz }
procedure Transform(var maca:matrix; x,y,z:single; var nx, ny, nz:single);
 begin
  nx:=((X*maca[0,0] +Y*maca[0,1] +Z*maca[0,2])*maca[0,3])+maca[3,0];
  ny:=((X*maca[1,0] +Y*maca[1,1] +Z*maca[1,2])*maca[1,3])+maca[3,1];
  nz:=((X*maca[2,0] +Y*maca[2,1] +Z*maca[2,2])*maca[2,3])+maca[3,2];
 end;
 
{ liczy perspektywę }
procedure pers(x,y,z:single; var x2,y2:integer);
 begin
  x2:=round(x*256/z+160);
  y2:=round(y*256/z+100);
 end;
 
 
{procedura rysuje pixel w trybie 320x200 256 kolorów }
procedure plot(x,y:integer; k:byte); assembler;
 asm
  mov ax,0a000h
  mov es,ax
  mov di,x
  mov ax,y
  shl ax,6
  add di,ax
  shl ax,2
  add di,ax
  mov al,k
  stosb
 end;
 
{ procedura włącza tryb 320x200 256 kolorów }
procedure Start; assembler;
 asm
  mov ax,0013h
  int 10h
 end;
 
{ upłynnia ekran - zwalnia do około 70FPS }
procedure wait; assembler;
 
asm
  push ax
  push dx
  mov dx, $03da
  @wait:
  in al, dx
  and al, $08
  jz @wait
  pop dx
  pop ax
end;
 
{ Czyści ekran }
procedure ClrVir; assembler;
 asm
  mov ax,0A000h
  mov es,ax
  xor di,di
  DB $66
  xor ax,ax
  mov cx,16000d
  DB $66
  rep stosw
 end;
 
{kod programu }
begin
 Create_SinCos_Table;  { wywołanie procki liczącej sin, cos }
 start;                { tryb 13h }
 
 { zeruj konty obrotów }
 anx:=0;     
 any:=0;
 anz:=0;
 
 repeat
  inc(anx); { obracamy dookoła osi X }
  setmatrix(m,anx,any,anz,0,0,30,1,1,1); { ustawiamy macierz, obrotu o kąty anx, any, anz
                                           z wektorem przesunięcia [0,0,30]  czyli oddalamy obiekt
                                           i skali 1:1:1 dla każdej osi - normalne wymiary }
 
  { dla 8 punktów }
  for i:=0 to 7 do
   begin
    transform(m, fig[i,1], fig[i,2], fig[i,3],
                 temp[i,1], temp[i,2], temp[i,3]);   { przekrztałć każdy punkt i zapisz go do temp } 
 
    pers(temp[i,1],temp[i,2],temp[i,3],x2,y2);       { wylicz współrzędną ekranową }
 
     if (x2&gt;=0)and(x2<320)and
        (y2>=0)and(y2&lt;200) then plot(x2,y2,100);     { jeśłi mieści się na ekranie to narysuj }
   end;
   wait;                                             { zwolnij animacje }
   ClrVir;                                           { czyść ekran }
 until port[$60]=1;                                  { czekaj na ESC }
end.



OBRACANIE DOOKOŁA PUNKTU 3D.
Wyobraź sobie że punkt B jest środkiem okręgu, A jest na jego obwodzie, Wszystko co musisz zrobić to sprawić aby środek tego kręgu przypadał na punkt (0,0,0) chyba już wiesz jak? Jeśli nie to już tłumacze, wystarczy.... od punktu A odjąć punkt B i tyle :D. W tym momencie możemy obrócić punkt, i przywrócić poprzednią pozycję środka okręgu czyli A+B;
  NX := (A-B)*M + B; 

Naszym zadaniem jest wpisać go do macierzy :D w obecnej postaci nie możemy tego zrobić :/ przekształćmy więc ten wzór:
 NX := AM - BM + B; 
 NX := AN;

 M jest to macierz 4x4 - tylko obrotu!!!
 N jest to macierz M plus translacja:  -BM + B!!!
 Jak widać włączyliśmy to paskudne wyrażenie po prawej do macierzy jako translacja

procedure Matrix_rot_3Dp(var m:matrix; bx,by,bz:single; anx, any, anz:byte);
var bmx,bmy,bmz:single;
begin
  Setmatrix(m,anx,any,anz, 0,0,0, 1,1,1); { macierz obrotu... }
 
  Transform(m,bx,by,bz, bmx, bmy, bmz);
  bmx:=-bmx+bx;
  bmy:=-bmy+by;
  bmz:=-bmz+bz;
 
  Setmatrix(m,anx,any,anz, bmx,bmy,bmz, 1,1,1); { macierz obrotu końcowy :D }
end;


Powyższe algo opracowywałem sam :D może są inne metody, lepsze, szybsze na dokonanie takiego obrotu ale ja jestem z niego dumny :-P

8 komentarzy

darktemplar 2005-07-17 14:06

Procesor numeryczny(możnaby o tym wspomnieć co to te fmule) akurat woli Single, Vogel. Ten zwykły też. A dlaczego nie Comp, albo od razu Extended? Zresztą zawsze możesz kod przerobić pod Double, a nawet pod Extended.

ADuch 2004-12-09 18:07

Dla zainteresowanych, trzecia część artykułu mam zamiar zamieścić najpóźniej pod koniec ferii, na razie ukończyłem pisać, trójkąt FLAT SHADED z 2D clipingiem, dopisze jeszcze GOURAUND i będzie gotowe:P

szepiet 2004-11-21 01:43

hej masz zamiar kontynuowac te artykuly, mowie tu o kolejne czesci z algorytmami elementow zaslonietych, to by sie przydalo pzdr szepiet

AklimX 2004-11-02 17:14

Vogel: a po co aż taka dokładność w 13h ?

Vogel 2004-06-29 17:14

Dlaczego Single a nie Double o większej dokładności?

ADuch 2004-06-05 11:12

Jestem dyslektykiem i czasem robię głupie błędy :-( Staram się je poprawiać ale nie wszystkie wyłapuje....

ŁF 2004-06-04 23:35

macierzY a nie macierza! macierz jest  rodzaju żeńskiego!
kĄt!

artykuł nawet nie najgorszy, ale fatalne (!) błędy ortograficzne, brak formatowania kodu (tagi < delphi>)

nazgul 2004-06-03 19:46

<code=delphi></code> a tak pozatym przydatne; dałem ci 5