我正在尝试实现观看矩阵和投影,类似于gluLookAt来获取每个3D坐标的观看位置。我已经实施了一些似乎可行的方法,但却被撤销了。 例如,以下代码获得了正确的位置(当我实际上不更改坐标时。但是,如果我将向上矢量更改为指向X而不是Y,则得到反向坐标。
import numpy as np
def normalize_vector(vector):
return vector / (np.linalg.norm(vector))
def get_lookat_matrix(position_vector, front_vector, up_vector):
m1 = np.zeros([4, 4], dtype=np.float32)
m2 = np.zeros([4, 4], dtype=np.float32)
z = normalize_vector(-front_vector)
x = normalize_vector(np.cross(up_vector, z))
y = np.cross(z, x)
m1[:3, 0] = x
m1[:3, 1] = y
m1[:3, 2] = z
m1[3, 3] = 1.0
m2[0, 0] = m2[1, 1] = m2[2, 2] = 1.0
m2[:3, 3] = -position_vector
m2[3, 3] = 1.0
return np.matmul(m1, m2)
def get_projection_matrix(near, far):
aspect = 1.0
fov = 1.0 # 90 Degrees
m = np.zeros([4, 4], dtype=np.float32)
m[0, 0] = fov/aspect
m[1, 1] = fov
m[2, 2] = (-far)/(far-near)
m[2, 3] = (-near*far)/(far-near)
m[3, 2] = -1.0
return m
position_vector = np.array([0, 0, 0], dtype=np.float32)
front_vector = np.array([0, 0, -1], dtype=np.float32)
up_vector = np.array([0, 1, 0], dtype=np.float32)
viewing_matrix = get_lookat_matrix(position_vector=position_vector, front_vector=front_vector, up_vector=up_vector)
print("viewing_matrix\n", viewing_matrix, "\n\n")
projection_matrix = get_projection_matrix(near=0.1, far=100.0)
point = np.array([1, 0, -10, 1], dtype=np.float32)
projected_point = projection_matrix.dot(viewing_matrix.dot(point))
# Normalize
projected_point /= projected_point[3]
print(projected_point)
它发生于坐标的许多变化。我不确定我在哪里错了。
答案 0 :(得分:2)
gluLookAt
定义了一个4 * 4的观看转换矩阵,供OpenGL使用。
“数学” 4 * 4矩阵看起来像这样:
c0 c1 c2 c3 c0 c1 c2 c3
[ Xx Yx Zx Tx ] [ 0 4 8 12 ]
[ Xy Yy Zy Ty ] [ 1 5 9 13 ]
[ Xz Yz Zz Tz ] [ 2 6 10 14 ]
[ 0 0 0 1 ] [ 3 7 11 15 ]
但是4 * 4 OpenGL矩阵的内存图像看起来像这样:
[ Xx, Xy, Xz, 0, Yx, Yy, Yz, 0, Zx, Zy, Zz, 0, Tx, Ty, Tz, 1 ]
请参见The OpenGL Shading Language 4.6, 5.4.2 Vector and Matrix Constructors, page 101
和OpenGL ES Shading Language 3.20 Specification, 5.4.2 Vector and Matrix Constructors, page 100:
要通过指定矢量或标量来初始化矩阵,必须以列大顺序将分量分配给矩阵元素。
mat4(float, float, float, float, // first column float, float, float, float, // second column float, float, float, float, // third column float, float, float, float); // fourth column
请注意,与将列从上到下写的数学矩阵相比,这感觉很自然,在OpenGL矩阵初始化时,列是从左到右写的。这样做的好处是,轴或平移的x,y,z分量在内存中直接连续。访问矩阵的轴向量或平移向量时,这是一个很大的优势。
另请参见Data Type (GLSL) - Matrix constructors。
这意味着您必须“交换”矩阵的列和行(转置):
def get_lookat_matrix(position_vector, front_vector, up_vector):
m1 = np.zeros([4, 4], dtype=np.float32)
m2 = np.zeros([4, 4], dtype=np.float32)
z = normalize_vector(-front_vector)
x = normalize_vector(np.cross(up_vector, z))
y = np.cross(z, x)
m1[0, :3] = x
m1[1, :3] = y
m1[2, :3] = z
m1[3, 3] = 1.0
m2[0, 0] = m2[1, 1] = m2[2, 2] = 1.0
m2[3, :3] = -position_vector
m2[3, 3] = 1.0
return np.matmul(m1, m2)
def get_projection_matrix(near, far):
aspect = 1.0
fov = 1.0 # 90 Degrees
m = np.zeros([4, 4], dtype=np.float32)
m[0, 0] = fov/aspect
m[1, 1] = fov
m[2, 2] = (-far+near)/(far-near)
m[3, 2] = (-2.0*near*far)/(far-near)
m[2, 3] = -1.0
return m
答案 1 :(得分:1)
您必须做一个小改动:
m[2, 2] = -(far+near)/(far-near) //instead of m[2, 2] = (-far)/(far-near)
m[2, 3] = (-2.0*near*far)/(far-near) //instead of m[2, 3] = (-near*far)/(far-near)
最重要的是矩阵的行/列顺序。
正如@ Rabbid76指出的那样,市长列顺序是首选。 GLSL提供了转置矩阵的功能。您还可以告诉我们使用glUniformMatrix
系列命令将矩阵传递给GPU时进行转置。
让我们看看如何处理行市长订单矩阵,就像您的代码一样。
目前,使用CPU的目标是:用合并的矩阵finalPoint = matrixMultiply(C, P)
和C
的点坐标获得P
。 matrixMultiply
是用于矩阵乘法的任何函数。记住顺序很重要,A·B与B·A不同
因为C
是4x4矩阵,而P
是1x4,所以C·P是不可能的,它必须是P·C。
请注意,列顺序P
为4x1,则C·P是正确的运算。
让我们L
调用查找矩阵(专有名称为 view matrix )。它由方向矩阵O
和转换矩阵T
组成。带有列的顺序是L= O·T
。
转置矩阵的性质为(A·B) t = B t ·A t
因此,按照行顺序,您将获得O·T = O c t ·T c t = (T c ·O c ) t 其中c
用于列顺序。嘿!我们希望的是(O c ·T c ) t 请注意乘法顺序的变化?
因此,如果您使用行市长顺序矩阵,则会交换它们相乘的顺序。
还必须交换视图和投影组合矩阵。
因此替换:
return np.matmul(m2, m1) //was return np.matmul(m1, m2)
和
//was projected_point = projection_matrix.dot(viewing_matrix.dot(point))
projected_point = point.dot(viewing_matrix.dot(projection_matrix))
尽管有上述所有情况,但我建议使用列市长命令。这对于OpenGL来说是最好的。并且您将更好地了解在OpenGL上找到的所有数学和教程。