Вручную проецируйте координаты, аналогичные gluLookAt в python.

Я пытаюсь реализовать матрицу просмотра и проекцию, аналогичную gluLookAt, чтобы получить позицию просмотра каждой трехмерной координаты. Я реализовал что-то, что кажется близким к работе, но наоборот. Например, следующий код получает правильную позицию (когда я на самом деле не меняю координаты. Но если я изменю вектор вверх, чтобы он указывал на 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)

И это происходит со многими изменениями координат. Я не уверен, где я ошибаюсь.


person user972014    schedule 25.08.2018    source источник


Ответы (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 ]

См. Язык затенения OpenGL 4.6, 5.4.2 Векторные и матричные конструкторы , стр. 101
и OpenGL ES Спецификация Shading Language 3.20, 5.4.2 Векторные и матричные конструкторы, стр. 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 оси или перемещения находятся в памяти в прямой последовательности. Это большое преимущество при доступе к векторам осей или вектору перемещения матрицы.
См. также Тип данных (GLSL) — матричные конструкторы.

Это означает, что вам нужно «поменять местами» столбцы и строки (транспонировать) матрицы:

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
person Rabbid76    schedule 25.08.2018

Есть небольшое изменение, которое вы должны сделать:

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 предоставляет функцию транспонирования матрицы. Вы также можете указать транспонировать матрицу, когда она передается в GPU с помощью команд семейства glUniformMatrix.

Давайте посмотрим, как работать с матрицами порядка строк, как это делает ваш код.

Цель, к настоящему времени с ЦП, состоит в том, чтобы получить: 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·B)t = Bt · At

Таким образом, с порядком строк вы получаете O·T = Oct · Tct = (T< sub>c · 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.

person Ripi2    schedule 25.08.2018