2015-02-02 16 views
8

Wszystkie biblioteki macierzy dla WebGL mają jakąś funkcję o nazwie perspective, którą wywołuje się, aby uzyskać macierz perspektywy dla sceny.
Na przykład, metoda perspective w mat4.js file that's part of gl-matrix jest kodowana jako takie:Próba zrozumienia matematyki za macierzą perspektywy w WebGL

mat4.perspective = function (out, fovy, aspect, near, far) { 
    var f = 1.0/Math.tan(fovy/2), 
     nf = 1/(near - far); 
    out[0] = f/aspect; 
    out[1] = 0; 
    out[2] = 0; 
    out[3] = 0; 
    out[4] = 0; 
    out[5] = f; 
    out[6] = 0; 
    out[7] = 0; 
    out[8] = 0; 
    out[9] = 0; 
    out[10] = (far + near) * nf; 
    out[11] = -1; 
    out[12] = 0; 
    out[13] = 0; 
    out[14] = (2 * far * near) * nf; 
    out[15] = 0; 
    return out; 
}; 

ja naprawdę staram się zrozumieć, co to wszystko matematyka w tej metodzie jest rzeczywiście robi, ale ja potknięcia się na kilku punktach .

Po pierwsze, jeśli mamy płótno w następujący sposób o proporcjach 4: 3, to parametr metody będzie w rzeczywistości 4/3, prawda?

4:3 aspect ratio

Zauważyłem również, że 45 ° wydaje się wspólnym polu widzenia. Jeśli tak jest, to parametr fovy będzie wynosić π/4 radianów, prawda?

Co to jest zmienna f w metodzie skrótowej i jaki jest jej cel?
starałem się wyobrazić rzeczywisty scenariusz, a ja sobie wyobrazić coś jak następuje:

