Obliczenia w 3D

0

Witajcie,

Od jakiegoś czasu, po godzinach piszę prosty silnik 3D, jednak pojawił się problem z którym nie mogę sobie poradzić. Założeniem było napisanie prostego programu w którym mogę w 3d obracać sześciany. Program jest napisany w C# i problemem jest sposób, w jaki określić współrzędne kursora w tym trójwymiarowym świecie. ;/
Fragment programu wygląda tak:
Program
Nic skomplikowanego, rysownica, kilka suwaków do obrotu kamery, etykiety z aktualnymi wartościami. Na rysownicy niebieskie linie są na współrzędnych o wysokości 0, skrzyżowanie dwóch pomarańczowych linii o punkt 0,0, dwóch czerwonych to środek ekranu.
Program działa w taki sposób, że każdyz punktów jest klasą Vector3D z zmiennymi X,Y,Z Przed ich rysowaniem jest wykonywana funkcja generująca dla każdego punktu współrzędne 2D, które potem są widoczne na rysownicy.
Do wykonywania obrotu geometrii stosuję taki kod:

           if (cam.RotateZ.Value != 0)
           {
               var x = input.X * cam.RotateZ.ValueCosinus + input.Y * cam.RotateZ.ValueSinus;
               var y = input.X * -cam.RotateZ.ValueSinus + input.Y * cam.RotateZ.ValueCosinus;
               input.X = x;
               input.Y = y;
           } 
           if (cam.RotateY.Value != 0)
           {
               var x = input.X * cam.RotateY.ValueCosinus + input.Z * cam.RotateY.ValueSinus;
               var z = input.X * -cam.RotateY.ValueSinus + input.Z * cam.RotateY.ValueCosinus;
               input.X = x;
               input.Z = z;
           }
           if (cam.RotateX.Value != 0)
           {
               var y = input.Y * cam.RotateX.ValueCosinus + input.Z * cam.RotateX.ValueSinus;
               var z = input.Y * -cam.RotateX.ValueSinus + input.Z * cam.RotateX.ValueCosinus;
               input.Y = y;
               input.Z = z;
           }

cam - to inaczej klasa kamery w której mam zapisane wartości sinusów i cosinusów aby ich nie liczyć co chwile tylko podczas zmiany kąta, input, to punkt który jest przeliczany i następnie z niego jest brana współrzędna X,Y która jest rysowana. I to działa. :)

Jak wspomniałem wcześniej, problem jest wyliczenie współrzędnych kursora w 3D.
Robię to w taki sposób:

            if (cam.RotateX.Value != 0)
            {
                input.Y /= cam.RotateX.ValueCosinus;
            }
            if (cam.RotateY.Value != 0)
            {
                input.X /= cam.RotateY.ValueCosinus;
            }
            if (cam.RotateZ.Value != 0)
            {
                var x10 = input.X * cam.RotateZ.InverseValueCosinus + input.Y * cam.RotateZ.InverseValueSinus;
                var y20 = input.X * -cam.RotateZ.InverseValueSinus + input.Y * cam.RotateZ.InverseValueCosinus;
                input.X = x10;
                input.Y = y20;
            }

Ten kod działa, pod warunkiem że nie ma jednoczesnego obrotu w osi X i Y, w przeciwnym wypadku pojawiają się dziwne liczby. Jako dane wejściowe są współrzędne XY kursora z formatki (przez co input.Z = 0). "Inverse" to inaczej wartość kąta w stopniach pomnożona -1.

Czy ktoś mógłby mi podpowiedzieć jak zmienić ten fragment kodu aby wyliczyć poprawnie współrzędne kursora? Spodziewam się że problem jest nieuwzględnienie input.Z ale nie wiem jak to zrobić.

0

Ten kod działa, pod warunkiem że nie ma jednoczesnego obrotu w osi X i Y, w przeciwnym wypadku pojawiają się dziwne liczby.

