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
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.
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
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