Side view of [perspective in 3D scene

Myśląc w ten sposób, mogę zrozumieć, dlaczego podzielić fovy przez 2 a także dlaczego bierzesz tangens że stosunek, ale dlaczego jest odwrotnością tej przechowywanej w f? Znowu mam wiele problemów ze zrozumieniem, co naprawdę reprezentuje to, co f.

Następnie mam pojęcia near i far będąc punkty strzyżenia wzdłuż osi, tak że jest w porządku, ale jeśli mogę użyć numery na zdjęciu powyżej (tj π/4, 4/3, 10 i 100) i podłączyć do sposobu perspective, a potem kończy się z matrycy, jak następuje:

enter image description here

przypadku f wynosi:

enter image description here

Więc ja zostaję z następującymi pytaniami:

  1. Co jest f?
  2. Co oznacza wartość przypisana do out[10] (tj. 110/-90)?
  3. Co robi -1 przypisana do out[11]?
  4. Co oznacza wartość przypisana do out[14] (tj. 2000/-90)?

Na koniec, powinienem zauważyć, że przeczytałem już Gregg Tavares's explanation on the perspective matrix, ale po tym wszystkim jestem z tym samym zakłopotaniem.

+0

Może [ten link] (http://www.songho.ca/opengl/gl_projectionmatrix.html) trochę pomaga. Jest to odniesienie do przestarzałej funkcji GL o stałej funkcji, ale matematyka jest nadal aktualna. – derhass

+0

Przepraszam, ale ten link był jeszcze bardziej zagmatwany niż wszystkie inne linki, które dotąd oglądałem. Domyślam się, że to, o co proszę, to więcej niż wyjaśnienie matematyczne to koncepcyjne wyjaśnienie tego, co się dzieje i jak formuje się matrycę, biorąc pod uwagę faktyczny scenariusz. – HartleySan

Odpowiedz

12

Zobaczmy, czy mogę to wyjaśnić, a może po przeczytaniu tego można wymyślić lepszy sposób na wyjaśnienie.

Po pierwsze, WebGL wymaga współrzędnych przestrzeni klipów. One-< -> +1 na x, yi z. Tak więc matryca perspektywiczna jest zasadniczo zaprojektowana tak, aby zajmować przestrzeń wewnątrz modelu i konwertować ją do przestrzeni klipów.

Jeśli spojrzeć na tym schemacie

frustum-side

wiemy, że styczna = odwrotny (Y) nad sąsiednim (Z), więc jeśli wiemy oo możemy obliczyć y, które byłyby siedzi na krawędzi frustum dla danego fovY.

tan(fovY/2) = y/-z 

pomnożyć obie strony przez -z

y = tan(fovY/2) * -z 

jeśli definiujemy

f = 1/tan(fovY/2) 

otrzymujemy

y = -z/f 

notatka nie wykonali konwersję z cameraspace do clipspace. Wszystko, co zrobiliśmy, to obliczenie na skraju pola widzenia dla danego z w przedziale kamerowym. Krawędź pola widzenia jest również krawędzią przestrzeni klipu. Ponieważ spacja ma rozmiar od +1 do -1, możemy po prostu podzielić spację kamerową przez -z/f, aby uzyskać spację.

Czy to ma sens? Spójrz na schemat ponownie. Załóżmy, że niebieski z był -5 i dla pewnego pola widzenia y wyszedł do +2.34. Musimy przekonwertować +2.34 na +1 clipspace. Generyczna wersja to

clipY = cameraY * f/-z

Patrząc na `makePerspective”

function makePerspective(fieldOfViewInRadians, aspect, near, far) { 
    var f = Math.tan(Math.PI * 0.5 - 0.5 * fieldOfViewInRadians); 
    var rangeInv = 1.0/(near - far); 

    return [ 
    f/aspect, 0, 0, 0, 
    0, f, 0, 0, 
    0, 0, (near + far) * rangeInv, -1, 
    0, 0, near * far * rangeInv * 2, 0 
    ]; 
}; 

widzimy, że w tym przypadku f

tan(Math.PI * 0.5 - 0.5 * fovY) 

który jest w rzeczywistości taki sam, jak

Dlaczego jest napisane w ten sposób? Zgaduję, ponieważ gdybyś miał pierwszy styl i tan opuściłby 0, dzieliłbyś przez 0, twój program zawiesza się, jeśli tak jest, w ten sposób nie ma podziału, więc nie ma szansy na dzielenie przez zero.

Widząc, że -1 jest matrix[11] miejscu znaczy kiedy skończymy

matrix[5] = tan(Math.PI * 0.5 - 0.5 * fovY) 
matrix[11] = -1 

clipY = cameraY * matrix[5]/cameraZ * matrix[11] 

Dla clipX my w zasadzie zrobić dokładnie to samo obliczenie wyjątkiem skalowane do proporcji.

matrix[0] = tan(Math.PI * 0.5 - 0.5 * fovY)/aspect 
matrix[11] = -1 

clipX = cameraX * matrix[0]/cameraZ * matrix[11] 

Wreszcie trzeba przekształcić cameraZ w -zNear < -> -zFar zakresie do CLIPZ w < -1 -> + 1 zakresu.

Standardowa matryca perspektywiczna wykonuje to z wartością reciprocal function, dzięki czemu wartości z bliska powodują, że kamera uzyskuje większą rozdzielczość niż wartości z daleko od kamery. Że formuła jest

clipZ = something/cameraZ + constant 

Użyjmy s dla something i c na stałe.

clipZ = s/cameraZ + c; 

i rozwiązania dla s i c.W naszym przypadku wiemy

s/-zNear + c = -1 
s/-zFar + c = 1 

więc przesunąć `c” na drugą stronę

s/-zNear = -1 - c 
s/-zFar = 1 - c 

pomnożyć przez -zXXX

s = (-1 - c) * -zNear 
s = (1 - c) * -zFar 

tych 2 rzeczy teraz równa siebie tak

(-1 - c) * -zNear = (1 - c) * -zFar 

powiększ ilości

(-zNear * -1) - (c * -zNear) = (1 * -zFar) - (c * -zFar) 

uprościć

zNear + c * zNear = -zFar + c * zFar 

ruch zNear w prawo

c * zNear = -zFar + c * zFar - zNear 

przenieść c * zFar w lewo

c * zNear - c * zFar = -zFar - zNear 

uproszczenia

c * (zNear - zFar) = -(zFar + zNear) 

podzielić przez (zNear - zFar)

c = -(zFar + zNear)/(zNear - zFar) 

rozwiązania dla s

s = (1 - -((zFar + zNear)/(zNear - zFar))) * -zFar 

uproszczenia

s = (1 + ((zFar + zNear)/(zNear - zFar))) * -zFar 

zmiany liczby 1 do (zNear - zFar)

s = ((zNear - zFar + zFar + zNear)/(zNear - zFar)) * -zFar 

uprościć

s = ((2 * zNear)/(zNear - zFar)) * -zFar 

uprościć niektóre więcej

s = (2 * zNear * zFar)/(zNear - zFar) 

Dang życzę Stack Exchange Network wspierany matematyki jak ich stronie matematyki robi :(

więc z powrotem na górę. Nasz forumla był

s/cameraZ + c 

Wiemy s i c teraz.

clipZ = (2 * zNear * zFar)/(zNear - zFar)/-cameraZ - 
     (zFar + zNear)/(zNear - zFar) 

przejdźmy -z zewnątrz

clipZ = ((2 * zNear * zFar)/zNear - ZFar) + 
     (zFar + zNear)/(zNear - zFar) * cameraZ)/-cameraZ 

możemy zmienić / (zNear - zFar) do * 1/(zNear - zFar) tak

rangeInv = 1/(zNear - zFar) 
clipZ = ((2 * zNear * zFar) * rangeInv) + 
     (zFar + zNear) * rangeInv * cameraZ)/-cameraZ 

Patrząc wstecz na makeFrustum widzimy to się skończyć dokonywania

clipZ = (matrix[10] * cameraZ + matrix[14])/(cameraZ * matrix[11]) 

Patrząc na powyższym wzorem, który pasuje

rangeInv = 1/(zNear - zFar) 
matrix[10] = (zFar + zNear) * rangeInv 
matrix[14] = 2 * zNear * zFar * rangeInv 
matrix[11] = -1 
clipZ = (matrix[10] * cameraZ + matrix[14])/(cameraZ * matrix[11]) 

Mam nadzieję, że sens. Uwaga: Większość z nich to po prostu moje ponowne pisanie this article.

+0

Już prawie od nowa rozważałem twoją odpowiedź, odkąd opublikowałeś ją kilka dni temu, i wreszcie, zaczyna się to zbierać, chociaż przyznaję, że wciąż jestem trochę zdezorientowana. Mam również bardzo mocno na [twój post] (http://games.greggman.com/game/webgl-3d-perspective/), a także wyjaśnienia na temat macierzy perspektywy w ... – HartleySan

+0

[Ta książka] (http://www.amazon.com/WebGL-Programming-Guide-Interactive-Graphics/dp/0321902920/ref=sr_1_1?ie=UTF8&qid=1423125734&sr=8-1&keywords=WebGL) i [ta książka] (http: // www.amazon.com/Math-Primer-Graphics-Game-Development/dp/1568817231/ref=sr_1_1?ie=UTF8&qid=1423125771&sr=8-1&keywords=3D+Math), aby lepiej zrozumieć, co się dzieje. Z całym tym stwierdzeniem, jest to moje dotychczasowe zrozumienie i proszę poprawić, jeśli się mylę: – HartleySan

+0

'Math.tan (fovy/2)' zasadniczo opisuje związek między 'y' i' z'. (Przecież to po prostu podstawowa trygonometryczna definicja stycznej.) Jako taka, odwrotność tego jest równa 'maxZ/maxY'. W związku z tym, gdy przez to wiele wierzchołków y, podział przez 'maxY' zasadniczo ma wpływ na normalizację y od 0 do 1. Stąd, im większe Z, tym większa staje się wartość y. Ponadto, rozumiem, dlaczego robisz to samo dla x, ale także współczynnik proporcji. W porządku. Teraz, gdzie nadal jestem bardzo zdezorientowany, to część z. Dla jednego ... – HartleySan

0

f jest czynnikiem, który skaluje osi Y tak, że wszystkie punkty wzdłuż górnej powierzchni swojej ściętego widzenia, post-perspektywicznym-Division, mają współrzędną y 1, a te na dolnej powierzchni mają współrzędna y z -1. Spróbuj podpiąć punkty wzdłuż jednej z tych płaszczyzn (przykłady: 0, 2.41, 1, 2, 7.24, 3) i możesz zobaczyć, dlaczego tak się dzieje: ponieważ kończy się przed dzieleniem y równym jednorodnemu w.

+0

Sneftel, dzięki za odpowiedź. Co rozumiesz przez "prawy samolot" i "lewy samolot"? Poza tym twoja odpowiedź brzmi, jakby 'f' po prostu normalizował wszystko w odniesieniu do wartości' y', ale nie rozumiem dlaczego i co to ma wspólnego z homogenicznym w. Czy mógłbyś podać kilka wglądów w pytania # 2 # 4 powyżej? Dziękuję bardzo. – HartleySan

+0

Przepraszam, powinienem był powiedzieć "górny lub dolny" samolot. Rozważ wszystkie punkty w przestrzeni, które po wyrenderowaniu będą rysowane jako piksele na szczycie lub na dole ekranu. Tworzą płaskie powierzchnie na świecie. – Sneftel

+0

Podobnie jak w przypadku 2 i 4, tak jak skala "f" i "f/aspekt" xiy do zakresu (-1,1), skala z do zakresu (-1, 1). A 3 to ustawienie podziału perspektywy. Myślę, że możesz potrzebować trochę więcej na papierze, żeby zrozumieć, co tu się dzieje. W szczególności zobacz, czy możesz określić, dlaczego punkty o dużej współrzędnej z są rysowane bliżej siebie niż punkty o małej współrzędnej z. – Sneftel