Może w złej kolejności przeprowadzasz te obroty? Kolejność może mieć znaczenie w tym przypadku. A może coś pomyliłeś z obliczeniami (ale to musiałbyś sprawdzić tam skąd bierzesz (albo wyprowadzasz) wzory (wiem, że to żadna porada w tym momencie, ale mimo wszystko jak coś nie działa, to może od strony matematycznej nie ma to sensu, spróbuj to jakoś rozrysować czy sprawdzić na jakichś stronach o matmie czy na StackOverflow).

Ew. możesz poszukać też informacji o tym, jak się robi rotację na macierzach czy kwaternionach, może będzie łatwiej.
Zapewne też do macierzy i kwaternionów znajdziesz mnóstwo przykładów kodu w sieci, bo w silnikach 3D się używa tych technik.

Czyli ja bym poszukał haseł:
rotation 3d, euler rotation, rotation matrix, quaternions

2

Nie widzę całego programu ale znam problem i w 99% wynika on z tego, że robisz to źle ( wynika to z tego, że optymalizujesz liczenie sinusów, które przy poprawnej implementacji wymagane jest jedynie do obracania kamery a i to tylko w niektórych przypadkach ).

Jeśli możesz wrzucić cały kod programu to zerknę jednak fajnie jak opisałbyś jaką ogólną koncepcję przyjąłeś. Czy chodzisz kamerą po scenie czy np. kręcisz całą sceną o kąty wynikające z parametrów kamery. Niby to samo ale w praktyce wychodzi zupełnie coś innego. Ważne jest także jaki wybrałeś sposób rzutowania na płaszczyznę.

Poprawna metoda to statyczna scena po której rusza się kamera. Przeliczamy tylko te punkty, które mamy w ostrosłupie kamery(+niezbędny margines jeśli mamy trójkąty czyli pozostałe punkty trójkątów, które załapały się na kamerę).
Generalnie tematyka jest z jednej strony bardzo złożona ale jeśli od razu zabierzemy się do tematu jak należy to nie jest tak strasznie.

Jakiś czas temu w poście https://4programmers.net/Forum/Algorytmy/335561-algorytm_bresenhama_z_niezmienna_osia_wiodaca?p=1649661#id1649661 zamieściłem materiały, z którymi warto się zapoznać przed pisaniem czegoś takiego. Najpierw przejrzyj a potem dogłębnie przeanalizuj punkt 2.3 ( rzutowanie ).

Na twoim obrazku nie widzę perspektywy więc wnoszę, że robisz zwykły rzut równoległy a do do silnika gier nie będzie się nadawało. Musisz mieć perspektywę która wynika sama z rzutu ( http://mst.mimuw.edu.pl/lecture.php?lecture=gk1&part=Ch5 ). Ogólnie prowadzisz proste między okiem kamery do punku sceny i wyliczasz punkty przecięcia z płaszczyzną, która jest ekranem.

0

Poprawione, dzięki @katakrowa za naprowadzenie mnie na materiały które są bardzo wartościowe!.
Jeśli chodzi o perspektywę... no tutaj nie ma nie ma jeszcze szału, ale myślę ze za jakiś czas coś się uda ciekawego zrobić ;)

Perspektywa

Przyjąłem taką zasadę że cała scena obraca się aktualnie na środku ekranu (oczywiście uwzględniając przesunięcie całej sceny w osi XYZ), ale docelowo chce aby np. ctrl + scroll myszki za to odpowiadał. Obecnie scrollowanie mam do punktu gdzie jest kursor lub do środka ekranu. Przesuwanie też jest za pomocą myszki (lub przycisków w prawym górnym rogu). Pierwotnie miało to być 2D, ale że apetyt rośnie w miarę jedzenia to rozbudowuję to na 3D ;)

2

@WileCoyote: Przyjąłem taką zasadę że cała scena obraca się aktualnie na środku ekranu
To bardzo bardzo bardzo źle.
Każdorazowe przeliczanie punktów sceny powoduje, że podczas obliczeń zmieniasz ich wartości na nowe. Każda taka zamiana powoduje powstawanie małych błędów wynikających z obliczeń na liczbach typu float. Te błędy niestety się sumują i scena zacznie Ci się rozlatywać. krawędzie ścian przestaną być równoległe, wierzchołki wielokątów przestaną się pokrywać.

Przesuwanie po osiach X,Y,Z to kolejny błąd. Jak scenę będziesz miał dużą to przy większych odległościach od środka sceny błędy precyzji liczb typu float są coraz większe doprowadzając to tego samego co ich wielokrotne "obracanie/przeliczanie".

