Strona wykorzystuje ciasteczka by usprawnić komfort z jej korzystania. Korzystając ze strony akceptujesz naszą Politykę Ciasteczek. X

Podstawy Stage3D część II: poruszanie kamerą i funkcja poinatAt [ekspert]

W poprzedniej części artykułu pisałem jak rozbudować przykład z dokumentacji ActionScript3 na temat grafiki 3D by obsługiwał wyświetlanie wielu obiektów przy jednoczesnym zachowaniu możliwości transformacji każdego obiektu z osobna. Pojawiła się tam również wzmianka o matrycy odpowiedzialnej za kamerę ("view"), która tak naprawdę nie do końca działała jak należy ponieważ brakowało w niej kilku podstawowych ustawień. Tu właśnie zaczyna się schody dla osób które nigdy nie miały styczności z grafiką 3D, ponieważ informacje dostępne w Internecie na temat owych ustawień i jak powinny wyglądać są dość mieszane, szczególnie że prawidłowe poruszanie kamerą będzie zależało od ustawień i implementacji samego środowiska 3D. Dlatego tą cześć artykułu o podstawach Stage3D poświecę prawidłowemu stosowaniu wirtualnej kamery, a w szczególności konstrukcji, pozycjonowaniu i korzystaniu z funkcji pointAt.

Zaczynając gdzie skończyliśmy ostatnim razem (kod źródłowy z poprzedniej części artykułu: Tutorial3D.zip) mamy już całkiem niezły start do zaimplementowanie wirtualnej kamery. Co prawda można dla niej przygotować jakąś osobną klasę, ale matryca na razie w zupełności wystarcza. W każdym razie, matryca kamery jest gotowa i można z niej skorzystać jednak rezultaty transformacji będą co najmniej... nieprzewidywalne, albo przynajmniej innej niż się można było by spodziewać (np. x+=-5 okaże się przesunięciem w prawo zamiast lewo). Wiąże się to z pominięciem pewnego szczegółu w funkcji renderującej w przykładzie dostarczonym przez Adobe, a który może nam przysporzyć kilku nieprzespanych nocy. O co dokładnie chodzi? Zerknijmy jak tworzona jest matryca finalTransform w funkcji render:

finalTransform.identity();   
finalTransform.append(box.getMatrix3D());   
finalTransform.append(world);   
finalTransform.append(view);   
finalTransform.append(projection);
Niby wszystko wydaje się w porządku, ale wystarczy się zatrzymać na chwilę i przyjrzeć dokładnie - powiedzmy, że chcemy aby cały świat był przesunięty w lewo, a kamera w prawo, dlatego ustawiamy world na x = -10, a view na x = 5. W idealnym świecie oznaczało by to, że teraz jest między nimi 15 jednostek różnicy, ale oczywiście w naszej aplikacji tak nie będzie, a wszystko przez to jak powstaje finalTransform. Spójrzmy jeszcze raz, world to przesuniecie o -10 a view o +5:
finalTransform.append(-10);   
finalTransform.append(5);
Wiec jaki będzie wynik? Oczywiście każdy obiekt w programie zostanie najpierw przesunięty w lewo (-10) a potem w prawo (+5), ostatecznie lądując na pozycji x = -5. Niby oczywista oczywistość, ale bardzo ważne jest zrozumienie, że tak naprawdę nie przesuwamy kamery, ale cały świat wokół niej. I to jest ten ważny szczegół: nie przesuwamy kamery w lewo, tylko cały świat w prawo! I prawda jest taka, że można było by to załatwić prostym "po prostu transformuj kamerę w przeciwnym kierunku i będzie ok", ale … to tragiczny pomysł - jeszcze rotacje i pozycje da się tak zmieniać, ale o funkcji poinatAt możesz zapomnieć. Na szczęście właściwie rozwiązanie jest bardzo proste, wystarczy obrócić matrycę widoku:
var camera:Matrix3D = view.clone();  
camera.invert();
Zgadza się, tyle wystarczy. Cała funkcja render będzie teraz wyglądać tak:
private function render(event:Event):void {  
renderContext.clear(.3, .3, .3);  
var camera:Matrix3D = view.clone();  
camera.invert();  
for each(var box:Box3D in vBox) {  
finalTransform.identity();  
finalTransform.append(box.getMatrix3D());  
finalTransform.append(world);  
finalTransform.append(camera);  
finalTransform.append(projection);  
renderContext.setProgramConstantsFromMatrix(Context3DProgramType.VERTEX, 0, finalTransform, true);  
box.render(renderContext);  
}  
renderContext.present();  
}
Te dwie linijki kodu wystarczą by nasza kamera zaczęła działać dokładnie tak jak się tego spodziewamy, przesunięcie view w lewo autentycznie będzie wyglądać jakbyśmy przesunęli kamerę w lewo, a co najważniejsze będziemy mogli skorzystać z funkcji pointAt, o czym napiszę za chwilę. W tym momencie najlepiej byłoby przetestować poruszanie widokiem w rezultacie dostając coś takiego:
(Poruszanie na klawiszach strzałek)
W razie czego możesz skorzystać z kodu na końcu poradnika.
Teraz gdy już wszystko jest poprawnie ustawione, czas wziąć się za funkcję pointAt. Bez wiedzy o tym, że matryce view należy obrócić (funkcją invert) przed użyciem w finalTransform, pointAt nigdy nie działało by jak należy! Jednak to nie koniec problemów, zostaje jeszcze kwestia parametrów które są potrzebne dla pointAt, bo oczywiście domyślne nie będą działać. Te parametry to:
1. Pozycja celu - matryca zostanie zwrócona w kierunku tego punktu.
2. Kierunek zwrotu - innymi słowy kierunek "do przód" bez żadnych transformacji; dla kamery zawsze będzie to Vector3D(0, 0, -1).
3. Kierunek wznoszenia - w którą stronę jest "do góry"; domyślnie Vector3D(0, -1, 0).
To powinno wystarczyć. Poprawnie działający pointAt będzie wyglądać tak:
Niestety, zmiana pozycji nie uwzględnia kierunku w jakim spogląda kamera - zostawmy to na kiedy indziej.
Kod źródłowy: Tutorial3D-2.zip