手动投影类似于python

时间:2018-08-25 14:34:24

标签: python python-3.x opengl projection pyopengl

我正在尝试实现观看矩阵和投影,类似于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)

它发生于坐标的许多变化。我不确定我在哪里错了。

2 个答案:

答案 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的点坐标获得PmatrixMultiply是用于矩阵乘法的任何函数。记住顺序很重要,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上找到的所有数学和教程。