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 шт):
Во-первых, в кубе должно быть только 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
. Для тебя обработку нажатия стоит изменить.