9. Tworzenie cieni płaskich
Transkrypt
9. Tworzenie cieni płaskich
Tworzenie cieni płaskich Pod pojęciem cieni płaskich będziemy rozumieć cienie dwuwymiarowe, to znaczy rzucane na płaskie powierzchnie, ewentualnie na kilka płaskich powierzchni. Opisana poniżej metoda nie nadaje się do tworzenia cieni rzucanych na inne rodzaje powierzchni, np. na sferę, walec itp. Cień płaski można utworzyć poprzez odpowiednią trnasformację świata. Poniżej znajduje się scena wykorzystująca tę metodę: Cień został uzyskany porzez "spłaszczenie" przedmiotu. Stworzymy teraz macierz dokonującą takiej transformacji. . Powyższy rysunek przedstawia odpowiednie rzutowanie. Żólty punkt L to pozycja światła w przestrzeni (lx, ly, lz), czrwony P - to rzutowany funkt (px, py, pz) na płaszczyznę, zaś różowy S to ponkt cienia (sx, sy, sz) odpowiadający zrzutowaniu punktu P. Jak widać punkt p musi być odpowiednio przesunięty wzdłuż różowego wektora. Określimy teraz kierunke i zwrot w jakim to przesunięcie ma być wykonane. Możemy określić współrzędne wektora żółtego biegnącego od pozycji światła do punktu rzutowanego: LP = (px = lx, py - ly, pz - lz) Jeśli jakiś wektor pomnożymy przez dowolna liczbę rzeczywistą nieujemną i różną od zera to otrzymany nowy wektor ma taki sam zwrot i kierunek jak wektor poprzedni, lecz inną długość. Zatem nasz punkt P musimy przesunąć o wektor * LP, gdzie to pewna dodatnia liczba rzeczywista różna od zera. Czyli możemy tak zapisać wektorowo transformację: S = P + * LP Rozpisując to wzdłuż poszczególnych osi otrzymamy trzy równania skalarne: sx = px + * lpx 1) sy = py + * lpy 2) sz = pz + * lpz 3) Dodatkowo wiemy, że punkt cienia (sx, sy, sz) leży na znanej nam płaszczyźnie o równaniu: Ax + By + Cz + D = 0, czyli spełnia to równanie Asx + Bsy + Csz + D=0 4) Podstawmy równania 1), 2), 3) do 4) A(px + * lpx)+ B(py + * lpy) + C(pz + * lpz) + D=0 Wyliczmy stąd wpółczynnik alfa: Apx Bpy Cpz D Apx Alx Bpy Bly Cpz Clz Podstawiając go do 1), 2), 3) otrzymamy równianie transformacji: sx px * ( B * ly C * lz D) py * ( B * lx ) pz * ( C * lx ) D * lx px * ( A) py * ( B ) pz * ( C ) A * lx B * ly C * lz sy px * ( A * ly ) py * ( A * lx C * lz D ) pz * ( C * ly ) D * ly px * ( A) py * ( B) pz * ( C) A * lx B * ly C * lz sy px * ( A * lz ) py * ( B * lz ) pz * ( A * lx B * ly D) D * lz px * ( A) py * ( B) pz * ( C ) A * lx B * ly C * lz Jak widać powyższa transformacja jest nieliniowa (mam na mysli fakt, ze mianownik zależy od współrzędnych punktu P(px, py, pz). Macierz skonstruujemy w następujący sposób - jako sx wpiszemy licznik sx, jako sy wpiszemy licznik sy, jako sz wpiszemy licznik sz. Natowmiast mianownik będzie potraktowany jako współczynnik skalowania. Jeśli jest on równy 1, to nie wnosi nic nowego do transformacji (punkt ukaże się dokładnie pod takimi wpółrzędnymi jakie wyszły z transformacji). Jednak gdzy jest on różny od zera, to każda współrzędna jest przez niego dzielona i dopiero jest rysowany punkt (sx/w. sy/w. sz/w) - czyli to, co nas teraz interesuje. Poniżej przedstawiłem procedurę tworzącą macierz płaskiego cienia (w trochę bardziej zwięzłej postaci). Przyjmuje ona macierz, która będzie modyfikowana, pozycję światła jako wektor i trzy punkty (jako wektory) należące do płaszczyzny, na którą jest rzucany cień. Public Sub MakeShadowMatrix(ByRef DestMat As D3DMATRIX, l As D3DVECTOR, p() As D3DVECTOR) Dim normal As D3DVECTOR 'wektor normalny płaszczyzny, na którą jest rzucany cień Dim D As Single Dim dot As Single 'iloczyn skalarny wektora normalnego płaszczyzny i 'pozycji światła normal = CalcNormal(p(0), p(1), p(2)) D = -(normal.x * p(2).x + normal.y * p(2).y + normal.z * p(2).z) dot = DX.VectorDotProduct(normal, l) + D With DestMat .rc11 = dot - normal.x * l.x .rc21 = -normal.y * l.x .rc31 = -normal.z * l.x .rc41 = -D * l.x .rc12 = -normal.x * l.y .rc22 = dot - normal.y * l.y .rc32 = -normal.z * l.y .rc42 = -D * l.y .rc13 = -normal.x * l.z .rc23 = -normal.y * l.z .rc33 = dot - normal.z * l.z .rc43 = -D * l.z .rc14 = -normal.x .rc24 = -normal.y .rc34 = -normal.z .rc44 = dot - D End With End Sub