Interfejs drop'n'drag w programie prezentującym sceny 3D

0

Witam, jestem w trakcie pisania programu wyświetlającego sceny 3D wygenerowane na przykład z 3dmaxa w postaci plików .brp. Koncept jest taki, żeby zrobić to bez użycia bibliotek do grafiki 3d, a więc rzutowanie perspektywiczne, rysowanie trójkątów i całe sterowanie kamerą chcę napisać sam. Na koniec dopiszę też pewnie raytracer, ale póki co chcę to mieć choć trochę działające (wyświetlanie "gołych" trójkątów - tylko tych widocznych rzecz jasna (weryfikacja wektorów normalnych)).

Chciałem, żeby nawigacja kamerką odbywała się tak jak np. w google earth: jako interfejs drop'n'drag. Scroll ma oddalać i przybliżać scenę, a "łapanie" myszką ma obkręcać sceną na wszystkie strony. Opcję pochylania (w earth: ruszanie myszki z naciśniętym scrollem) sobie podaruję.

Mamy tutaj 3 zdarzenia:
1 - Scroll up
2 - Scroll down
3 - Mouse dragged

Jak to napisać z punktu widzenia grafiki 3D? Proszę bez kodu - najlepiej normalnymi słowami, jeśli jest tu ktoś, kto zna się na grafice, ewentualnie polecenie jakiejś dobrej książki - też najlepiej pisanej normalnymi słowami (nie konkretny język prog.).

Dzięki

0

Z punktu widzenia grafiki 3D używa się akceleracji sprzętowej oferowanej przez biblioteki OpenGL lub Direct 3D. Jednak skoro na zaliczenie masz napisać własny renderer to będzie trochę przy tym roboty... coś jakbyś pisał własne API naśladujące programowo działanie OpenGL'a. Chyba, że ogarniesz całe renderowanie w OpenCL lub CUDA, to będziesz miał "akcelerację sprzętową". Zdarzenia obsłużysz w zależności od użytej technologii. Np. biblioteka SDL ma obsługę takich zdarzeń jakie wymieniłeś i mógłbyś swojego renderer'a zbudować w oparciu o tą bibliotekę (bez podpinania się pod OpenGL, bo tak też można :) ). 3D to po prostu odpowiednie rysowanie 2D w stosownych proporcjach ;)

Nie pisałem nigdy software'owego renderer'a, ale pewnie w SDL przyda Ci się funkcja putpixel - http://info.wsisiz.edu.pl/~szymank0/doku.php?id=public:programowanie:sdl:sdltutorial2

A tutaj znajdziesz jakieś materiały o tworzeniu własnego renderer'a: http://www.gamedev.net/topic/595604-resources-for-writing-a-software-renderer/

A ten interfejs w temacie to powinien być drag and drop, a nie tak jak napisałeś :)

0

Koncept jest taki, żeby zrobić to bez użycia bibliotek do grafiki 3d, a więc rzutowanie perspektywiczne, rysowanie trójkątów i całe sterowanie kamerą chcę napisać sam.

Hmm, no więc Twój problem polega na tym że chcesz napisać całe renderowanie kompletnie od zera?

Proszę bez kodu - najlepiej normalnymi słowami, jeśli jest tu ktoś, kto zna się na grafice, ewentualnie polecenie jakiejś dobrej książki - też najlepiej pisanej normalnymi słowami (nie konkretny język prog.).

Ciężko będzie bez kodu przekazać swoje myśli, ale spróbuję.

No wiec tak, Twój program składa się (będzie się składał) z 3 głównych części:

  • Interfejs użytkownika - zajmuje się przetwarzaniem zdarzeń, typu kliknięcie myszką, odnawianie ekranu, etc. To zakładam że sam zrobisz.
  • Wczytywanie plików graficznych do programu - to pomijasz kompletnie w poście, a niekoniecznie jest proste do zrobienia (zależy od formatu, z bardzo prostych do parsowanie polecam .obj).
  • Renderowanie sceny.

