Ładowanie
popup

Przyspieszanie Frustum Culling

2010-12-01 15:37:00

Dziś mały tip o optymalizacji Frustum Culling(zdefiniowany jest na 6 płaszczyznach) dla OBBox i AABBox (Prostopadłościan dowolnie zorientowany i 'wyrównany' do osi układu współrzędnych). Zacznę od struktury boxów:

class AABBox{ Vec3f min; // maksymalne wartości dla każdej z osi Vec3f max; // minimalne wartości dla każdej z osi }; class OBBox{ Vec3f center; // środek prostopadłościanu Vec3f axis[3]; // wektory opisujące układ współrzędnych Vec3f size; // długośći dla każdej z osi };

Najprostszym sposobem na sprawdzenie zawierania takich boxów w frustumie jest sprawdzenie zawierania każdego z jego wierzchołków dla każdej z 6 płaszczyzn frustuma. Żeby to troszkę przyspieszyć wyznaczam jeden wierzchołek (punkt p), który jest "najdalej" wysunięty względem normalnej danej płaszczyzny i sprawdzam relację tego punktu względem tej płaszczyzny. Jeśli dany wierzchołek znajduje się "nad" płaszczyzną - box znajduje się w zasięgu widzenia. Większość tego typu implementacji np. http://www.lighthouse3d.com/opengl/viewfrustum/index.php?gatest3 oblicza niepotrzebnie czy box przecina się z płaszczyzną - co narzuca niepotrzebny drugi test punkt-płaszczyzna i jest w większości przypadków niepotrzebne.

Wyliczanie punktu p dla AABBox:

// aabb - nasz box // normal - normalna płaszczyzny Point3f p=aabb.min; if(normal.x>=0) p.x=aabb.max.x; if(normal.y>=0) p.y=aabb.max.y; if(normal.z>=0) p.z=aabb.max.z;

Wyliczanie punktu dla OBBox:

// obb - nasz box // normal - normalna płaszczyzny // definiuje punkt poczatkowy (bedziemy go przesuwac wzgledem kazdej osi o odpowienie wartosci) Point3f p=obb.center; for(int i=0;i<3;++i){ float d=obb.axis[i]^normal; // projekcja normalnej wzdluz tej osi (operator ^ wykonuje iloczyn skalarny) // jesli d jest mniejsze od 0 - szukany punkt jest po negatywnej stronie danej osi (-axis[i]) if(d>0.f) p+=obb.axis[i]*obb.size[i]; else p-=obb.axis[i]*obb.size[i]; }

Dysponując takim punktem p, wystarczy sprawdzić czy znajduje się on "nad" płaszczyznami frustuma. Przykładowy kod sprawdzający:

// dla każdej płaszczyzny frustuma: for(int i=0;i<6;++i){ // m_planes - tablica z płaszczyznami if(!m_planes[i].Distance(p)>0) // sprawdzam czy punkt p znajduje się nad płaszczyzną return false; // jeśli nie - box napewno jest niewidoczny (można przerwać sprawdzanie) } return true;

Wykonałem prosty programik testowy, gdzie można sobie sprawdzić działanie tego kodu: frustm.culling.2.zip

Demo Frustum Culling...
Demo Frustum Culling...

Jak widać na tym screenie - przyrost wydajnosci jest, ale oczywiście nie ma większego sensu się bawić w takie optymalizacje - chyba że "dla sportu" ;)

Tagi: Programowanie Engine Optymalizacja | Komentarze (1)

Kod z przyszłości

2010-10-25 14:35:47

Wczoraj spotkała mnie ciekawa niespodzianka. Podczas testowania silnika przy każdej zmianie jednego pliku i próbie skompilowania i uruchomienia programu testowego (który znajduje się w tej samej solucji co silnik i jest zależny od silnika) prawie cały silnik kompilował się od początku. Opcja Minimal Build (czy coś takiego) była włączona... skasowanie plików tymczasowych nie pomogło (chyba że były jeszcze jakieś których nie zauważyłem), restart VS też nic, pozostało ręcznie kompilowanie modyfikowanego pliku i linkowanie zamiast po prostu wciśnięcia F5. Pomyślałem że mu przejdzie np. po restarcie komputera :] Przed wyłączeniem komputera i pójściem spać włączyłem backup dysku... dodam że mój backup kopiuje tylko pliki które mają "nowszą" datę modyfikacji. spojrzałem na log, w którym lista zaktualizowanych plików kopii była pusta, mimo że modyfikowałem kilka plików. Od razu popatrzyłem na da datę systemową, która wskazywała na 2006 rok i podobną godzinę do tej która wtedy była w rzeczywistości (sic!), przyczyną zmiany daty był podłączony dysk przenośny USB który nie wiem czemu przy każdym starcie komputera resetował datę (bez dysku już było ok). Po ustawieniu daty na prawidłową od razu uruchomiłem VS - tym razem IDE kompilowało tylko te pliki które były NAPRAWDĘ zmienione od ostatniej kompilacji, a nie te które tylko mają od tej daty kompilacji "nowszą" datę :)

