Как правильно сделать отображение диффузионного света в python 3.13 с помощью OpenGL?
Данный код нужен для отрисовки файлов obg и был написан по видеоуроку, т.к. с библиотекой OpenGL я знаком впервые.
Сам я имею только общее представление о том, как работает освещение в питоне.
Код разбит на несколько разных листов, они представлены ниже, но сейчас в нём возникает ошибка в листе object_3d на линии 65.
IndexError: index 6 is out of bounds for axis 0 with size 6
main.py
from object_3d import *
from camera import *
from projection import *
import pygame as pg
screen_zoom = 1.5
class SoftwareRender:
def __init__(self):
pg.init()
self.RES = self.WIDTH, self.HEIGHT = 1600/screen_zoom, 900/screen_zoom
self.H_WIDTH, self.H_HEIGHT = self.WIDTH // 2, self.HEIGHT // 2
self.FPS = 60
self.screen = pg.display.set_mode(self.RES)
self.clock = pg.time.Clock()
self.create_objects()
def create_objects(self):
self.camera = Camera(self, [-5, 6, -55])
self.projection = Projection(self)
self.object = self.get_object_from_file('resources/single_Cube.obj') #Здесь указывается имя файла для отрисовки
self.object.rotate_y(-math.pi / 4)
def get_object_from_file(self, filename):
vertex, faces, vertex_normal= [], [], []
with open(filename) as f:
for line in f:
if line.startswith('v '):
vertex.append([float(i) for i in line.split()[1:]] + [1])
elif line.startswith('f'):
faces_ = line.split()[1:]
faces.append([int(face_.split('/')[0]) - 1 for face_ in faces_])
elif line.startswith('vn'):
vertex_normal.append([float(i) for i in line.split()[1:]])
print(vertex_normal)
return Object3D(self, vertex, faces, vertex_normal)
def draw(self):
self.screen.fill((45,45,45))
self.object.draw()
def run(self):
while True:
self.draw()
self.camera.control()
[exit() for i in pg.event.get() if i.type == pg.QUIT]
pg.display.set_caption(str(self.clock.get_fps()))
#print(str(self.clock.get_fps()))
pg.display.flip()
self.clock.tick(self.FPS)
if __name__ == '__main__':
app = SoftwareRender()
app.run()
object_3d.py
import pygame as pg
from matrix_functions import *
from numba import njit
from OpenGL.GL import *
from OpenGL.GLU import *
verticies = (
(1, -1, -1), # 0
(1, 1, -1), # 1
(-1, 1, -1), # 2
(-1, -1, -1), # 3
(1, -1, 1), # 4
(1, 1, 1), # 5
(-1, -1, 1), # 6
(-1, 1, 1), # 7
)
colors = (
(1, 1, 1),
(0, 1, 0),
(0, 0, 1),
(0, 1, 0),
(0, 0, 1),
(1, 0, 1),
(0, 1, 0),
(1, 0, 1),
(0, 1, 0),
(0, 0, 1),
)
@njit(fastmath=True)
def any_func(arr, a, b):
return np.any((arr == a) | (arr == b))
class Object3D:
def __init__(self, render, vertices='', faces='', vertices_normal=''):
self.render = render
self.vertices = np.array(vertices)
self.vertices_normal = np.array(vertices_normal)
print('\n',self.vertices_normal)
self.faces = faces
self.translate([0.0001, 0.0001, 0.0001])
self.font = pg.font.SysFont('Arial', 30, bold=True)
self.color_faces = [(pg.Color('orange'), face) for face in self.faces]
self.movement_flag, self.draw_vertices = True, False
self.label = ''
def draw(self):
self.screen_projection()
self.color_projection()
self.movement()
def movement(self):
if self.movement_flag:
self.rotate_y(-(pg.time.get_ticks() % 0.005))
def color_projection(self):
glBegin(GL_QUADS)
for i_surface, surface in enumerate(self.faces):
x = 0
glNormal3fv(self.vertices_normal[i_surface])
for vertex in surface:
x += 1
glColor3fv(colors[x])
glVertex3fv(verticies[vertex])
glEnd()
glColor3fv(colors[0])
glBegin(GL_LINES)
for edge in edges:
for vertex in edge:
glVertex3fv(verticies[vertex])
glEnd()
def screen_projection(self):
vertices = self.vertices @ self.render.camera.camera_matrix()
vertices = vertices @ self.render.projection.projection_matrix
vertices /= vertices[:, -1].reshape(-1, 1)
vertices[(vertices > 2) | (vertices < -2)] = 0
vertices = vertices @ self.render.projection.to_screen_matrix
vertices = vertices[:, :2]
print('\n','\n',vertices)
for index, color_face in enumerate(self.color_faces):
color, face = color_face
polygon = vertices[face]
print("\n",polygon)
if not any_func(polygon, self.render.H_WIDTH, self.render.H_HEIGHT):
pg.draw.polygon(self.render.screen, (199, 202, 207), polygon)
#pg.draw.polygon(self.render.screen, (97, 98, 102), polygon)
if self.label:
text = self.font.render(self.label[index], True, pg.Color('white'))
self.render.screen.blit(text, polygon[-1])
if self.draw_vertices:
for vertex in vertices:
if not any_func(vertex, self.render.H_WIDTH, self.render.H_HEIGHT):
pg.draw.circle(self.render.screen, pg.Color('white'), vertex, 6)
def translate(self, pos):
self.vertices = self.vertices @ translate(pos)
def scale(self, scale_to):
self.vertices = self.vertices @ scale(scale_to)
def rotate_x(self, angle):
self.vertices = self.vertices @ rotate_x(angle)
def rotate_y(self, angle):
self.vertices = self.vertices @ rotate_y(angle)
def rotate_z(self, angle):
self.vertices = self.vertices @ rotate_z(angle)
class Axes(Object3D):
def __init__(self, render):
super().__init__(render)
self.vertices = np.array([(0, 0, 0, 1), (1, 0, 0, 1), (0, 1, 0, 1), (0, 0, 1, 1)])
self.faces = np.array([(0, 1), (0, 2), (0, 3)])
self.colors = [pg.Color('red'), pg.Color('green'), pg.Color('blue')]
self.color_faces = [(color, face) for color, face in zip(self.colors, self.faces)]
self.draw_vertices = False
self.label = 'XYZ'
camera.py
import pygame as pg
from matrix_functions import *
from pygame.draw import polygon
class Camera:
def __init__(self, render, position):
self.render = render
self.position = np.array([*position, 1.0])
self.forward = np.array([0, 0, 1, 1])
self.up = np.array([0, 1, 0, 1])
self.right = np.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
self.moving_speed = 0.3
self.rotation_speed = 0.015
self.anglePitch = 0
self.angleYaw = 0
self.angleRoll = 0
def control(self):
key = pg.key.get_pressed()
if key[pg.K_a]:
self.position -= self.right * self.moving_speed
if key[pg.K_d]:
self.position += self.right * self.moving_speed
if key[pg.K_w]:
self.position += self.forward * self.moving_speed
if key[pg.K_s]:
self.position -= self.forward * self.moving_speed
if key[pg.K_q]:
self.position += self.up * self.moving_speed
if key[pg.K_e]:
self.position -= self.up * self.moving_speed
if key[pg.K_LEFT]:
self.camera_yaw(-self.rotation_speed)
if key[pg.K_RIGHT]:
self.camera_yaw(self.rotation_speed)
if key[pg.K_UP]:
self.camera_pitch(-self.rotation_speed)
if key[pg.K_DOWN]:
self.camera_pitch(self.rotation_speed)
def camera_yaw(self, angle):
self.angleYaw += angle
def camera_pitch(self, angle):
self.anglePitch += angle
def axiiIdentity(self):
self.forward = np.array([0, 0, 1, 1])
self.up = np.array([0, 1, 0, 1])
self.right = np.array([1, 0, 0, 1])
def camera_update_axii(self):
# rotate = rotate_y(self.angleYaw) @ rotate_x(self.anglePitch)
rotate = rotate_x(self.anglePitch) @ rotate_y(self.angleYaw) # this concatenation gives right visual
self.axiiIdentity()
self.forward = self.forward @ rotate
self.right = self.right @ rotate
self.up = self.up @ rotate
def camera_matrix(self):
self.camera_update_axii()
return self.translate_matrix() @ self.rotate_matrix()
def translate_matrix(self):
x, y, z, w = self.position
return np.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 np.array([
[rx, ux, fx, 0],
[ry, uy, fy, 0],
[rz, uz, fz, 0],
[0, 0, 0, 1]
])
projection.py
import math
import numpy as np
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 = np.array([
[m00, 0, 0, 0],
[0, m11, 0, 0],
[0, 0, m22, 1],
[0, 0, m32, 0]
])
HW, HH = render.H_WIDTH, render.H_HEIGHT
self.to_screen_matrix = np.array([
[HW, 0, 0, 0],
[0, -HH, 0, 0],
[0, 0, 1, 0],
[HW, HH, 0, 1]
])
matrix_functions.py
import math
import numpy as np
def translate(pos):
tx, ty, tz = pos
return np.array([
[1, 0, 0, 0],
[0, 1, 0, 0],
[0, 0, 1, 0],
[tx, ty, tz, 1]
])
def rotate_x(a):
return np.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 rotate_y(a):
return np.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 rotate_z(a):
return np.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 scale(n):
return np.array([
[n, 0, 0, 0],
[0, n, 0, 0],
[0, 0, n, 0],
[0, 0, 0, 1]
])
single_Cube.obg
g cube
# Vertices
v 0.0 0.0 0.0
v 0.0 1.0 0.0
v 1.0 1.0 0.0
v 1.0 0.0 0.0
v 0.0 0.0 1.0
v 0.0 1.0 1.0
v 1.0 1.0 1.0
v 1.0 0.0 1.0
# Normal vectors
# One for each face. Shared by all vertices in that face.
vn 1.0 0.0 0.0
vn -1.0 0.0 0.0
vn 0.0 1.0 0.0
vn 0.0 -1.0 0.0
vn 0.0 0.0 1.0
vn 0.0 0.0 -1.0
# Faces v/vt/vn
# 3-------2
# | - |
# | # | Each face = 2 triangles (ccw)
# | - | = 1-2-3 + 1-3-4
# 4-------1
# Face 1: cghd = cgh + chd
f 3//1 7//1 8//1
f 3//1 8//1 4//1
# Face 2: aefb = aef + afb
f 1//2 5//2 6//2
f 1//2 6//2 2//2
# Face 3: gcbf = gcb + gbf
f 7//3 3//3 2//3
f 7//3 2//3 6//3
# Face 4: dhea = dhe + dea
f 4//4 8//4 5//4
f 4//4 5//4 1//4
# Face 5: hgfe = hgf + hfe
f 8//5 7//5 6//5
f 8//5 6//5 5//5
# Face 6: cdab = cda + cab
f 3//6 4//6 1//6
f 3//6 1//6 2//6