Oświetlenie graniastosłupa
Myślę, że najpierw musisz przeczytać artykuł o rysowaniu bryły w rzucie aksonometrycznym np aksonometria prostopadła oraz artykuł omawiający jak roztrzygnąć widoczność ściany w wielościanie.
Uzyskamy dynamiczne oświetlenie liczone dla całej ściany (naszą sceną 3D jest tylko graniastosłup).
Natężenie oświetlenia wyrazimy przy pomocy gradientu kolorów długości 90 np:
void paleta_zl()
{
float r,g,b;
float pr=154f/255f,dr=(254f-154f)/255f;
float pg=122f/255f,dg=(252f-122f)/255f;
float pb=40f/255f,db=40f/255f;
for(int i=0;i<91;i++)
{
r=pr+i*dr/91f;
g=pg+i*dg/91f;
b=pb+i*db/91f;
pal[i]=new Color(r,g,b);
}
}
|
Prezentowane podejście jest bardzo proste i potraktuj je jako początek swojej przygody z tym tematem.
Zdefiniowana w sugerowanych na początku artykułach funkacja
widoczna posłuży nam do oświetlenia naszego graniastosłupa.
Oprócz oceny
widoczności, będzie miała zadanie ustalić
numer koloru w gradiencie, którym wyrazimy natężenie oświetlenia wg zasady:
<(kierunek rzutu , wektor normalny ściany) = nr koloru -> im większy kąt tym oświetlenie jest jaśniejsze
|
Algorytm oceniający ścianę :
if(widoczna) zamaluj(pal[kolor])
|
boolean widoczna(int i,int j,int m)
{
pkt3d p,q,N,K;
double a,b,c,iloczyn,i_dl;
p=new pkt3d(0,0,0);q=new pkt3d(0,0,0);
N=new pkt3d(0,0,0);
K=new pkt3d(pkt3d.obs.x,pkt3d.obs.y,pkt3d.obs.z);
p.x=w[i].x-w[j].x; q.x=w[m].x-w[j].x;
p.y=w[i].y-w[j].y; q.y=w[m].y-w[j].y;
p.z=w[i].z-w[j].z; q.z=w[m].z-w[j].z;
N.x=p.y*q.z-p.z*q.y;
N.y=p.x*q.z-p.z*q.x;
N.z=p.x*q.y-p.y*q.x;
iloczyn=N.x*K.x+N.y*K.y+N.z*K.z;
i_dl=mat.dl3(N)*mat.dl3(pkt3d.obs);
kolor=(int)(90d*Math.abs(iloczyn)/i_dl);
return(iloczyn<0);
}
|
Obok wersja funkcji dla
Javy. Zobacz wersję
C++Builder.
W
Javie nie ma
wskaźników, funkcje zwracają tylko jedną wartość - na swoją nazwę.
C++/C jest tu oczywiście bardziej elastyczne.
Zmienna
kolor musi być zmienną
globalną (z tym ostrożnie).
Dla każdego kąta
<(wektor Normalny, wektor Rzutu) otrzymujemy liczbę całkowitą z przedziału
<0,90>
Można więc porównać, że oświetlenie ściany wyrażamy w mierze "kątowej" dla kątów ostrych, liczonej w stopniach.
Teraz zajmiemy się procedurą zamaluj(). Mamy dwa przypadki :
- zamaluj czworokąt tzn ścianę boczną
- zamaluj wielokąt o n-bokach tzn podstawę bryły
|
W
Javie zamalowanie np trójkata (
wsp. całk) wygląda tak:
int[] fx={x1,x2,x3,}, fy={y1,y2,y3};
g.setColor(pal[kolor]);
g.fillPolygon(fx,fy,3);
|
Teraz trzeba to wszystko zorganizować.
1. Zgromadzimy współrzędne wierzchołków w tablicy pkt3d[] w=new pkt3d[30];. Zobacz przykład .
2. Zdefiniujemy funkcję R3 -> C2 oraz procedury "oświetlające" : ściany boczne i podstawy
Point w_sc(int nr)
{
Point p=new Point(0,0);
lo28.pkt rz=new lo28.pkt(0,0);
rz=pkt3d.rzut(w[nr]);
p.x=lo28.cal_x(rz.x);
p.y=lo28.cal_y(rz.y);
return(p);
}
|
void Filuj4(Graphics g,int w1,int w2,int w3,int w4)
{
Point[] u={w_sci(w1),w_sc(w2),w_sc(w3),w_sc(w4)};
int[] fx={u[0].x,u[1].x,u[2].x,u[3].x},
fy={u[0].y,u[1].y,u[2].y,u[3].y};
g.setColor(pal[kolor]);
g.fillPolygon(fx,fy,4);
}
|
Zdefiniuj funkcję
rzut. Jeśli zdecydujesz się na
aksonometrię prostopadłą z zadanym kierunkiem rzutu ciało funkcji może wyglądać:
pkt2d rzut(pkt3d p)
{
return(new pkt2d(p.y*sa+p.x*ca,(p.y*ca-p.x*sa)*sf+p.z*cf));
}
|
Dla podstaw procedura "oświetlająca" może wyglądać np tak :
void FilujP(Graphics g,int kt)
{
Point fig []=new Point[15];
int[] fx=new int[15];
int[] fy=new int[15];
int j=-1;
for(int i=kt;i<kt+ile;i++)
{
j++;
fig[j]=w_sciany(i);
fx[j]=fig[j].x;
fy[j]=fig[j].y;
}
g.setColor(pal[kolor]);
g.fillPolygon(fx,fy,ile);
}
|
3. Zorganizujemy proces oświetlania ścian.
Fragment kodu dotyczący oświetlania podstaw może wyglądać tak:
void podstawy(Graphics g)
{
if(widoczna(2,1,0)) FilujP(g,0);
else
FilujP(g,ile) ;
}
|
Zobacz jeszcze wersję
C++Builder