Не работает код для отрисовки трёхмерного куба Python

Рейтинг: -2Ответов: 1Опубликовано: 09.08.2023

main.py

import pygame
from object_3d import *
from camera import *
from projection import *


class SoftwareRender:
    def __init__(self):
        pygame.init()
        self.WIDTH, self.HEIGHT = 1200, 700
        self.RES = self.WIDTH, self.HEIGHT 
        self.H_WIDTH, self.H_HEIGHT = self.WIDTH // 2, self.HEIGHT // 2
        self.FPS = 60
        self.screen = pygame.display.set_mode(self.RES)
        self.clock = pygame.time.Clock()
        self.create_objact()

    def create_objact(self):
        self.camera = Camera(self, [0.5, 1, -4])
        self.projection = Projection(self)
        self.objact = Objact3D(self)
        self.objact.translate([0.2, 0.4, 0.2])
        self.objact.rotate_y(math.pi/6)

    def draw(self):
        self.screen.fill(pygame.Color('darkslategray'))
        self.objact.draw()
    def run(self):
        while True:
            self.draw()
            [exit() for i in pygame.event.get() if i.type == pygame.QUIT]
            pygame.display.set_caption(str(self.clock.get_fps()))
            pygame.display.flip()
            self.clock.tick(self.FPS)


app = SoftwareRender()
app.run()

matrix.py

import math, numpy

def mx_translate(pos):
    tx, ty, tz = pos
    return numpy.array([
        [1, 0, 0, 0],
        [0, 1, 0, 0],
        [0, 0, 1, 0],
        [tx, ty, tz, 1]
    ])


def mx_rotate_x(a):
    return numpy.array([
        [1, 0, 0, 0],
        [0, math.cos(a), math.sin(a), 0],
        [0, -math.sin(a), math.cos(a), 0],
        [0, 0, 0, 1]
    ])


def mx_rotate_y(a):
    return numpy.array([
        [math.cos(a), 0, -math.sin(a), 0],
        [0, 1, 0, 0],
        [math.sin(a), 0, math.cos(a), 0],
        [0, 0, 0, 1]
    ])


def mx_rotate_z(a):
    return numpy.array([
        [math.cos(a), math.sin(a), 0, 0],
        [-math.sin(a), math.cos(a), 0, 0],
        [0, 0, 1, 0],
        [0, 0, 0, 1]
    ])


def mx_scale(n):
    return numpy.array([
        [n, 0, 0, 0],
        [0, n, 0, 0],
        [0, 0, n, 0],
        [0, 0, 0, 1]
    ])

objact_3d.py

import math, numpy

def mx_translate(pos):
    tx, ty, tz = pos
    return numpy.array([
        [1, 0, 0, 0],
        [0, 1, 0, 0],
        [0, 0, 1, 0],
        [tx, ty, tz, 1]
    ])


def mx_rotate_x(a):
    return numpy.array([
        [1, 0, 0, 0],
        [0, math.cos(a), math.sin(a), 0],
        [0, -math.sin(a), math.cos(a), 0],
        [0, 0, 0, 1]
    ])


def mx_rotate_y(a):
    return numpy.array([
        [math.cos(a), 0, -math.sin(a), 0],
        [0, 1, 0, 0],
        [math.sin(a), 0, math.cos(a), 0],
        [0, 0, 0, 1]
    ])


def mx_rotate_z(a):
    return numpy.array([
        [math.cos(a), math.sin(a), 0, 0],
        [-math.sin(a), math.cos(a), 0, 0],
        [0, 0, 1, 0],
        [0, 0, 0, 1]
    ])


def mx_scale(n):
    return numpy.array([
        [n, 0, 0, 0],
        [0, n, 0, 0],
        [0, 0, n, 0],
        [0, 0, 0, 1]
    ])

camera.py

import pygame
from matrix import *

class Camera:
    def __init__ (self, render, position):
        self.render = render
        self.position = numpy.array([*position, 1.0])
        self.forward = numpy.array([0,0,1,1])
        self.up = numpy.array([0,1,0,1])
        self.right = numpy.array([1,0,0,1])
        self.h_fov = math.pi/3
        self.v_fov = self.h_fov * (render.HEIGHT / render.WIDTH)
        self.near_plane = 0.1
        self.far_plane = 100

    def translate_matrix(self):
        x, y, z, w = self.position
        return numpy.array([
            [1, 0, 0, 0,],
            [0, 1, 0, 0,],
            [0, 0, 1, 0,],
            [-x, -y, -z, 1,]
        ])
    def rotate_matrix(self):
        rx, ry, rz, w = self.right
        fx,fy, fz, w = self.forward
        ux, uy, uz, w = self.up
        return numpy.array([
            [rx, ux, fx, 0],
            [ry, uy, fy, 0],
            [rz, uz, fz, 0],
            [0, 0, 0, 1],
        ])
    
    def camera_matrix(self):
        return self.translate_matrix() @ self.rotate_matrix()

projection.py

import math, numpy

class Projection:
    def __init__(self, render):
        NEAR = render.camera.near_plane
        FAR = render.camera.far_plane
        RIGHT = math.tan(render.camera.h_fov / 2)
        LEFT = -RIGHT
        TOP = math.tan(render.camera.v_fov / 2)
        BOTTOM = -TOP
        m00 = 2 / (RIGHT - LEFT)
        m11 = 2 / (TOP / BOTTOM)
        m22 = (FAR + NEAR) / (FAR - NEAR)
        m32 = -2 * NEAR * FAR / (FAR - NEAR)
        self.projection_matrix = numpy.array([
            [m00,0,0,0],
            [0,m11,0,0],
            [0,0,m22,0],
            [0,0,m32,0],
        ])

        HW, HH = render.H_WIDTH, render.H_HEIGHT
        self.to_screen_matrix = numpy.array([
            [HW, 0, 0, 0,],
            [0, -HH, 0, 0,],
            [0, 0, 1, 0,],
            [HW, HH, 0, 1,],
        ])

Ответы

▲ 0Принят

Код проекционной матрицы в файле неверен. Корректный код должен быть следующим:projection.py

def projection_matrix(self):
    NEAR = self.render.camera.near_plane
    FAR = self.render.camera.far_plane
    RIGHT = math.tan(self.render.camera.h_fov / 2) * self.render.WIDTH
    LEFT = -RIGHT
    TOP = math.tan(self.render.camera.v_fov / 2) * self.render.HEIGHT
    BOTTOM = -TOP
    m00 = 2 / (RIGHT - LEFT)
    m11 = 2 / (TOP - BOTTOM)
    m22 = (FAR + NEAR) / (FAR - NEAR)
    m32 = -2 * NEAR * FAR / (FAR - NEAR)
    self.projection_matrix = numpy.array([
        [m00,0,0,0],
        [0,m11,0,0],
        [0,0,m22,0],
        [0,0,m32,1],
    ])

Исправленный код правильно спроецирует 3D-сцену на 2D-экран.