PlanetEditor/grk/cw 6/Zadania 4.html

101 lines
13 KiB
HTML
Raw Permalink Normal View History

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" lang="" xml:lang="">
<head>
<meta charset="utf-8" />
<meta name="generator" content="pandoc" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes" />
<title>Zadania 4</title>
<style>
code{white-space: pre-wrap;}
span.smallcaps{font-variant: small-caps;}
span.underline{text-decoration: underline;}
div.column{display: inline-block; vertical-align: top; width: 50%;}
div.hanging-indent{margin-left: 1.5em; text-indent: -1.5em;}
ul.task-list{list-style: none;}
</style>
<style>
.code-block {
background-color: #f4f4f4;
border-left: 5px solid #f36d33;
margin: 20px 0;
padding: 10px 20px;
font-family: Consolas, "Courier New", Courier, monospace;
color: #333;
overflow: auto;
}
</style>
<link rel="stylesheet" href="style.css" />
<script src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-chtml-full.js" type="text/javascript"></script>
<!--[if lt IE 9]>
<script src="//cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.3/html5shiv-printshiv.min.js"></script>
<![endif]-->
</head>
<body>
<h2 id="ładowanie-obiektów-za-pomocą-assimpa">Ładowanie obiektów za pomocą Assimp</h2>
<p>W projekcie zaimplementowano ładowanie modeli przy użyciu biblioteki Assimp. Obiekty są ładowane za pomocą funkcji <code>loadModelToContext(std::string path, Core::RenderContext&amp; context)</code>. Pierwszym argumentem tej funkcji jest ścieżka, pod którą znajduje się model, a drugim jest referencja do <code>RenderContext</code>. Ta struktura przechowuje informacje o modelu, m.in. o jego VAO czy liczbie wierzchołków. Rysowanie obiektu odbywa się za pomocą funkcji <code>Core::DrawContext(Core::RenderContext&amp; context)</code>. Obecnie na przykładzie rysowana jest sfera ładowana z pliku.</p>
<p>Podczas poprzednich zadań zdefiniowaliśmy funkcje tworzące macierze widoku i projekcji. Aby narysować model, należy najpierw zdefiniować macierz modelu, przemnożyć ją przez macierz kamery i macierz widoku, a następnie wysłać ją do GPU i dopiero potem narysować model. Te operacje są bardzo powtarzalne, dlatego można je przenieść do osobnej funkcji. W pliku <code>ex_4_1.hpp</code> znajduje się funkcja <code>drawObjectColor</code>, która przyjmuje rysowany obiekt jako <code>Core::RenderContext&amp;</code>, macierz modelu jako <code>glm::mat4</code> oraz kolor jako <code>glm::vec3</code>. <h3 id="zadanie">Zadanie</h3> Wszystkie obiekty rysowane przez <code>drawObjectColor</code> są w jednym kolorze napraw to. Wewnątrz funkcji przekaż kolor jako <code>uniform</code> do GPU (za pomocą funkcji <code>glUniform3f</code>) i odpowiednio zmodyfikuj shader fragmentów, aby wyznaczyć go jako kolor wyjściowy.</p>
<p>Korzystając z tej funkcji, stwórz układ słoneczny z przynajmniej jedną planetą, która posiada księżyc. Planeta powinna poruszać się wokół słońca, a księżyc wokół planety.</p>
<h3 id="zadanie">Zadanie*</h3>
<p>Rozbuduj układ planetarny do przynajmniej 5 planet i pasa asteroid. Ściągnij z internetu/stwórz kilka prostych modeli asteroid, z których zbudujesz pas asteroid.</p>
<h3 id="zadanie-1">Zadanie</h3>
<p>Celem tego zadania jest dodanie statku, który będzie latać po układzie planetarnym.</p>
<p>Załaduj model statku, który jest w pliku <code>spaceship.obj</code>. Stwórz zmienne globalne <code>spaceshipPos</code> oraz <code>spaceshipDir</code>, które będą określać pozycję i kierunek, w którym statek się porusza. Później będziemy je zmieniać za pomocą przycisków, na razie wewnątrz funkcji <code>processInput</code> przypisz do nich odpowiednio <code>cameraPos+1.5*cameraDir+glm::vec3(0,-0.5f,0)</code> oraz <code>cameraDir</code>. W ten sposób po prawidłowym ustawieniu macierzy statek będzie znajdował się zawsze przed kamerą.</p>
<p>Przesuń i obróć statek w odpowiedni sposób. Przesunięcie zrealizujemy przez translację do <code>spaceshipPos</code> natomiast macierz statku liczy się tak samo, jak macierz kamery, tylko zamiast <code>-cameraDir</code> bierzemy <code>spaceshipDir</code> i na końcu trzeba tę macierz odwrócić (lub transponować, co jest tym samym, ponieważ mówimy o macierzy ortonormalnej).</p>
<h3 id="zadanie-2">Zadanie</h3>
<h3 id="zadanie-3">Zadanie*</h3>
<p>Obecnie klawisze regulują ustawienia kamery, do której jest podpięty statek. Zmodyfikuj aplikację, aby klawisze umożliwiały poruszanie się statkiem, a kamera podążała za nim. W tym celu w obsłudze klawiatury modyfikuj wektory <code>spaceshipPos</code> i <code>spaceshipDir</code>. Następnie dostosuj <code>cameraPos</code> i <code>cameraDir</code> w zależności od wektorów <code>spaceshipPos</code> i <code>spaceshipDir</code>.</p>
<h3 id="zadanie-4">Zadanie*</h3>
<p>Obecnie szybkość ruchu statku lub kamery zależy od liczby klatek, co może powodować różne efekty na różnych komputerach, co nie jest pożądane. Aby to skorygować, musimy obliczyć czas, jaki upłynął między poszczególnymi klatkami i dostosować do niego przesunięcia oraz obroty. Utwórz zmienne globalne <code>float lastFrameTime</code> oraz <code>float deltaTime</code>, następnie w funkcji <code>renderScene</code> dodaj oblicz <code>deltaTime = time-lastFrameTime</code> i przypisz do zmiennej <code>lastFrameTime</code> wartość <code>time</code>. Nie chcemy, żeby wartość <code>deltaTime</code> była zbyt duża, gdy nagle spadnie liczba klatek, dlatego ucinamy ją od góry przez <code>0.1</code>.</p>
<p>Wykorzystaj <code>deltaTime</code> w funkcji <code>processInput</code> aby uniezależnić prędkość poruszania się od liczby klatek na sekundę.</p>
<h3 id="zadanie-5">Zadanie*</h3>
<p>Zastąp model statku innym modelem.</p>
<h2 id="bufor-głębokości">Bufor głębokości</h2>
<p>Bufor głębokości rejestruje odległość danego piksela od kamery. Pozwala to podczas rysowania kolejnych obiektów odrzucić piksele, które znajdowałyby się za już narysowanymi. Ten mechanizm jest automatycznie, żeby go uruchomić, wystarczy dodać instrukcję <code>glEnable(GL_DEPTH_TEST)</code>, poza tym przed rysowaniem klatki należy wyczyścić bufor głębokości, co robimy w instrukcji <code>glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)</code>.</p>
<h3 id="zadanie-7">Zadanie</h3>
<p>Sprawdź, co się stanie, gdy nie włączymy <code>glEnable(GL_DEPTH_TEST)</code> oraz sprawdź co, się dzieje, gdy nie czyścimy żadnego bufora lub gdy czyścimy tylko bufor koloru czy tylko bufor głębokości. Dlaczego dzieje się to, co widzisz?</p>
<h2 id="wizualizacja-bufora-głębokości">Wizualizacja bufora głębokości</h2>
<p>W tej części zwizualizujemy jak wygląda bufor głębokości przy pomocy skali szarości. Zrealizujemy poprzez napisanie odpowiedniego shadera.</p>
<blockquote>
<p><strong>Uwaga</strong> nie jest to faktycznie rysowanie bufora głębokości, to wymagałoby stworzenie FrameBufferObject renderowanie głębokości do niego i narysowanie wyniku na ekranie. Zrobimy to na późniejszych zajęciach przy okazji rysowania cieni.</p>
</blockquote>
<p>Wykorzystamy wbudowaną zmienną <code>gl_FragCoord</code> we fragment shaderze. Zawiera ona informacje o pozycji fragmentu.</p>
<h3 id="zadanie-8">Zadanie</h3>
<p>We fragment shaderze podmień wartości R G B w shaderze fragmentów na <code>gl_FragCoord.z</code>.</p>
<p>Zauważ, że obiekty są bardzo jasne i stają się ciemniejsze, dopiero gdy kamera podjedzie bardzo blisko. Wynika to z tego, że wartości <code>z</code> w <code>gl_FragCoord</code> nie są liniowe ze względu na rzutowanie perspektywiczne omówione na poprzednich zajęciach. Poniższy wykres prezentuje przykładową różnicę między faktyczną wartością a wartością w <code>gl_FragCoord</code>. <img src="./img/z_depth_graph2.jpg" /></p>
<p>My chcielibyśmy wyświetlać je liniowo. W tym celu będziemy musieli wrócić do współrzędnych w przestrzeni świata. Zauważ, że wartości <code>gl_FragCoord.z</code> są z zakresu od <span class="math inline">\([0,1]\)</span> a nie <span class="math inline">\([-1,1]\)</span> jak są zapisane współrzędne w przestrzeni ekranu. Dlatego pierwszym krokiem będzie przekonwertowanie ich (poprzez pomnożenie przez 2 i odjęcie 1). Współrzędne w przestrzeni ekranu obliczamy wzorem <span class="math display">\[z&#39;=-\frac{(n + f)}{(n - f)}- \frac{(2 n f)}{z(n - f)}.\]</span>My chcemy obliczyć <span class="math inline">\(z\)</span> po przekształceniu wzoru otrzymujemy: <span class="math display">\[z=\frac{-2nf}{z&#39;(n-f)+n+f}.\]</span> <h3 id="zadanie-9">Zadanie</h3> We fragment shaderze uwórz funkcję, która oblicza <span class="math inline">\(z\)</span> i wyświetl zlinearyzowaną odległość. Pamiętaj, że wartość <span class="math inline">\(z\)</span> jest z zakresu od <span class="math inline">\(n\)</span> do <span class="math inline">\(f\)</span>, dlatego zmień podziel ją przez <span class="math inline">\(-f\)</span> przed rysowaniem.</p>
<h3 id="zadanie-9">Zadanie</h3>
<p>Wykorzystaj informację o odległości, żeby dodać do sceny efekt mgły. Zmieszaj kolor (funkcja "mix" w glsl) obiektu z kolorem tła, jako współczynnik weź wartość z poprzedniego zadania.</p>
<h2 id="kreatywne-wykorzystanie-bufora-głębokości">Kreatywne wykorzystanie bufora głębokości</h2>
<p>Czasem chcielibyśmy, żeby niektóre wyświetlane elementy były inaczej traktowane przez bufor głębokości. Przykładowo chcielibyśmy stworzyć bardziej złożone tło dla naszej sceny. Chcemy wtedy, żeby to tło było ,,za każdym innym obiektem w scenie. Możemy to osiągnąć poprzez namalowanie tła na początku, a następnie usunięcie zawartości bufora głębokości.</p>
<h3 id="zadanie-10">Zadanie*</h3>
<p>Dodaj jakiś rodzaj tła w sposób opisany powyżej. Mogą być to na przykład małe sfery udające gwiazdy. Prostokąt w przestrzeni ekranu, który będzie zmieniał kolory czy kręcący się prostopadłościan.</p>
<h2 id="kreatywne-wykorzystanie-bufora-głębokości">Implementacja ruchu kamery w symulatorze kosmicznym bez synchronizacji modelu statku</h2>
<h3 id="zadanie-6">Zadanie*</h3>
<p>W tym ćwiczeniu zajmiemy się implementacją systemu kontroli kamery, który pozwoli na interaktywne sterowanie widokiem w symulatorze lotu. Będzie to wymagało obsługi wejścia myszy do kontroli ruchów pitch i yaw. Dodaj nastepujace zmienne globalne do kodu.
<pre class="code-block"><code>
float pitch = 0.0f;
float yaw = -90.0f;
float lastX = 800.0f / 2.0;
float lastY = 600.0 / 2.0;
bool firstMouse = true;
</code></pre></p>
Zaimplementuj w funkcji processinput logikę odpowiedzialną za przetwarzanie wejścia myszy. Użyj funkcji glfwGetCursorPos do uzyskania bieżącej pozycji kursora. Dodaj zmienną sensitivity do regulacji czułości myszy. Na początku sprawdzamy, czy jest to pierwsze uchwycenie pozycji myszy po uruchomieniu aplikacji. Jeśli tak, to zapisujemy bieżącą pozycję myszy jako punkt odniesienia (lastX i lastY) i ustawiamy zmienną firstMouse na false. To zapobiega gwałtownemu skoku kamery, gdybyśmy po raz pierwszy poruszyli myszą. Następnie obliczamy różnicę (xoffset i yoffset) między aktualną pozycją kursora a ostatnią zapisaną pozycją. Zauważ, że wartość yoffset jest odwrócona, ponieważ w systemach okienkowych współrzędna y zwykle rośnie w dół ekranu, a chcemy, aby ruch w górę odpowiadał wzrostowi kąta widzenia kamery. Po obliczeniu przesunięć skalujemy je wartością sensitivity, która pozwala na dostosowanie reakcji kamery na ruch myszy. Mała wartość czułości sprawi, że kamera będzie reagować łagodniej, podczas gdy większa wartość uczyni ruchy bardziej gwałtownymi. Na koniec, dodajemy przeskalowane przesunięcia do naszych kątów orientacji kamery: yaw (odpowiedzialny za obrót wokół osi pionowej) i pitch (odpowiedzialny za obrót wokół osi poziomej). Trzeba tu uważać, aby nie przekroczyć pewnych granic. Na przykład, nie chcemy, aby kamera "przewracała się" do góry nogami, dlatego ograniczamy kąt pitch do wartości pomiędzy -89 a 89 stopni. Dodaj logikę do ograniczenia kąta pitch, aby uniknąć "przewrócenia" kamery.
<pre class="code-block"><code> if (pitch > 89.0f)
pitch = 89.0f;
if (pitch < -89.0f)
pitch = -89.0f;
</code></pre></p>
Zastosuj funkcje trygonometryczne do przeliczenia kątów na wektor kierunku kamery:<pre class="code-block"><code>
glm::vec3 front;
front.x = cos(glm::radians(yaw)) * cos(glm::radians(pitch));
front.y = sin(glm::radians(pitch));
front.z = sin(glm::radians(yaw)) * cos(glm::radians(pitch));
cameraDir = glm::normalize(front);</code></pre></p>
Na zakończenie ćwiczenia oczekujemy, że symulator będzie poprawnie implementował ruch kamery, lecz dołączony model statku kosmicznego nie będzie jeszcze obracał się razem z kamerą.
</body>
</html>