3д графика, баг при повороте камеры

впервые на stack overflow так что не судите строго. пишу свой 3д движок на python с нуля, баг происходит при повороте камеры по оси x. с первого взгляда поворот происходит нормально, но если встать сбоку от объекта и повернуть камеру объект повернется по оси z а не по x.

так же если встать за или внутрь объекта он ломается, но это я пофиксю сам.

матрица поворота верна, я сверял два раза, использую numpy и pygame. код ниже:

import numpy as np
from math import cos, sin, radians
import pygame, sys

pygame.init()

def vec3(x, y, z):
    return x, y, z

def project_point(vec, camera_vec, distance):
    vec = np.array(vec) - np.array(camera_vec)
    d = distance
    projection_matrix = np.array([
        [d, 0, 0],
        [0, d, 0]
    ])
    if d + vec[2] != 0:
        projected_point = np.dot(projection_matrix, vec) / (d + vec[2])
        return projected_point
    return (0, 0)

def rotate(vec, euler_angle, center):
    center = np.array(center)
    vec = np.array(vec) - center
    x, y, z = radians(euler_angle[0]), radians(euler_angle[1]), radians(euler_angle[2])
    rotate_matrix = np.array([
        [cos(y)*cos(z),                        -sin(z)*cos(y),                         sin(y)       ],
        [sin(x)*sin(y)*cos(z) + sin(z)*cos(x), -sin(x)*sin(y)*sin(z) + cos(x)*cos(z), -sin(x)*cos(y)],
        [sin(x)*sin(z) - sin(y)*cos(x)*cos(z),  sin(x)*cos(z) + sin(y)*sin(z)*cos(x),  cos(x)*cos(y)]
    ])
    rotated_vector = (vec @ rotate_matrix) + center
    return rotated_vector

def draw(surface, u, edges, draw_vertexes=False, draw_edges=True):
    up = []
    for vec in u:
        cv = camera_vec
        final_vec = rotate(vec, [crx, cry, crz], (cv[0], cv[1], cv[2]-cdistance))
        projected_point = project_point(final_vec, camera_vec, cdistance)
        up.append((projected_point[0]+hw, projected_point[1]+hh))
    if draw_vertexes:
        for i in up:
            pygame.draw.circle(screen, (255, 255, 255), (i[0], i[1]), 5)

    if draw_edges:
        for edge in edges:
            pygame.draw.line(screen, (0, 0, 0), up[edge[0]], up[edge[1]], 1)

def update_camera(camera_pos, camera_rotation):
    cx, cy, cz = camera_pos
    crx, cry, crz = camera_rotation
    keys = pygame.key.get_pressed()
    if keys[pygame.K_w]:
        cx += sin(radians(cry))
        cz += cos(radians(cry))
    if keys[pygame.K_s]:
        cx -= sin(radians(cry))
        cz -= cos(radians(cry))
    if keys[pygame.K_a]:
        cx -= cos(radians(cry))
        cz += sin(radians(cry))
    if keys[pygame.K_d]:
        cx += cos(radians(cry))
        cz -= sin(radians(cry))
    if keys[pygame.K_SPACE]:  cy -= 1
    if keys[pygame.K_LSHIFT]: cy += 1

    if keys[pygame.K_LEFT]:   cry = (cry - 0.5) % 360 
    if keys[pygame.K_RIGHT]:  cry = (cry + 0.5) % 360 
    if keys[pygame.K_UP]:     crx = (crx + 0.5) % 360 
    if keys[pygame.K_DOWN]:   crx = (crx - 0.5) % 360 

    updated_camera_pos = cx, cy, cz
    updated_camera_rotation = crx, cry, crz

    return updated_camera_pos, updated_camera_rotation

res = w, h = 800, 600
hw, hh = w // 2, h // 2

screen = pygame.display.set_mode(res)
pygame.display.set_caption('3d engine')

cx, cy, cz = 50, -50, -100
crx, cry, crz = [0, 0, 0]

cdistance = 250

camera_vec = vec3(cx, -cy, cz)
u = [
    vec3(0,   0,     0), vec3(0,   100,   0),
    vec3(100, 100,   0), vec3(100, 0,     0),

    vec3(0,   0,   100), vec3(0,   100, 100),
    vec3(100, 100, 100), vec3(100, 0,   100)
]

edges = [
    (0, 1), (1, 2), (2, 3), (3, 0),
    (4, 5), (5, 6), (6, 7), (7, 4),
    (0, 1), (1, 5), (5, 4), (4, 0),
    (3, 2), (2, 6), (6, 7), (7, 3)
]

while True:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            pygame.quit()
            sys.exit()

    draw(screen, u, edges)

    updated_camera = update_camera(camera_vec, [crx, cry, crz])

    cx, cy, cz = updated_camera[0]
    crx, cry, crz = updated_camera[1]

    camera_vec = vec3(cx, cy, cz)

    pygame.display.flip()
    screen.fill((80, 80, 100))

код может выглядить плохо, я понимаю.


Ответы (1 шт):

Автор решения: Ersamuel

Во-первых, в кубе должно быть только 12 ребер, а у тебя указано 16, причем некоторые из них повторяются. Содержимое edges изменить на:

edges = [
(0, 1), (1, 2), (2, 3), (3, 0),
(4, 5), (5, 6), (6, 7), (7, 4),
(0, 4), (1, 5), (2, 6), (3, 7)]

Камера действует так, как должна: поворачивается относительно оси X, а не относительно направления cry. Для тебя обработку нажатия стоит изменить.

→ Ссылка