166 lines
19 KiB
HTML
166 lines
19 KiB
HTML
<!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 3</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;}
|
||
pre > code.sourceCode { white-space: pre; position: relative; }
|
||
pre > code.sourceCode > span { display: inline-block; line-height: 1.25; }
|
||
pre > code.sourceCode > span:empty { height: 1.2em; }
|
||
code.sourceCode > span { color: inherit; text-decoration: inherit; }
|
||
div.sourceCode { margin: 1em 0; }
|
||
pre.sourceCode { margin: 0; }
|
||
@media screen {
|
||
div.sourceCode { overflow: auto; }
|
||
}
|
||
@media print {
|
||
pre > code.sourceCode { white-space: pre-wrap; }
|
||
pre > code.sourceCode > span { text-indent: -5em; padding-left: 5em; }
|
||
}
|
||
pre.numberSource code
|
||
{ counter-reset: source-line 0; }
|
||
pre.numberSource code > span
|
||
{ position: relative; left: -4em; counter-increment: source-line; }
|
||
pre.numberSource code > span > a:first-child::before
|
||
{ content: counter(source-line);
|
||
position: relative; left: -1em; text-align: right; vertical-align: baseline;
|
||
border: none; display: inline-block;
|
||
-webkit-touch-callout: none; -webkit-user-select: none;
|
||
-khtml-user-select: none; -moz-user-select: none;
|
||
-ms-user-select: none; user-select: none;
|
||
padding: 0 4px; width: 4em;
|
||
color: #aaaaaa;
|
||
}
|
||
pre.numberSource { margin-left: 3em; border-left: 1px solid #aaaaaa; padding-left: 4px; }
|
||
div.sourceCode
|
||
{ }
|
||
@media screen {
|
||
pre > code.sourceCode > span > a:first-child::before { text-decoration: underline; }
|
||
}
|
||
code span.al { color: #ff0000; font-weight: bold; } /* Alert */
|
||
code span.an { color: #60a0b0; font-weight: bold; font-style: italic; } /* Annotation */
|
||
code span.at { color: #7d9029; } /* Attribute */
|
||
code span.bn { color: #40a070; } /* BaseN */
|
||
code span.bu { } /* BuiltIn */
|
||
code span.cf { color: #007020; font-weight: bold; } /* ControlFlow */
|
||
code span.ch { color: #4070a0; } /* Char */
|
||
code span.cn { color: #880000; } /* Constant */
|
||
code span.co { color: #60a0b0; font-style: italic; } /* Comment */
|
||
code span.cv { color: #60a0b0; font-weight: bold; font-style: italic; } /* CommentVar */
|
||
code span.do { color: #ba2121; font-style: italic; } /* Documentation */
|
||
code span.dt { color: #902000; } /* DataType */
|
||
code span.dv { color: #40a070; } /* DecVal */
|
||
code span.er { color: #ff0000; font-weight: bold; } /* Error */
|
||
code span.ex { } /* Extension */
|
||
code span.fl { color: #40a070; } /* Float */
|
||
code span.fu { color: #06287e; } /* Function */
|
||
code span.im { } /* Import */
|
||
code span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Information */
|
||
code span.kw { color: #007020; font-weight: bold; } /* Keyword */
|
||
code span.op { color: #666666; } /* Operator */
|
||
code span.ot { color: #007020; } /* Other */
|
||
code span.pp { color: #bc7a00; } /* Preprocessor */
|
||
code span.sc { color: #4070a0; } /* SpecialChar */
|
||
code span.ss { color: #bb6688; } /* SpecialString */
|
||
code span.st { color: #4070a0; } /* String */
|
||
code span.va { color: #19177c; } /* Variable */
|
||
code span.vs { color: #4070a0; } /* VerbatimString */
|
||
code span.wa { color: #60a0b0; font-weight: bold; font-style: italic; } /* Warning */
|
||
</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>
|
||
<h1 id="ćwiczenia-3">Ćwiczenia 3</h1>
|
||
<h2 id="przestrzenie-potoku-graficznego">Przestrzenie potoku graficznego</h2>
|
||
<p>W trakcie wykładu zostały opisane kolejne przestrzenie potoku graficznego. W tej części zajęć przejdziemy kolejne jego etapy.</p>
|
||
<p><img src="./img/coordinate_systems.jpg" /></p>
|
||
<p>Pierwszy krok, czyli przejście do <code>World Space</code> już wykonujemy za pomocą macierzy transformacji. Następnym krokiem będzie stworzenie macierzy projekcji i macierzy widoku. Zaczniemy od macierzy projekcji. Będziemy modyfikować macierz, którą wysyła funkcja <code>createPerspectiveMatrix</code> (macierz jest transponowana dalej, więc zapisuj ją tak, jak tu widzisz). Zanim zaczniemy domnóż macierz perspektywy do macierzy transformacji obiektów.</p>
|
||
<p><strong>Nim przejdziemy dalej odkomentuj rysowanie prostopadłościanu.</strong></p>
|
||
<h2 id="macierz-perspektywy">Macierz perspektywy</h2>
|
||
<p>Rozważmy mnożenie dowolnej macierzy przez wektor kolumnowy:</p>
|
||
<p><span class="math display">\[\begin{bmatrix} m_{11} & m_{12} & m_{13}& m_{14}\\m_{21} & m_{22} & m_{23}& m_{24}\\m_{31} & m_{32} & m_{33}& m_{34}\\m_{41} & m_{42} & m_{43}& m_{44}\\\end{bmatrix}\begin{bmatrix} x \\ y \\ z \\ w \end{bmatrix}=\begin{bmatrix} x*m_{11}+y*m_{12}+z*m_{13}+m_{14}\\x*m_{21} +y*m_{22} +z*m_{23} +m_{24}\\x*m_{31} +y*m_{32} +z*m_{33} +m_{34}\\x*m_{41} +y*m_{42} +z*m_{43} +m_{44}\\\end{bmatrix}\]</span></p>
|
||
<p>Pierwszym krokiem jest wykorzystanie homogenizacji do uzyskania efektu perspektywy. W tym celu musimy ustawić współrzędną <span class="math inline">\(w\)</span> na <span class="math inline">\(-z\)</span> za pomocą macierzy. Jeśli przyjrzymy się obliczeniom powyżej. Zobaczymy, że do tego musimy ustawić <span class="math inline">\(m_{43}\)</span> na <span class="math inline">\(-1\)</span> a pozostałe w tym wierszu na <span class="math inline">\(0\)</span>. Przemnożenie daje nam:</p>
|
||
<p><span class="math display">\[\begin{bmatrix} 1 & 0 & 0& 0\\0 & 1 & 0& 0\\0 & 0 & 1& 0\\0 & 0 & -1& 0 \end{bmatrix}\begin{bmatrix} x \\ y \\ z \\ 1 \end{bmatrix}=\begin{bmatrix} x \\ y \\ z \\ -z\end{bmatrix}\]</span></p>
|
||
<p>Po homogenizacji otrzymamy wektor:</p>
|
||
<p><span class="math display">\[\begin{bmatrix} -\frac{x}{z} \\ -\frac{x}{z} \\ -1 \\ 1\end{bmatrix}\]</span></p>
|
||
<hr />
|
||
<h3 id="zadanie">Zadanie</h3>
|
||
<p>zmodyfikuj macierz perspektywy w taki sposób.</p>
|
||
<hr />
|
||
<p>Wartość współrzędnej <span class="math inline">\(z\)</span> jest równa -1 dla każdego parametru. Co spowoduje, że nie będzie wiadomo, który wierzchołek bliżej, a który dalej i otrzymam zjawisko znane jako z-fighting. By tego uniknąć. Musimy zmapować współrzędną <span class="math inline">\(z\)</span>. Przypomnijmy, że przy arbitralnej macierzy wartość współrzędnej <span class="math inline">\(z\)</span> będzie następującej postaci:</p>
|
||
<p><span class="math display">\[ x*m_{31} +y*m_{32} +z*m_{33} +m_{34} \]</span></p>
|
||
<p>więc możemy pracować tylko z parametrami <span class="math inline">\(m_{33}\)</span> i <span class="math inline">\(m_{34}\)</span>, czyli <span class="math inline">\(z*m_{33} +m_{34}\)</span>, po uwzględnieniu dodatkowo homogenizacji otrzymamy ostateczny wzór:</p>
|
||
<p><span class="math display">\[ z'=-m_{33} -\frac{m_{34}}{z}.\]</span></p>
|
||
<p>Przypomnijmy, że w <code>Clipping Space</code> współrzędna <span class="math inline">\(z\)</span> musi się mapować na wartości od <span class="math inline">\(-1\)</span> do <span class="math inline">\(1\)</span>, żeby znalazły się w bryle kanonicznej (obiekty poza bryłą kanoniczną nie będą wyświetlone). Jak na wykładzie określimy sobie parametry <span class="math inline">\(0 < n < f\)</span>, które będą określać pozycję minimalnej i maksymalnej płaszczyzny osi <span class="math inline">\(z\)</span>. Chcemy, żeby dla <span class="math inline">\(z=n\)</span> wartość <span class="math inline">\(z'\)</span> wynosiła <span class="math inline">\(-1\)</span> oraz dla <span class="math inline">\(z=f\)</span> wartość <span class="math inline">\(z'\)</span> wynosiła <span class="math inline">\(1\)</span>, daje nam to układ równań:</p>
|
||
<p><span class="math display">\[\begin{matrix} -m_{33}& -&\frac{m_{34}}{n}&=&-1\\-m_{33} &-&\frac{m_{34}}{f}&=&1\end{matrix}\]</span></p>
|
||
<p>co po przekształceniu da nam:</p>
|
||
<p><span class="math display">\[\begin{matrix} m_{33}&=&\frac{(n + f)}{(n - f)}\\m_{34} &=&\frac{(2 n f)}{(n - f)}\end{matrix}\]</span></p>
|
||
<p>Ostatecznie otrzymujemy:</p>
|
||
<p><span class="math display">\[\begin{bmatrix} 1 & 0 & 0& 0\\0 & 1 & 0& 0\\0 & 0 & \frac{(n + f)}{(n - f)}& \frac{(2 n f)}{(n - f)}\\0 & 0 & -1& 0 \end{bmatrix}\]</span></p>
|
||
<p>Zauważ, że te wartość zmienia się zgodnie ze wzorem <span class="math inline">\(-\left(\frac{(n + f)}{(n - f)}+ \frac{(2 n f)}{z(n - f)}\right)\)</span> czyli zmienia się to asymptotycznie, co można zobaczyć na wykresie.</p>
|
||
<p><img src="./img/z_depth_graph.jpg" /></p>
|
||
<h3 id="zadanie-1">Zadanie*</h3>
|
||
<p>Rozwiąż samodzielnie ten układ równań.</p>
|
||
<h3 id="zadanie-2">Zadanie</h3>
|
||
<p>Dodaj zmienne lokalne <code>n</code> i <code>f</code> w funkcji, ustal im jakieś arbitralne wartości i dodaj rzutowanie <span class="math inline">\(z\)</span> zgodnie ze wzorem, który otrzymaliśmy. Spróbuj ustawić taką wartość <code>f</code>, żeby tylna ściana sześcianu zniknęła.</p>
|
||
<p>Uzyskana macierz daje nam rzutowanie perspektywiczne, ale możemy ją jeszcze rozbudować o zmianę kąta widzenia, a także naprawić problem z nieprawidłowym skalowaniem się ekranu przy zmianie jego proporcji. Obie te czynności sprowadzają się do tego samego, mianowicie chcemy zmienić kształt bryły widzenia w osiach <span class="math inline">\(x\)</span> i <span class="math inline">\(y\)</span>. By tego dokonać, musimy zmienić wartość parametrów <span class="math inline">\(m_{11}\)</span> i <span class="math inline">\(m_{22}\)</span>, to one odpowiadają za skalowanie w tych osiach. Parametry te ściskają lub rozszerzają przestrzeń w tych osiach, więc zmniejszenie wartości zwiększy kąt widzenia w danej osi.</p>
|
||
<p>Zacznijmy od kąta widzenia. Można go zmienić zwyczajnie ustawiając zamiast <span class="math inline">\(1\)</span> dowolną inną wartość <span class="math inline">\(S\)</span> parametrów <span class="math inline">\(m_{11}\)</span> i <span class="math inline">\(m_{22}\)</span>. Jednak jeśli chcemy uzyskać faktyczne parametry oparte na polu widzenia, musimy skorzystać ze wzoru:</p>
|
||
<p><span class="math display">\[S=\frac{1}{\tan(\frac{fov}{2}*\frac{\pi}{180})}\]</span></p>
|
||
<h3 id="zadanie-3">Zadanie</h3>
|
||
<p>Dodaj do <code>createPerspectiveMatrix</code> argument fov, który będzie ustalał kąt widzenia.</p>
|
||
<h3 id="zadanie-4">Zadanie</h3>
|
||
<p>Prawidłowe skalowanie okna uzyskamy poprzez mnożenie <span class="math inline">\(m_{22}\)</span> przez stosunek szerokości do wysokości ekranu. W zadaniu jest zmienna globalna <code>aspectRatio</code>. W funkcji <code>framebuffer_size_callback</code> nadpisz tą zmienną właściwym stosunkiem (pamiętaj, że dzielenie liczb stałoprzecinkowych w c++ nie uwzględnia ułamków). Następnie prześlij ja do <code>createPerspectiveMatrix</code> jako dodatkowy parametr i wykorzystaj przy renderowaniu sceny.</p>
|
||
<hr />
|
||
<h3 id="zadanie-5">Zadanie*</h3>
|
||
<p>Po dodaniu skalowania okna, poszerzanie kwadratowego okna będzie zmniejszać kąt widzenia na osi pionowej. Natomiast wydłużanie go będzie go zwiększać. Zaproponuj rozwiązanie, które sprawi, że poszerzanie kwadratowego okna będzie zwiększać kąt widzenia w osi poziomej i jednocześnie wydłużanie kwadratowego okna będzie zwiększać kąt widzenia w osi pionowej.</p>
|
||
<h2 id="macierz-widoku">Macierz widoku</h2>
|
||
<p>Celem macierzy widoku jest wprowadzenie pojęcia kamery, jako obiektu, który możemy ustawić i poruszać nim w przestrzeni. Na taką macierz składa się pozycja kamery kierunek patrzenia oraz jej orientacja: wektory <code>cameraDir</code> oraz <code>cameraUp</code> i <code>cameraSide</code>. W tym celu potrzebujemy jeden wektor, który będzie określał pozycję początku układu współrzędnych (czyli pozycję kamery). Oraz 3 wektory ortonormalne, które będą rozpinać przestrzeń (odpowiedzialne za kierunek i orientację). Ponieważ te wektory są ortogonalne, możemy je rekonstruować za pomocą dwóch wektorów, jeden będzie nam wskazywać kierunek patrzenia (<code>cameraDir</code>), drugi górę (<code>cameraUp</code>).</p>
|
||
<p><img src="./img/camera.jpg" /></p>
|
||
<blockquote>
|
||
<p>Układ współrzędnych kamery</p>
|
||
</blockquote>
|
||
<p>W kodzie jest zaimplementowana obsługa klawiatury, klawisze <strong>W</strong> i <strong>S</strong> przesuwają kamerę do przodu i do tyłu, natomiast <strong>A</strong> i <strong>D</strong> obracają ją na boki. Robią to poprzez modyfikacje zmiennych globalnych <code>cameraDir</code> i <code>cameraPos</code>, jednak, żeby kamera faktycznie działała, trzeba uzupełnić funkcję <code>createCameraMatrix</code> i dodać jej wynik do transformacji obiektów.</p>
|
||
<h3 id="zadanie-6">Zadanie</h3>
|
||
<p>Uzupełnij funkcję <code>createCameraMatrix</code>. Najpierw oblicz wektor skierowany w bok za pomocą iloczynu wektorowego między <code>cameraDir</code> a wektorem <span class="math inline">\([0,1,0]\)</span>. Wektor może być długości innej niż 1, dlatego znormalizuj go. Zapisz wynik do <code>cameraSide</code>. Podobnie oblicz <code>cameraUp</code> jako znormalizowany iloczyn wektorowy między <code>cameraSide</code> i <code>cameraDir</code>.</p>
|
||
<blockquote>
|
||
<p>Wektor normalizuje się za pomocą funkcji: <code>glm::normalize</code></p>
|
||
</blockquote>
|
||
<p>Macierz kamery złożona jest z iloczynu macierzy obrotu i macierzy translacji. By otrzymać pierwszą z nich, korzystamy z ortonormalności bazy, dzięki temu wystarczy zapisać wektory <code>cameraSide</code>, <code>cameraUp</code> i <code>-cameraDir</code> wierszami. >Zauważ, że <code>cameraDir</code> musi być odwrócony tak jak na obrazku, ma być zwrócony do kamery, nie od niej. Macierz wygląda następująco:</p>
|
||
<p><span class="math display">\[M_{VR}=\begin{bmatrix} cameraSide_x & cameraSide_y & cameraSide_z & 0\\cameraUp_x & cameraUp_y & cameraUp_z & 0\\-cameraDir_x & -cameraDir_y & -cameraDir_z & 0\\0 & 0 & 0 & 1 \end{bmatrix}\]</span></p>
|
||
<p>Macierz translacji <span class="math inline">\(M_{VT}\)</span> otrzymujemy przez translacje o <code>-cameraPos</code>. Ostatecznie macierz widoku jest postaci <span class="math display">\[M_V=M_{VR}*M_{VT}\]</span></p>
|
||
<p>W macierzy <code>transformation</code> umieść pomnożone przez siebie macierze perspektywy (wynik funkcji <code>createPerspectiveMatrix</code>) i kamery (wynik funkcji <code>createCameraMatrix</code>) w odpowiedniej kolejności. Jako efektem otrzymamy pełen potok graficzny, czyli kamerę, którą możemy poruszać klawiszami, z poprawnym rzutowaniem perspektywicznym.</p>
|
||
<h3 id="zadanie-7">Zadanie</h3>
|
||
<p>Wyświetl dwa dodatkowe prostopadłościany w różnych pozycjach i orientacjach.</p>
|
||
<h3 id="zadanie-8">Zadanie*</h3>
|
||
<p>Zmodyfikuj ustawienia tak, żeby kamera zawsze była zwrócona w punkt wybrany punkt <span class="math inline">\(p\)</span> <span class="math inline">\((0,0,0)\)</span>. W funkcji <code>processInput</code> zakomentuj obsługę klawiszy <strong>A</strong> i <strong>D</strong>. Zamiast tego na końcu tej funkcji ustaw <code>cameraDir</code> jako znormalizowana różnicę między punktem <span class="math inline">\(p\)</span> a <code>cameraPos</code>. Jako obsługę klawiszy <strong>R</strong> i <strong>F</strong> dodaj przesuwanie kamery w górę i w dół.</p>
|
||
<h1 id="ładowanie-modeli-z-użyciem-assimp">Ładowanie modeli z użyciem assimp**</h1>
|
||
<p>W tym zadaniu przećwiczymy ładowanie modeli z plików, wykorzystamy do tego bibliotekę assimp (The Open Asset Import Library ), która zapewnia wspólny interfejs dla różnych typów plików.</p>
|
||
<p>Funkcja <code>loadModelToContext</code> pobiera ścieżkę do pliku z modelem i wczytuje go przy użyciu importera assimp.</p>
|
||
<div class="sourceCode" id="cb1"><pre class="sourceCode cpp"><code class="sourceCode cpp"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a><span class="at">const</span> aiScene* scene = <span class="kw">import</span>.ReadFile(path, aiProcess_TriangulateaiProcess_Triangulate | aiProcess_CalcTangentSpace);</span></code></pre></div>
|
||
<p>Importer przyjmuje ścieżkę i flagi preprocesingu, które mówią, jakie operacje ma wykonać importer przed przekazaniem nam pliku. W naszym przypadku dokonuje triangularyzacji (zamienia wszystkie wielokąty na trójkąty) i oblicza przestrzeń styczną (o której będzie mowa później).</p>
|
||
<blockquote>
|
||
<p>Wywołaj funkcję dla ścieżki do statku <strong>./models/spaceship.obj</strong> i zmiennej globalnej <code>Core::RenderContext sphereContext</code>. Dodaj breakpoint po załadowaniu sceny i obejrzyj jak wygląda struktura załadowanego obiektu.</p>
|
||
</blockquote>
|
||
<p>Załadowany obiekt posiada szereg pól jak na przykład tekstury, oświetlenia, materiały, węzły (<em>Node</em>) czy modele. Węzły odpowiadają za hierarchię elementów w modelu, co ułatwia jego animację, wykorzystamy to w późniejszych zajęciach. Nasze pliki składają się z tylko jednego modelu, dlatego nie musimy się na tym skupiać i wywołujemy tylko pierwszy model, do którego odwołujemy się za pomocą <code>scene->mMeshes[0]</code>. Wywołaj <code>context.initFromAiMesh</code> z nim jako argumentem.</p>
|
||
<h3 id="zadanie-9">Zadanie**</h3>
|
||
<p>Jeśli tego nie zrobiłeś wywołaj metodę <code>context.initFromAiMesh</code> z argumentem<code>scene->mMeshes[0]</code> w funkcji <code>init</code>, po wczytaniu modelu. Metoda nie jest kompletna, uzupełnij ją o ładowanie indeksów, wierzchołków, normalnych i współrzędnych tekstur do bufora. Współrzędne tekstur i indeksy zostały przekonwertowane do odpowiedniego formatu i znajdują się w zmiennych <code>std::vector<aiVector2D> textureCoord</code> i <code>std::vector<unsigned int> indices</code> odpowiednio. Pozostałe są dostępne jako atrybuty <code>aiMesh</code>, mianowicie <code>mesh->mVertices</code> zawiera wierzchołki a <code>mesh->mNormals</code> normalne.</p>
|
||
<p>Dodatkowo <code>mesh->mNumVertices</code> zawiera liczbę wierzchołków.</p>
|
||
<p>zawierają rozmiary buforów.</p>
|
||
<p>Utwórz jedną duża tablicę/vector, która zawiera informacje o wierzchołkach, normalnych i współrzędnych tekstur. Powinna mieć ona format jak na poniższym obrazku:</p>
|
||
<p><img src="https://i.imgur.com/WLAQtGH.jpg" /></p>
|
||
<p>Gdy załadujesz kontekst, wykorzystaj w <code>renderScene</code> funkcję <code>Core::DrawContext(Core::RenderContext& context)</code> do narysowania obiektów. Rozmieść statek i kulę w przestrzeni za pomocą macierzy transformacji i obrotu.</p>
|
||
</body>
|
||
</html>
|