在 python 中手动投影类似于 gluLookAt 的坐标
Manually project coordinates similar to gluLookAt in python
我正在尝试实现观察矩阵和投影,类似于 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)
它发生在坐标的许多变化中。我不确定我哪里错了。
gluLookAt
定义了一个4*4的观察变换矩阵,供OpenGL使用。
一个"mathematical" 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:
To initialize a matrix by specifying vectors or scalars, the components are assigned to the matrix elements in column-major order.
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。
这意味着您必须 "swap" 矩阵的列和行(转置):
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
您必须做一个小改动:
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)
重要的是矩阵的 row/column 顺序。
正如@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
观察矩阵(正确名称是观察矩阵)。它由方向矩阵 O
和平移矩阵 T
组成。列顺序为 L= O·T
.
A属性的转置矩阵为(A·B)t = Bt·At
所以,按行顺序你得到 O·T = Oct · Tct = (Tc · Oc)t 其中 c
用于列顺序。嘿!我们想要的是 (Oc · Tc)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 上找到的任何数学和教程。
我正在尝试实现观察矩阵和投影,类似于 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)
它发生在坐标的许多变化中。我不确定我哪里错了。
gluLookAt
定义了一个4*4的观察变换矩阵,供OpenGL使用。
一个"mathematical" 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:
To initialize a matrix by specifying vectors or scalars, the components are assigned to the matrix elements in column-major order.
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。
这意味着您必须 "swap" 矩阵的列和行(转置):
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
您必须做一个小改动:
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)
重要的是矩阵的 row/column 顺序。
正如@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
观察矩阵(正确名称是观察矩阵)。它由方向矩阵 O
和平移矩阵 T
组成。列顺序为 L= O·T
.
A属性的转置矩阵为(A·B)t = Bt·At
所以,按行顺序你得到 O·T = Oct · Tct = (Tc · Oc)t 其中 c
用于列顺序。嘿!我们想要的是 (Oc · Tc)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 上找到的任何数学和教程。