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>0) and (x2<320) and
(y2>0) and (y2<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 > 9n + 16
3n > 16
n > 16/3
n > 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 <- cosinus konta X
cy <- cosinus konta y
cz <- j.w ale z
sx <- sinus konta X
sy <- sinus konta y
sz <- 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>=0)and(x2<320)and
(y2>=0)and(y2<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
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