Programowanie 3D cz. II

ADuch

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>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 <- 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: 9n + 16 - n to ilość punktów, natomiast dla metody prymitywnej 12n. Policzymy teraz dla ilu punktów stosowanie macierzy jest opłacalne, rozwiążemy układ równań: 12n > 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 <- oznacza translacje dla poszczególnych współrzędnych.
Sx, Sy, Sz <- skala dla poszczególnych osi.
0 <- 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

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.

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

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

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

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

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

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>)


a tak pozatym przydatne; dałem ci 5