Do każdej klatki musisz brać dane z oryginalnej sceny. Gorąco polecam Ci zaimplementować opcję z ruchomą kamerą po stałej scenie. Nie wiem ile masz lat i przypuszczam, że to może być dla Ciebie z punktu widzenia matematyki nieco trudniejsze niż obracanie wokół osi sinusami i cosinusami ale uwierz mi warto poświęcić te 2 dni na analitykę.
Metoda, której używasz jest dobra jedynie do prezentacji pojedynczych obiektów ale już nie nadaje się do zrobienia silnika do gry ( chyba, że to będzie gra w kostkę Rubika albo szachy ).

Kolejne zalety wynikające z proponowanego podejścia:

  • praktycznie brak użycia funkcji trygonometrycznych;
  • możliwość umieszczenia na scenie kilku kamer;
  • tworzenia kamer o różnej perspektywie t.j. żaba albo rybie oko ( to wszystko potem jest już banalne ).
  • ułatwienie przygotowania danych dalszych obliczeń związanych z wypełnianiem trójkątów itp ...
  • łatwiejsze umieszczanie obiektów na scenie ( w końcu będzie statyczna ).

Jeśli będziesz miał problemy śmiało zapytaj a ja postaram się pomóc

0

@katakrowa - taki obrót jest naprawdę zły? Przecież uwzględniam przesunięcie kamery co uważam daje pożądany efekt. Może po prostu źle to opisałem, ale uważam że jest możliwość umieszczenia kilku kamer (tzn. późniejszej wersji umieszczę kilka kolorowych obiektów i kilka kamer aby było to widoczne że to kamera się obraca a nie scena).
W prywatnej wiadomości wysłałem Ci linka gdzie możesz pobrać ten program i zobaczyć jak to wygląda :)

Jeśli chodzi o materiały - to tak, w tym tygodniu chce lepiej je poznać. Dodatkowo znalazłem jeden projekt w C#
https://www.codeproject.com/Articles/36868/Quaternion-Mathematics-and-3D-Library-with-C-and-G
któremu tez się przyjże.

Jeśli chodzi o perspektywę... to raczkuje. Ale mam nadzieje zrobić coś dobrego

1
WileCoyote napisał(a):

@katakrowa - taki obrót jest naprawdę zły? Przecież uwzględniam przesunięcie kamery co uważam daje pożądany efekt. Może po prostu źle to opisałem, ale uważam że jest możliwość umieszczenia kilku kamer (tzn. późniejszej wersji umieszczę kilka kolorowych obiektów i kilka kamer aby było to widoczne że to kamera się obraca a nie scena).

W prywatnej wiadomości wysłałem Ci linka gdzie możesz pobrać ten program i zobaczyć jak to wygląda :)

Jeśli chodzi o materiały - to tak, w tym tygodniu chce lepiej je poznać. Dodatkowo znalazłem jeden projekt w C#
https://www.codeproject.com/Articles/36868/Quaternion-Mathematics-and-3D-Library-with-C-and-G
któremu tez się przyjże.

Jeśli chodzi o perspektywę... to raczkuje. Ale mam nadzieje zrobić coś dobrego

Na ekranie na początku zabawy wygląda podobnie i wydaje się, że to jest to samo ale w praktyce sumują się błędy, które w końcu ( po określonej licznie obrotów ) zaczną mieć wpływ na poprawność obliczeń i kształty obiektów zaczną się rozlatywać.

Niestety do końca nie wiem jak to jest u Ciebie rozwiązane. Powinno być tak:

screenshot-20201130170227.png

To co robisz nie jest błędem. Jedynie metoda, którą wybrałeś nie nadaje się do zastosowania jako silnik do 3D. Silnik 3D nie powinien być pisany wiedząc, że długie poruszanie się po scenie spowoduje nieuniknione problemy. Dlatego właśnie wymyślono inne metody, które na wymienione problemy są odporne. Owszem trzeba poznać 2 lub dodatkowe wzory no ale to nie jest jakaś tajemna wiedza.

screenshot-20201130170720.png

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