Renderowanie softwarowe nie jest aż takie trudne jak się wydaje:

  • przede wszystkim, musisz mieć jakąś 'kamerę'. A konkretnie, musisz skombinować sobie macierz projekcji. Możesz np. podglądnąć jak to robi OpenGL - http://www.songho.ca/opengl/gl_projectionmatrix.html (przy czym OpenGL stosuje praworęczny system współrzędnych, a taki DirectX - leworęczny. Musisz zwracać na to uwagę w takich sytuacjach, bo macierze wyglądają przez to inaczej).
  • Mając macierz projekcji, chcesz narysować model na ekranie. Robisz to rysując po kolei wszystkie trójkąty. W tym celu, dla każdego trójkąta:
  • Mnożysz wszystkie jego wierzchołki przez macierz projekcji.
  • Jeśli trójkąty są posortowane, odrzucasz współrzędną Z. Jeśli nie, masz problem, bo musisz zainwestować w Depth Buffer (drugi bufor, poza Color Buffer który przechowuje piksele)
  • Rasteryzujesz trójkąty po prostu - przykładowy algorytm tutaj: http://www.codeproject.com/Tips/86354/draw-triangle-algorithm-2D
  • Interpolujesz kolory (i głębię, jeśli masz depth buffer): najlepiej za pomocą współrzędnych barycentrycznych http://mathworld.wolfram.com/BarycentricCoordinates.html - zapisujesz w Color bufferze (czyli po prostu obrazku) (jeśli masz depth buffer, dla każdego piksela sprawszasz czy przypadkiem nie jest dalej niż obecna wartość w depth-bufferze - jesli tak, nie zapisujesz nic do color buffera)

I w sumie tyle. Całe to obracanie, drag&dropy, etc można spokojnie zamknąć w interfejsie tworząc odpowiednio ustawioną kamerę.

0

Hehe faktycznie drag'n'drop :).

Dziękuje za odpowiedzi; Parser mam juz napisany i parsuję sobie pliki .brp z opisem tekstowym bryłki. Składa się on z następujących sekcji:
[liczba punktów np. '531']
[lista punktów np. '-13.339 -30.112 39.656']
[liczba trójkątów np. '1000']
[lista trójkątów z odnośnikami do numeru punktu np. '12 0 4']
[lista materiałów z jakich stworzony jest trójkąt np. '0']
[ilość materiałów np. '1']
[lista właściwości materiału np. 50 39 241 0.4 0.7 1 (pierwsze 3 to RGB, dalsze 3 to kS kD kA czyli wsp. rozpraszania, połyskliwości i zdolność do rozpraszania światła otaczającego)]

To wczytuję sobie do struktury, która mi te dane przechowuje.

Jak widać chcę zrobić model oświetlenia Phonga, ale na razie nie zawracam sobię tym głowy. Problemem jest to jak rysować w panelu owe trójkąty.

Wiem, że trzeba wykonać rzutowanie - logiczna sprawa. Też znany mi jest aspekt "skrętności" układu współrzędnych i w związku z tym dwa typy macierzy do przekształceń/rzutowań.

Jeśli interfejsem jest powiedzmy panel pełnoekranowy 1920 × 1080 to jak rzutować tę scenę, przecież jeśli przemnożę każdy punkt bryły przez macierz rzutowania, otrzymam odpowiedniki tych punktów na rzutnię (2D w formie euklidesowej) no a jeśli projektowana scena jest dużo większa niż mój panel? Właśnie o to mi chodzi jak to "przeskalować"... Druga sprawa to kontrola wielkości rzutni (zoom) to obsłuży zdarzenie scrolla, ale konkretnie jakie matematyczne działanie zostanie wykonane? Przeciąganiem myszki obracam samą sceną, czyli obserwator jest cały czas w swoim miejscu (nieruchoma kamera). Doczytałem, że wtedy zmieniam układ współrzędnych sceny, podczas gdy układ współrzędnych obserwatora pozostaje ten sam - jak to dokładniej zrobić? Czy to tylko kwestia mnożenia przez odpowiednią macierz?

0

wszystko jest kwestią mnożenia przez odpowiednią macierz
"zoomowanie" to po prostu zmniejszanie kąta widzenia (tak samo jak w soczewkach lunety)

najlepiej porób sobie coś w directx czy opengl i zobacz jak to wygląda
ogólnie każdy punkt mnożysz przez macierz świata (to załatwia obracanie obiektów, przesuwanie, skalowanie - wystarczy przemnożyć przez siebie macierze i można łatwo łączyć te operacje), potem przez macierz widoku (to ustawia kamerę, tj. przesuwa po prostu obiekty względem kamery i umożliwia rozglądanie się i przemieszanie; po przemnożeniu przez tą macierz masz też rozeznanie które obiekty znajdują się przed, a które ZA kamerą - po prostu te za kamerą mają ujemną współrzędną Z) a na końcu przez macierz projekcji (to zamienia współrzędne 3D na współrzędne 2D ekranu, przy czym wyliczone współrzędne są w przedziałach <0; 1>; wszystko co nie mieści się w kwadracie [0, 0], [1, 1] jest poza ekranem; żeby uzyskać współrzędne na ekranie, wystarczy pomnożyć taką współrzędną razy rozdzielczość ekranu - np. punkt (0.23, 0.7) na ekranie 1920x1600 znajdzie się w miejscu (442, 1120))