PS. Wg. mnie Visual Studio powinien się połapać że data systemowa może być zła, skoro kompiluje pliki które zostały zmodyfikowane w "przyszłości".

Tagi: Inne Visual Studio | Dodaj komentarz

Method Chaining

2010-10-04 10:56:39

Dziś napiszę o często stosowanej przeze mnie (i ogólnie w programowaniu wysokopoziomowym) technice programowania, mianowicie - "Method Chaining". Główną jej zaletą jest zwiększenie czytelności kodu, który staje się czytelniejszy i po prostu krótszy. Dobry przykład zastosowania tej techniki znajdziemy w bibliotece jQuery, albo w standardowej bibliotece C++ (choć przez przeładowanie operatorów << i >> można jej od razu nie dostrzec). Polega ona na wywoływaniu kilku funkcji na rzecz danego obiektu w jednej instrukcji. np.:

obiekt.funkcja1().funkcja2().funkcja3();

Powyższy przykład bez zastosowania Method Chaining wyglądał by tak:

obiekt.funkcja1(); obiekt.funkcja2(); obiekt.funkcja3();

Działanie kodu jest proste: każda wywołana funkcja w "łańcuchu" zwraca jako rezultat adres/referencję do obiektu dla którego jest wywoływana (wskaźnik this). Poniżej inny przykład z jakiegoś mojego kodu:

// z wykorzystaniem method chaining geometry.Scale(vec_scale).Translate(trans_vec).Rotate(rotate_vec,rotate_angle).CalculateNormals().Apply(); // bez wykorzystania method chaning geometry.SetScale(vec_scale); geometry.SetTranslate(trans_vec); geometry.SetRotate(rotate_vec,rotate_angle); geometry.SetCalculateNormals();

Kod ten kolejno wykonuje jakieś operacje na modelu o nazwie geometry, czyli kolejno: zmienia skalę modelu, dokonuje transformacji, następnie jest rotacja o zadany kąt według jakiegoś wektora, na koniec wyliczenie wektorów normalnych i wykonanie wcześniej ustalonych obliczeń funkcją apply. Jak widać kod jest bardziej skondensowany i czytelniejszy, a samo jego pisanie jest szybsze i przyjemniejsze (zwłaszcza z pomocą narzędzi typu InteliSense). Przykładowa implementacja klasy Geometry może wyglądać tak:

