如何从头开始在 pygame 3d 引擎中移动相机?

How to make camera movement in a pygame 3d engine from scratch?

我不熟悉矩阵和矩阵 t运行sformations 和其他东西。我正在使用 2d 在 pygame 中制作自己的 3d 引擎,但我 运行 遇到了问题。我无法让我的引擎实现相机移动,它只能进行相机旋转。这是我的代码:

window.py: 自定义 window class

from .general_imports import *

class Py3dWindow:
    def __init__(self, title=None, width=800, height=600,):
        self.title = title
        self.window = None
        self.width = width
        self.height = height
        self.meshes = []
        self.events_assinged = False
    
        self.projection_matrix = np.matrix([
            [1, 0, 0, 0],
            [0, 1, 0, 0],
            [0, 0, 1, 0],
            [0, 0, 0, 1]
        ])
        print(self.projection_matrix)

        self.camera = [
            [0, 0, 0, 0],
            [0, 0, 0],
            75, # fov
            1, # near
            1000 # far
        ]

    def add_mesh(self, mesh):
        self.meshes.append(mesh)

    def translate_matrix(self):
        angle_x = math.radians(self.camera[1][0])
        angle_y = math.radians(self.camera[1][1])
        angle_z = math.radians(self.camera[1][2])

        pos_x = self.camera[0][0]
        pos_y = self.camera[0][1]
        pos_z = self.camera[0][2]

        rotation_z = np.matrix([
            [int(np.cos(angle_z)), -int(np.sin(angle_z)), 0, 0],
            [int(np.sin(angle_z)), int(np.cos(angle_z)), 0, 0],
            [0, 0, 1, 0],
            [0, 0, 0, 1]
        ],)

        rotation_y = np.matrix([
            [int(np.cos(angle_y)), 0, int(np.sin(angle_y))],
            [0, 1, 0, 0],
            [-int(np.sin(angle_y)), 0, int(np.cos(angle_y))],
            [0, 0, 0, 1]
        ])

        rotation_x = np.matrix([
            [1, 0, 0, 0],
            [0, int(np.cos(angle_x)), -int(np.sin(angle_x)), 0],
            [0, int(np.sin(angle_x)), int(np.cos(angle_x)), 0],
            [0, 0, 0, 1],
        ])

        position_matrix = np.matrix([
            [1, 0, 0, pos_x],
            [0, 1, 0, pos_y],
            [0, 0, 1, pos_z],
            [0, 0, 0, 1],
        ])
        
        self.projection_matrix = np.dot(rotation_z, np.dot(rotation_y, rotation_x)) * position_matrix

        print(self.projection_matrix)

    def init_window(self):
        pygame.init()
        self.window = pygame.display.set_mode((self.width, self.height), pygame.RESIZABLE)
        pygame.display.set_caption(self.title)
        self.running = False

    def run(self,):
        self.running = True
        while self.running:
            self.draw()
            self.events(pygame.event.get())
            self.update()
            self.translate_matrix()
        pygame.quit()

    def events(self, events):
        for event in events:
            if event.type == pygame.QUIT:
                self.running = False
        if self.events_assinged:
            self._events(events)

    def update(self,):
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                self.running = False

    def draw(self,):
        self.window.fill((0, 0, 0))
        for mesh in self.meshes:
            mesh.draw(self.window)
        pygame.display.update()
        time.sleep(0.001)

    def on_events(self, func):
        self._events = func
        self.events_assinged = True
    
    def on_update(self, func):
        self.update = lambda: func()

    def on_draw(self, func):
        self.draw = lambda: func()

mesh.py:网格class

from .general_imports import *

class Mesh:
    def __init__(self, parnet):
        self.points = []
        self.parent = parnet

    def add_point(self, position):
        position = [position[0] * 100, position[1] * 100, position[2] * 100, 1]
        self.points.append(np.matrix(position))

    def draw(self, window):
        for point in self.points:
            p = (point - self.parent.camera[0])
            p = np.reshape(p, (4, 1))
            projection = np.dot(self.parent.projection_matrix, p)
            projection = projection.tolist()
            x = projection[0][0]
            y = projection[1][0]
            pygame.draw.circle(window, (255, 255, 255), (int(x) + window.get_width() / 2 - 100, int(y) + window.get_width() / 2- 100) , 1)

demo.py:演示

# Py3D demo

from Py3D.__init__ import *
import random

win = Py3dWindow(title="Py3D")
win.init_window()

mesh_ = Mesh(win)

side = 1

for i in range(-side, side):
    for j in range(-side, side):
        for k in range(-side, side):
            mesh_.add_point((i, j, k))

win.add_mesh(mesh_)

def move(events):
    for i in events:
        if i.type == 771:
            if i.text == 'w':
                win.camera[0][2] -= 1
            if i.text == 's':
                win.camera[0][2] += 1
            if i.text == 'a':
                win.camera[0][0] -= 1
            if i.text == 'd':
                win.camera[0][0] += 1

def rotate():
    win.camera[1][0] = 45
    win.camera[1][1] = 45
    win.camera[1][2] = 45

win.on_events(move)
win.on_update(rotate)
win.run()

错误

ValueError: shapes (4,4) and (1,4) not aligned: 4 (dim 1) != 1 (dim 0)

我找到的解决方案

在translate_matrix函数中,我忘记在rotation_y中为整个数组添加四个索引。而已!效果很好!

正如您所指出的,在 translate_matrix 函数中,您的 rotation_y 变量声明不正确:

rotation_y = np.matrix([
    [int(np.cos(angle_y)), 0, int(np.sin(angle_y))], # <--- missing an item
    [0, 1, 0, 0],
    [-int(np.sin(angle_y)), 0, int(np.cos(angle_y))], # <--- missing an item
    [0, 0, 0, 1]
])

第一项和第三项只声明了3项,而第二项和第三项以及rotation_xrotation_z的所有项都声明了4项。显然这行不通,因为矩阵应该是矩形的。