to właśnie w macierzy projekcji masz takie coś jak kąt widzenia i proporcje ekranu - wystarczy zmienić ten jeden argument żeby uzyskać przybliżenie
możesz łatwo znaleźć gotowe wzory na to wszystko

0

Ok, już powoli zaczynam to ogarniać. Kwestia obrotu jednak dalej jest mi obca. Oczywiście gdybym miał obracać bryłę przez przekazanie odpowiednich macierzy przekształceń np. w pliku tekstowym to byłoby proste, bo tak jak mówisz, jest to faktycznie zwykłe mnożenie... Cały gwóźdź tkwi w tym jak to połączyć ze zdarzeniem mouseDragged?

Domyślam się, że po prostu co każdy przesunięty po ekranie pixel z naciśniętym przyciskiem myszy ma być generowana macierz (złożenie macierzy, bo odbywa się to zazwyczaj w kilku płaszczyznach), no a następnie mnożenie i aktualizowanie ekranu (to już proste). Jak wygenerować to złożenie macierzy obrotu dla interfejsu drop'n'drag?

0

http://nehe.gamedev.net/tutorial/arcball_rotation/19003/ - do rotacji zapoznaj się z tym tutorialem :)

0

. Gościu zrobił scenę 3D nie używając z-bufora. Wszystko jest narysowane za pomocą linii bodajże w SDL_gfx. Ale robi masakryczne wrażenie. Trza być cholernie dobrym z matmy żeby takie coś zrobić. Tu masz link do źródeł:
http://www.quake2.info/ostrowsr/fly3d.zip

0

Ok, wyświetlam już rzut ortogonalny :D. Teraz czas na zrobienie rasteryzacji trójkątów. Do analizy widoczności wykorzystam algorytm zBufora. Mam taki ogólnie pseudokod wyświetlania sceny:

fill depth buffer with infinity value;
fill color buffer with background color;
for each triangle t in the scene model
for each pixel (i,j) of the triangle projection on the image
plane;
{
calculate z coordinate of the triangle fragment visible
through pixel (e.g. by using interpolation)
if ( z < depth_buffer[i,j] )
{
depth_buffer[i,j] = z;
calculate color c of the fragment at pixel (i,j)
by using Phong shading – interpolate normals and caluclate
color model;
color_buffer[i,j] = c;
}
}
}

Problem tkwi w linijce "foreach pixel (i,j) of the triangle projection". Wiadomo, że należy przemnożyć każdy trójkąt przez złożenie macierzy MODELVIEW oraz PROJECTION, wtedy otrzymamy skonwertowane vertexy dla obecnego widoku kamery.

Dalej, trzeba iterować w każdym trójkącie, po każdym jego pixelu. Tutaj właśnie leży problem - jak ma wyglądać ta pętla iterująca po każdym pixelu trójkąta? Czy dla każdego z pixeli każdego trójkąta mam interpolować wektor normalny oraz współrzędną Z (no raczej tak, jeśli chcę potem uzyskać cieniowanie realistyczne, np. model Phonga). W takim razie w strukturze trójkąta wystarczy przechować informacje o vertexach i jednym wektorze normalnym dla powierzchni trójkąta?

Mógłby ktoś spróbować napisać do tej części pseudokod?

Jak dokonać analizy widoczności np. w sytuacji, kiedy trójkąt w części znajduje się poza kamerą? Trzeba pewnie jakoś dociąć go, aby uniknąć sytuacji nieprawidłowego wypełnienia bufora np: zBuffer[-2][0], ale to już chyba kwestia kilku warunków am i right?

No i w zasadzie co zapisywać do zBuffora: stricte wartość Z danego pixela (i,j), czy odległość koordynaty Z tego pixela od płaszczyzny ograniczającej widok z przodu (potem sprawdzanie czy pixel jest przed czy za, czy mieści się do tego przed płaszczyzną ograniczającą z tyłu i decyzja czy go narysować)

Jeśli to już zrobię, to arcball będzie kwestią kilku godzinek, a to już będzie finał programu; pozniej może pomyślę, nad przepisaniem tego na shadery (jak ogarnę CUDA)

1 użytkowników online, w tym zalogowanych: 0, gości: 1