class Geometry{ private: bool todo_calc_normals,todo_rotate,todo_translate,todo_scale; public: Geometry():todo_calc_normals(false),todo_rotate(false),todo_translate(false),todo_scale(false){} Geometry &CalculateNormals(){ todo_calc_normals=true; return *this; } Geometry &Scale(const Vec3f &iScale){ todo_scale=true; // tu zapisujemy jeszcze gdzies w pamieci wektory 'iScale' return *this; } Geometry &Translate(const Vec3f &iTranslation){ todo_translate=true; // zapisujemy w pamieci 'iTranslation' return *this; } Geometry &Rotate(const Vec3f &iRotation,float iAngle){ todo_rotate=true; // zapisujemy w pamieci wektor 'iRotation' i kat 'iAngle' return *this; } void Apply(){ // na podstawie zebranych danych obliczamy dodane zadania } };

Jak widać kod jest bardzo prosty - całą robotę wykonuje instrukcja return *this - czyli jak wspomniałem - zwracanie referencji do obiektu na którym dalej chcemy wywoływać następną funkcję. Należy pamiętać w powyższym kodzie o odpowiednim ustawieniu flag 'todo_*' i zapamiętywaniu danych wejściowych niezbędnych do zakończenia obliczeń. Dzięki funkcji Apply bardzo łatwo można wprowadzić spore optymalizacje: zamiast osobno przechodzić dane modelu dla policzenia kolejno kilku transformacji na modelu, można na raz dokonać obliczeń raz przechodząc dane.

Technika ta oczywiście nie ogranicza się tylko do wywoływania funkcji jednego obiektu - spore możliwości pokazuje gdy wywołujemy łańcuch metod na kilku różnych obiektach, wspaniale upraszczając kod. Na koniec zamieszczam jeszcze kilka imponujących przykładów z wykorzystaniem biblioteki jQuery które zainspirowały mnie do napisania tej notki:

$('ul.first') .find('.foo').css('background-color', 'red').end() .find('.bar').css('background-color', 'green').end();

Powyższy przykład szuka wszystkich pod-elementów wypunktowania ul o klasie first i zmienia kolor tła na czerwony dla tych z klasą foo i na zielony dla pod-elementów z klasą bar.

$('form#login') .find('label.optional').hide().end() .find('input:password').css('border', '1px solid red').end() .submit(function(){ return confirm('Are you sure you want to submit?'); });

Kod ten wybiera jakiś formularz do logowania (o identyfikatorze login), później ukrywa wszystkie labele o klasie optional tego formularza. Ustawia czerwoną ramkę na pole tekstowe hasła formularza i ustawia mu jeszcze callback wywoływany przy wysłaniu formularza.

Dodatkowo funkcja end pozwala na przedłużenie łańcucha metod cofając wskaźnik elementu na którym pracujemy zwracając referencję do swojego rodzica. Nie trzeba zapamiętywać żadnych wyszukanych zmiennych, czy wyszukiwać elementów kilka razy - wszystko jest wykonane za pomocą jednego łańcucha metod.

przykłady pochodzą z:

Tagi: Programowanie | Komentarze (2)

Preloading obrazków w jQuery

2010-09-29 10:34:04

Dziś mały tip Javascript/jQuery. Kod ten pozwala na wykrycie momentu załadowania do pamięci pobranego obrazka na stronie internetowej. Dodaje go tutaj bo trochę sie nad tym męczyłem, a może się komuś przydać (w programowaniu 'webowym' jestem nowicjuszem ;) ). Od razu przejdę do kodu i krótkiego omówienia co się w tym kodzie dzieje:

function load_image(imgSrc){ // przed ladowaniem obrazka (np. pojawia sie gif z ladowaniem) var image=new Image(); // (1) $(image).load(function(){ // (2) // obrazek zaladowany do pamieci (np. mozna schowac juz gif z ladowaniem) // laduje obrazek z pamieci do naszego elementu img (zmiana atrybutu src) $('#popup_image').attr('src',imgSrc); // (4) // np. pokazuje wczesniej ukryty obrazek $('#popup_image').fadeIn('slow'); // zmiana pozycji rozmiaru obrazka itp. ... // reset callbacka image.onload=function(){}; }).attr('src',imgSrc); // (3) }

PS. Przy okazji polecam wszystkim web-developerom bibliotekę jQuery, zwłaszcza często używających Javascript. Jest to kompaktowa i bardzo dobrze zaprojektowana biblioteka, bardzo przyjemna w obsłudze.

Tagi: Programowanie jQuery Javascript | Komentarze (2)

Kolorowanie

2010-04-24 13:09:56

(dziś trochę "lanie wody" :) )

W przerwie między kodowaniem silnika, nauką C#, modyfikowaniem (nieziemsko tragicznego) kodu osCommerce i innych PHP-owych dziwolągów, postanowiłem podjąć się czegoś innego - bardziej ambitnego - mianowicie: zmienić kolorowanie składni mojego edytora (VC++) :> Od dawna w każdym edytorze używam kolorowania pododnego do klasycznego Borlandowego, które trochę zmieniłem. Oto rezultat:

Mój schemat kolorów Visual C++
Mój schemat kolorów Visual C++

Używam czcionki Lucida Console. Wygląda prawie identycznie jak Consolas, z tym że jest trochę "niższa" i tekst jest bardziej zbity (taki preferuję).

Dla zainteresowanych pliki z ustawieniami kolorowania dla VC++2008 (działają też z VC++2010):

Tagi: Inne Visual Studio | Komentarze (1)

Eksportowanie geometrii z Blender-a

2010-02-16 12:17:34

Ostatnio przerabiałem eksporter geometrii z blendera. aktualny skrypt był bardzo stary i potrzebował natychmiastowej przeróbki, wiec napisałem nowy. Przedstawię kilka porad na podstawie moich ostatnich doświadczeń na tym polu. Używam Blendera 2.49

Najwazniejsze to określić co będziemy eksportować. Czy to będzie jedna siatka, czy cały model (model składa się z x siatek (mesh))?, czy format będzie 'końcowy' czy przetwarzany?, jakie dane eksportować? jak i czy eksportować animacje? jak eksportować materiały? czy to będzie format binarny czy tekstowy? postaram się pomoc w odpowiedzi na niektóre z nich

to mniej - więcej tyle. resztę informacji można znaleźć w dokumentacji Blendera, czy w źródłach innych eksporterów dołączonych do programu.

a to test mojego eksportera porsche: 24000 wierzchołków
a to test mojego eksportera porsche: 24000 wierzchołków
palma 3500, a to sama palma (mojej produkcji :P)
palma 3500, a to sama palma (mojej produkcji :P)

//~

Tagi: Programowanie Engine Blender Python | Komentarze (2)