![](/img/trans.png)
[英]Migrate from fixed-function to GLSL OpenGL: How should I modify my view (lookAt) matrix and perspection matrix?
[英]How to solve problem with lookat matrix on OpenGL/GLSL
我有以下代碼用於我自己的觀察矩陣(矩陣的乘法和向量的叉積工作完美,我檢查了它):
template<typename Type>
void setLookAt(Matrix4x4<Type>& matrix, const Vector3<Type> eye, const Vector3<Type> center, const Vector3<Type> up) noexcept
{
Math::Vector3f right = Math::cross(center, up).normalize();
Matrix4x4f lookAt({
right.getX(), right.getY(), right.getZ(), 0.0,
up.getX(), up.getY(), up.getZ(), 0.0,
center.getX(), center.getY(), center.getZ(), 0.0,
0.0, 0.0, 0.0, 1.0
});
Matrix4x4f additionalMatrix({
0.0, 0.0, 0.0, -(eye.getX()),
0.0, 0.0, 0.0, -(eye.getY()),
0.0, 0.0, 0.0, -(eye.getZ()),
0.0, 0.0, 0.0, 1.0
});
lookAt.mul(additionalMatrix);
matrix = lookAt;
}
template<typename Type>
void setPerspectiveMatrix(Matrix4x4<Type>& matrix, Type fov, Type aspect, Type znear, Type zfar) noexcept
{
const Type yScale = static_cast<Type>(1.0 / tan(RADIANS_PER_DEGREE * fov / 2));
const Type xScale = yScale / aspect;
const Type difference = znear - zfar;
matrix = {
xScale, 0, 0, 0,
0, yScale, 0, 0,
0, 0, (zfar + znear) / difference, 2 * zfar * znear / difference,
0, 0, -1, 0
};
}
矩陣乘法實現:
// static const std::uint8_t ROW_SIZE = 4;
// static const std::uint8_t MATRIX_SIZE = ROW_SIZE * ROW_SIZE;
// static const std::uint8_t FIRST_ROW = 0;
// static const std::uint8_t SECOND_ROW = ROW_SIZE;
// static const std::uint8_t THIRD_ROW = ROW_SIZE + ROW_SIZE;
// static const std::uint8_t FOURTH_ROW = ROW_SIZE + ROW_SIZE + ROW_SIZE;
template<class Type>
void Matrix4x4<Type>::mul(const Matrix4x4& anotherMatrix) noexcept
{
Type currentElements[MATRIX_SIZE];
std::copy(std::begin(mElements), std::end(mElements), currentElements);
const Type* otherElements = anotherMatrix.mElements;
for (std::uint8_t i = 0; i < MATRIX_SIZE; i += ROW_SIZE)
{
mElements[i] = currentElements[i] * otherElements[FIRST_ROW] +
currentElements[i + 1] * otherElements[SECOND_ROW] +
currentElements[i + 2] * otherElements[THIRD_ROW] +
currentElements[i + 3] * otherElements[FOURTH_ROW];
mElements[i + 1] = currentElements[i] * otherElements[FIRST_ROW + 1] +
currentElements[i + 1] * otherElements[SECOND_ROW + 1] +
currentElements[i + 2] * otherElements[THIRD_ROW + 1] +
currentElements[i + 3] * otherElements[FOURTH_ROW + 1];
mElements[i + 2] = currentElements[i] * otherElements[FIRST_ROW + 2] +
currentElements[i + 1] * otherElements[SECOND_ROW + 2] +
currentElements[i + 2] * otherElements[THIRD_ROW + 2] +
currentElements[i + 3] * otherElements[FOURTH_ROW + 2];
mElements[i + 3] = currentElements[i] * otherElements[FIRST_ROW + 3] +
currentElements[i + 1] * otherElements[SECOND_ROW + 3] +
currentElements[i + 2] * otherElements[THIRD_ROW + 3] +
currentElements[i + 3] * otherElements[FOURTH_ROW + 3];
}
}
跨產品實施:
template<typename Type>
Math::Vector3<Type> cross(Vector3<Type> vector, Vector3<Type> anotherVector) noexcept
{
const Type x = vector.getY()*anotherVector.getZ() - vector.getZ()*anotherVector.getY();
const Type y = -(vector.getX()*anotherVector.getZ() - vector.getZ()*anotherVector.getX());
const Type z = vector.getX()*anotherVector.getY() - vector.getY()*anotherVector.getX();
return { x, y, z };
}
使用它:
// OpenGL
glUseProgram(mProgramID);
Matrix4x4f lookAt;
setLookAt(lookAt, { 0.0f, 0.0f, 3.0f }, { 0.0f, 0.0f, -1.0f }, { 0.0f, 1.0f, 0.0f });
glUniformMatrix4fv(glGetAttribLocation(mProgramID, "viewMatrix"), 1, GL_TRUE, lookAt);
Matrix4x4f projection;
setPerspectiveMatrix(projection, 45.0f, width / height, -0.1, 100.0f);
glUniformMatrix4fv(glGetAttribLocation(mProgramID, "projectionMatrix "), 1, GL_TRUE, projection);
// GLSL
layout (location = 0) in vec3 position;
uniform mat4 viewMatrix;
uniform mat4 projectionMatrix;
void main()
{
gl_Position = projectionMatrix * viewMatrix * vec4(position, 1.0f);
}
使用此代碼后,我得到一個空白屏幕,盡管我必須繪制一個立方體。 問題出在矩陣本身,所以其他矩陣可以正常工作(偏移、旋轉等),但我可以確切地理解在哪里。 你能告訴我可能是什么問題嗎?
"projectionMatrix"
和"viewMatrix"
是統一變量。 統一位置可以通過glGetUniformLocation
而不是glGetAttribLocation
,這將返回活動屬性的屬性索引:
GLint projLoc = glGetUniformLocation( mProgramID, "projectionMatrix" );
GLint viewLoc = glGetUniformLocation( mProgramID, "viewMatrix" );
在透視投影中,投影矩陣描述了從針孔相機看到的世界中的 3D 點到視口的 2D 點的映射。
相機視錐體(截棱錐)中的眼睛空間坐標被映射到立方體(標准化設備坐標)。
在透視投影中,視圖空間(體積)由一個截錐體(截棱錐)定義,其中棱錐的頂部是觀察者的位置。 視線方向(視線)以及遠近距離定義了將金字塔截斷為截頭錐體的平面(視線方向是該平面的法向矢量)。
這意味着到近平面的距離和到遠平面的距離這兩個值都必須是正值:
Matrix4x4f lookAt;
setLookAt(lookAt, { 0.0f, 0.0f, 3.0f }, { 0.0f, 0.0f, -1.0f }, { 0.0f, 1.0f, 0.0f });
glUniformMatrix4fv(viewLoc, 1, GL_TRUE, lookAt);
Matrix4x4f projection;
setPerspectiveMatrix(projection, 45.0f, width / height, 0.1f, 100.0f); // 0.1f instead of -0.1f
glUniformMatrix4fv(projLoc, 1, GL_TRUE, projection);
視圖空間是局部系統,由場景中的視點定義。 視圖的位置、視線和視圖的向上方向,定義了一個相對於世界坐標系的坐標系。
視圖矩陣必須從世界空間轉換到視圖空間,因此視圖矩陣是視圖坐標系的逆矩陣。
如果視圖空間的坐標系是右手坐標系,其中 X 軸指向左側,Y 軸指向上方,則 Z 軸指向視圖外(注意在右手坐標系中Z 軸是 X 軸和 Y 軸的叉積)。
z 軸視線是從視點eye
到目標center
的向量:
template<typename Type>
void setLookAt(Matrix4x4<Type>& matrix, const Vector3<Type> eye, const Vector3<Type> center, const Vector3<Type> up) noexcept
{
Vector3f mz( { eye.getX()-center.getX(), eye.getY()-center.getY(), eye.getZ()-center.getZ() } );
mz = mz.normalize();
Vector3f my = up.normalize();
Vector3f mx = cross(my, mz).normalize();
Type tx = dot( mx, eye );
Type ty = dot( my, eye );
Type tz = -dot( mz, eye );
matrix = {
mx.getX(), mx.getY(), mx.getZ(), tx,
my.getX(), my.getY(), my.getZ(), ty,
mz.getX(), mz.getY(), mz.getZ(), tz,
0.0, 0.0, 0.0, 1.0
};
}
template<typename Type>
Vector3<Type> cross(Vector3<Type> vector, Vector3<Type> anotherVector) noexcept
{
const Type x = vector.getY()*anotherVector.getZ() - vector.getZ()*anotherVector.getY();
const Type y = -(vector.getX()*anotherVector.getZ() - vector.getZ()*anotherVector.getX());
const Type z = vector.getX()*anotherVector.getY() - vector.getY()*anotherVector.getX();
return { x, y, z };
}
template<typename Type>
Vector3<Type> Vector3<Type>::normalize(void) const
{
Type len = std::sqrt(mV[0]*mV[0] + mV[1]*mV[1] + mV[2]*mV[2]);
return { mV[0] / len, mV[1] / len, mV[2] / len };
}
template<typename Type>
Type dot(Vector3<Type> vector, Vector3<Type> anotherVector) noexcept
{
Type ax = vector.getX(), ay = vector.getY(), az = vector.getZ();
Type bx = anotherVector.getX(), by = anotherVector.getY(), bz = anotherVector.getZ();
return ax*bx + ay*by + az*bz;
}
透視投影矩陣可以由一個截錐體定義。
距離left
、 right
、 bottom
和top
是在近平面上從視圖中心到截錐體側面的距離。 near
和far
指定到截錐體上近平面和遠平面的距離。
r = right, l = left, b = bottom, t = top, n = near, f = far
x y z t
2*n/(r-l) 0 (r+l)/(r-l) 0
0 2*n/(t-b) (t+b)/(t-b) 0
0 0 -(f+n)/(f-n) -2*f*n/(f-n)
0 0 -1 0
如果投影是對稱的,其中視線是視錐體的對稱軸,則矩陣可以簡化:
x y z t
1/(ta*a) 0 0 0
0 1/ta 0 0
0 0 -(f+n)/(f-n) -2*f*n/(f-n)
0 0 -1 0
在哪里:
a = w / h
ta = tan( fov_y / 2 );
2 * n / (r-l) = 1 / (ta * a)
2 * n / (t-b) = 1 / ta
此外,投影矩陣從右手系切換到左手系,因為 z 軸旋轉。
template<typename Type>
void setPerspectiveMatrix(Matrix4x4<Type>& matrix, Type fov, Type aspect, Type znear, Type zfar) noexcept
{
const Type yScale = static_cast<Type>(1.0 / tan(RADIANS_PER_DEGREE * fov / 2));
const Type xScale = yScale / aspect;
const Type difference = zfar - znear;
matrix = {
xScale, 0, 0, 0,
0, yScale, 0, 0,
0, 0, -(zfar + znear) / difference, -2 * zfar * znear / difference,
0, 0, -1, 0
};
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.