main.py: from camera import Camera from config import * from model import * class App: def __init__(self): pygame.init() self.screen_width = 900 self.screen_height = 500 self.create_window() self.shader = load_shader('default') self.depth_shader = load_shader('depth') self.camera = Camera(position = (0, 10, 0), fov = 90, aspect_ratio = pygame.display.get_surface().get_size()[0] / pygame.display.get_surface().get_size()[1], near = 0.1, far = 50) self.vao = get_vao() self.vbo = get_vbo() self.instance_vbo = get_vbo() self.basic_transformations = {'position': (0, 0, 0), 'rotation': (0, 0, 0), 'scale': (1, 1, 1)} self.old_tick = 0 self.current_tick = 0 self.delta_time = 0 self.clock = pygame.time.Clock() self.set_attribs() self.init_objects() self.set_instance_data() def create_window(self): pygame.display.set_mode((self.screen_width, self.screen_height), pygame.OPENGL | pygame.DOUBLEBUF | pygame.RESIZABLE) pygame.display.set_caption('PyOpenGL 3D Engine') glClearColor(0.15, 0.15, 0.15, 1) def set_attribs(self): glEnable(GL_DEPTH_TEST) def init_objects(self): self.cube = Cube('textures/cubes/white_cube.png', self.shader, self.vao, self.vbo, self.instance_vbo, self.camera) random_color = random.choice(('red', 'blue', 'green', 'yellow')) self.cube_2 = Cube(f'textures/cubes/{random_color}_cube.png', self.shader, self.vao, self.vbo, self.instance_vbo, self.camera) self.lights = [] self.sun = Sun() self.shadow_map = ShadowMap(1, self.sun, self.depth_shader) glUniform1i(glGetUniformLocation(self.shader, 'ShadowMap'), self.shadow_map.unit) def set_instance_data(self): platform_size = (32, 32) for x in range(-platform_size[0] // 2, platform_size[0] // 2): for z in range(-platform_size[1] // 2, platform_size[1] // 2): self.cube.instance_positions.append((x, 0, z)) self.cube_2.instance_positions.append((0, 2, 0)) def draw(self): self.shadow_map.draw() glUseProgram(self.shader) self.shadow_map.use() glUniformMatrix4fv(glGetUniformLocation(self.shader, 'lightSpaceMatrix'), 1, GL_FALSE, glm.value_ptr(self.shadow_map.lightSpaceMatrix)) glViewport(0, 0, self.screen_width, self.screen_height) self.camera.aspect_ratio = self.screen_width / self.screen_height self.camera.projection_matrix = self.camera.calculate_projection_matrix() self.cube.draw(ShinyLevel = 0.0, transformations = self.basic_transformations, lights = self.lights, sun = self.sun) self.cube_2.draw(ShinyLevel = 0.5, transformations = self.basic_transformations, lights = self.lights, sun = self.sun) def game_loop(self): while True: for event in pygame.event.get(): if event.type == pygame.QUIT or (event.type == pygame.KEYDOWN and event.key == pygame.K_ESCAPE): self.quit() if event.type == pygame.KEYDOWN: if event.key == pygame.K_F11: pygame.display.toggle_fullscreen() if event.type == pygame.VIDEORESIZE: self.screen_width, self.screen_height = event.size glViewport(0, 0, self.screen_width, self.screen_height) self.camera.aspect_ratio = self.screen_width / self.screen_height self.camera.projection_matrix = self.camera.calculate_projection_matrix() pygame.event.set_grab(True) pygame.mouse.set_visible(False) self.camera.update() glUseProgram(self.shader) self.view = self.camera.get_view_matrix() glUniformMatrix4fv(glGetUniformLocation(self.shader, 'view'), 1, GL_FALSE, glm.value_ptr(self.view)) self.projection = self.camera.get_projection_matrix() glUniformMatrix4fv(glGetUniformLocation(self.shader, 'projection'), 1, GL_FALSE, glm.value_ptr(self.projection)) glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) self.draw() pygame.display.flip() def run(self): self.game_loop() def quit(self): self.shadow_map.destroy() self.cube.destroy() self.cube_2.destroy() glDeleteProgram(self.shader) pygame.quit() exit() if __name__ == '__main__': app = App() app.run() model.py: from config import * class Base: def __init__(self, texture_path, shader, vao, vbo, instance_vbo, camera): self.shader = shader self.vao = vao self.vbo = vbo self.instance_vbo = instance_vbo self.camera = camera if texture_path is not None: self.texture = Texture(texture_path, 0) else: self.texture = None self.render_mode = GL_TRIANGLES glUseProgram(self.shader) self.set_vertices() self.set_data() self.set_attribs() def set_vertices(self): self.vertices = () def set_data(self): self.vertices = np.array(self.vertices, dtype=np.float32) self.instance_positions = [] self.attribs_count = 8 try: self.vertex_count = int(len(self.vertices) / self.attribs_count) except ZeroDivisionError: self.vertex_count = 0 glBindBuffer(GL_ARRAY_BUFFER, self.vbo) glBufferData(GL_ARRAY_BUFFER, self.vertices.nbytes, self.vertices, GL_STATIC_DRAW) glUniform1i(glGetUniformLocation(self.shader, 'Image'), self.texture.unit) def set_instance_data(self): self.instance_positions = np.array(self.instance_positions, dtype=np.float32) glBindBuffer(GL_ARRAY_BUFFER, self.instance_vbo) glBufferData(GL_ARRAY_BUFFER, self.instance_positions.nbytes, self.instance_positions, GL_STATIC_DRAW) glEnableVertexAttribArray(3) glVertexAttribPointer(3, 3, GL_FLOAT, GL_FALSE, 0, ctypes.c_void_p(0)) glVertexAttribDivisor(3, 1) def set_attribs(self): glEnableVertexAttribArray(0) glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, self.attribs_count * 4, ctypes.c_void_p(0)) glEnableVertexAttribArray(1) glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, self.attribs_count * 4, ctypes.c_void_p(12)) glEnableVertexAttribArray(2) glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, self.attribs_count * 4, ctypes.c_void_p(20)) def get_model_matrix(self, position, rotation, scale): model = glm.mat4(1.0) model = glm.translate(model, glm.vec3(position)) model = glm.rotate(model, glm.radians(rotation[0]), glm.vec3(1, 0, 0)) model = glm.rotate(model, glm.radians(rotation[1]), glm.vec3(0, 1, 0)) model = glm.rotate(model, glm.radians(rotation[2]), glm.vec3(0, 0, 1)) model = glm.scale(model, glm.vec3(scale)) return model def update(self): pass def draw(self, ShinyLevel, transformations, lights, sun): glUseProgram(self.shader) glBindVertexArray(self.vao) self.set_instance_data() self.update() glUniform1f(glGetUniformLocation(self.shader, 'ShinyLevel'), ShinyLevel) glUniform1i(glGetUniformLocation(self.shader, 'IsF1Clicked'), pygame.key.get_pressed()[pygame.K_F1]) lights_num = len(lights) if self.texture is not None: self.texture.use() self.model = self.get_model_matrix(transformations['position'], transformations['rotation'], transformations['scale']) glUniformMatrix4fv(glGetUniformLocation(self.shader, 'model'), 1, GL_FALSE, glm.value_ptr(self.model)) for i, light in enumerate(lights): glUniform3fv(glGetUniformLocation(self.shader, f'lights[{i}].position'), 1, glm.value_ptr(light.position)) glUniform3fv(glGetUniformLocation(self.shader, f'lights[{i}].color'), 1, glm.value_ptr(light.color)) glUniform1f(glGetUniformLocation(self.shader, f'lights[{i}].strength'), light.strength) glUniform3fv(glGetUniformLocation(self.shader, 'sun.position'), 1, glm.value_ptr(sun.position)) glUniform3fv(glGetUniformLocation(self.shader, 'sun.color'), 1, glm.value_ptr(sun.color)) glUniform3fv(glGetUniformLocation(self.shader, 'sun.direction'), 1, glm.value_ptr(sun.direction)) glUniform3fv(glGetUniformLocation(self.shader, 'viewPos'), 1, glm.value_ptr(self.camera.position)) glUniform1i(glGetUniformLocation(self.shader, 'numLights'), lights_num) glDrawArraysInstanced(self.render_mode, 0, self.vertex_count, len(self.instance_positions)) def destroy(self): self.texture.destroy() glDeleteVertexArrays(1, [self.vao]) glDeleteBuffers(1, [self.vbo]) glDeleteBuffers(1, [self.instance_vbo]) class Cube(Base): def __init__(self, texture_path, shader, vao, vbo, instance_vbo, camera): super().__init__(texture_path, shader, vao, vbo, instance_vbo, camera) def set_vertices(self): self.vertices = ( # Front face -0.5, -0.5, -0.5, 0, 0, 0, 0, -1, 0.5, -0.5, -0.5, 1, 0, 0, 0, -1, 0.5, 0.5, -0.5, 1, 1, 0, 0, -1, -0.5, -0.5, -0.5, 0, 0, 0, 0, -1, 0.5, 0.5, -0.5, 1, 1, 0, 0, -1, -0.5, 0.5, -0.5, 0, 1, 0, 0, -1, # Back face -0.5, -0.5, 0.5, 0, 0, 0, 0, 1, 0.5, -0.5, 0.5, 1, 0, 0, 0, 1, 0.5, 0.5, 0.5, 1, 1, 0, 0, 1, -0.5, -0.5, 0.5, 0, 0, 0, 0, 1, 0.5, 0.5, 0.5, 1, 1, 0, 0, 1, -0.5, 0.5, 0.5, 0, 1, 0, 0, 1, # Left face -0.5, -0.5, -0.5, 0, 0, -1, 0, 0, -0.5, 0.5, -0.5, 1, 0, -1, 0, 0, -0.5, 0.5, 0.5, 1, 1, -1, 0, 0, -0.5, -0.5, -0.5, 0, 0, -1, 0, 0, -0.5, 0.5, 0.5, 1, 1, -1, 0, 0, -0.5, -0.5, 0.5, 0, 1, -1, 0, 0, # Right face 0.5, -0.5, -0.5, 0, 0, 1, 0, 0, 0.5, 0.5, -0.5, 1, 0, 1, 0, 0, 0.5, 0.5, 0.5, 1, 1, 1, 0, 0, 0.5, -0.5, -0.5, 0, 0, 1, 0, 0, 0.5, 0.5, 0.5, 1, 1, 1, 0, 0, 0.5, -0.5, 0.5, 0, 1, 1, 0, 0, # Top face -0.5, 0.5, -0.5, 0, 0, 0, 1, 0, 0.5, 0.5, -0.5, 1, 0, 0, 1, 0, 0.5, 0.5, 0.5, 1, 1, 0, 1, 0, -0.5, 0.5, -0.5, 0, 0, 0, 1, 0, 0.5, 0.5, 0.5, 1, 1, 0, 1, 0, -0.5, 0.5, 0.5, 0, 1, 0, 1, 0, # Bottom face -0.5, -0.5, -0.5, 0, 0, 0, -1, 0, 0.5, -0.5, -0.5, 1, 0, 0, -1, 0, 0.5, -0.5, 0.5, 1, 1, 0, -1, 0, -0.5, -0.5, -0.5, 0, 0, 0, -1, 0, 0.5, -0.5, 0.5, 1, 1, 0, -1, 0, -0.5, -0.5, 0.5, 0, 1, 0, -1, 0, ) class Light: def __init__(self, position = (0, 5, 0), color = (1, 1, 1), strength = 1): self.position = glm.vec3(position) self.color = glm.vec3(color) self.strength = strength def update_position(self, position): self.position = glm.vec3(position) def update_color(self, color): self.color = glm.vec3(color) def update_strength(self, strength): self.strength = strength class Sun: def __init__(self, color = (1, 1, 1), direction = (-1, -1.5, -1)): self.position = glm.vec3((0, 0, 0)) self.color = glm.vec3(color) self.direction = glm.vec3(direction) def update_position(self, position): self.position = glm.vec3(position) def update_color(self, color): self.color = glm.vec3(color) def update_direction(self, direction): self.direction = direction camera.py: from config import * class Camera: def __init__(self, position, fov, aspect_ratio, near, far): self.position = glm.vec3(position) self.up = glm.vec3(0, 1, 0) self.forward = glm.vec3(0, 0, 1) self.right = glm.vec3(1, 0, 0) self.fov = glm.radians(fov / 2) self.aspect_ratio = aspect_ratio self.near = near self.far = far self.view_matrix = self.calculate_view_matrix() self.projection_matrix = self.calculate_projection_matrix() self.yaw = 90 self.pitch = 0 self.speed = 0.0025 self.sens = 2 def update_vectors(self): self.forward.x = glm.cos(self.yaw) * glm.cos(self.pitch) self.forward.y = glm.sin(self.pitch) self.forward.z = glm.sin(self.yaw) * glm.cos(self.pitch) self.forward = glm.normalize(self.forward) self.right = glm.normalize(glm.cross(self.forward, self.up)) def update(self): self.update_vectors() self.player_movement() self.mouse_movement() self.view_matrix = self.calculate_view_matrix() def calculate_view_matrix(self): return glm.lookAt(self.position, self.position + self.forward, self.up) def calculate_projection_matrix(self): return glm.perspective(self.fov, self.aspect_ratio, self.near, self.far) def get_view_matrix(self): return self.view_matrix def get_projection_matrix(self): return self.projection_matrix def player_movement(self): keys = pygame.key.get_pressed() if keys[pygame.K_LSHIFT]: speed = self.speed * 1.5 else: speed = self.speed if keys[pygame.K_w]: self.position += self.forward * speed if keys[pygame.K_s]: self.position -= self.forward * speed if keys[pygame.K_a]: self.position -= self.right * speed if keys[pygame.K_d]: self.position += self.right * speed if keys[pygame.K_q]: self.position += self.up * speed if keys[pygame.K_e]: self.position -= self.up * speed def mouse_movement(self): x, y = pygame.mouse.get_rel() self.yaw += (x / 1000) * self.sens self.pitch -= (y / 1000) * self.sens config.py: import pygame import glm import numpy as np import ctypes import glm import random from OpenGL.GL import * from OpenGL.GL.shaders import compileShader, compileProgram from sys import exit def load_shader(shader_name): with open(f'shaders/{shader_name}.vert') as f: vertex_src = f.readlines() with open(f'shaders/{shader_name}.frag') as f: fragment_src = f.readlines() shader = compileProgram(compileShader(vertex_src, GL_VERTEX_SHADER), compileShader(fragment_src, GL_FRAGMENT_SHADER)) glUseProgram(shader) return shader def get_vao(): vao = glGenVertexArrays(1) glBindVertexArray(vao) return vao def get_vbo(): vbo = glGenBuffers(1) glBindBuffer(GL_ARRAY_BUFFER, vbo) return vbo def get_fbo(): fbo = glGenFramebuffers(1) glBindFramebuffer(GL_FRAMEBUFFER, fbo) return fbo class Texture: def __init__(self, path, unit): self.unit = unit texture = pygame.image.load(path).convert_alpha() width, height = texture.get_rect().size texture_data = pygame.image.tostring(texture, 'RGBA', True) self.texture_id = glGenTextures(1) glBindTexture(GL_TEXTURE_2D, self.texture_id) glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, texture_data) glGenerateMipmap(GL_TEXTURE_2D) glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT) glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT) glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST) glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR) def use(self): exec(f'glActiveTexture(GL_TEXTURE{self.unit})') glBindTexture(GL_TEXTURE_2D, self.texture_id) def destroy(self): glDeleteTextures(1, [self.texture_id,]) class ShadowMap: def __init__(self, unit, light, depth_shader): self.fbo = get_fbo() self.unit = unit self.resolution = (1024, 1024) self.BaseLight = light self.depth_shader = depth_shader self.lightSpaceMatrix = glm.mat4(1.0) self.border_color = glm.vec4(1.0) self.depth_map = glGenTextures(1) glBindTexture(GL_TEXTURE_2D, self.depth_map) glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, *self.resolution, 0, GL_DEPTH_COMPONENT, GL_FLOAT, None) glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST) glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST) glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER) glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER) glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, glm.value_ptr(self.border_color)) glBindFramebuffer(GL_FRAMEBUFFER, self.fbo) glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, self.depth_map, 0) glDrawBuffer(GL_NONE) glReadBuffer(GL_NONE) glBindFramebuffer(GL_FRAMEBUFFER, 0) def draw(self): glUseProgram(self.depth_shader) glViewport(0, 0, *self.resolution) glBindFramebuffer(GL_FRAMEBUFFER, self.fbo) glClear(GL_DEPTH_BUFFER_BIT) self.position = 10 * self.BaseLight.direction self.center = glm.vec3(0.0, 0.0, 0.0) self.up = glm.vec3(0.0, 0.0, 1.0) self.projection = glm.ortho(-10, 10, -10, 10, 1.0, 20.0) self.view = glm.lookAt(self.position, self.center, self.up) self.lightSpaceMatrix = self.projection * self.view glUniformMatrix4fv(glGetUniformLocation(self.depth_shader, 'lightSpaceMatrix'), 1, GL_FALSE, glm.value_ptr(self.lightSpaceMatrix)) glBindFramebuffer(GL_FRAMEBUFFER, 0) def use(self): exec(f'glActiveTexture(GL_TEXTURE{self.unit})') glBindTexture(GL_TEXTURE_2D, self.depth_map) def destroy(self): glDeleteTextures(1, [self.depth_map,]) glDeleteFramebuffers(1, [self.fbo,]) default.vert: #version 330 core layout(location = 0) in vec3 vertexPos; layout(location = 1) in vec2 vertexTexCoord; layout(location = 2) in vec3 vertexNormal; layout(location = 3) in vec3 instancePos; out vec3 position; out vec2 texcoord; out vec3 normal; out vec4 PositionLightSpace; uniform mat4 projection; uniform mat4 view; uniform mat4 model; uniform mat4 lightSpaceMatrix; void main() { vec4 worldPosition = model * vec4(vertexPos, 1.0) + vec4(instancePos, 0.0); gl_Position = projection * view * worldPosition; position = worldPosition.xyz; texcoord = vertexTexCoord; normal = normalize(mat3(model) * vertexNormal); PositionLightSpace = lightSpaceMatrix * vec4(position, 1.0); int renderMode = 0; } default.frag: #version 330 core in vec3 position; in vec2 texcoord; in vec3 normal; in vec4 PositionLightSpace; struct Light { vec3 position; vec3 color; vec3 strength; }; struct DirectionalLight { vec3 color; vec3 direction; }; out vec4 FragColor; vec3 calculatePointLight(Light light, vec3 Position, vec3 Normal); vec3 calculateDirectionalLight(DirectionalLight Sun, vec3 Position, vec3 Normal); float calculateShadow(vec4 PositionLightSpace); #define MAX_LIGHTS 100 uniform Light lights[MAX_LIGHTS]; uniform DirectionalLight sun; uniform int numLights; uniform vec3 viewPos; uniform sampler2D Image; uniform sampler2D shadowMap; uniform float ShinyLevel; uniform bool IsF1Clicked; void main() { vec3 temp = vec3(0.0); vec3 lightDir = -sun.direction; float shadow = calculateShadow(PositionLightSpace); temp += (1.0 - shadow) * calculateDirectionalLight(sun, position, normal); for(int i = 0; i < numLights; i++) { temp += calculatePointLight(lights, position, normal); } int renderMode = 0; if (IsF1Clicked) { renderMode = 1; } if (renderMode == 0) { FragColor = vec4(temp, 1.0); } if (renderMode == 1) { FragColor = vec4(vec3(shadow), 1.0); } } vec3 calculatePointLight(Light light, vec3 Position, vec3 Normal) { vec3 result = vec3(0.0); vec3 baseTexture = texture(Image, texcoord).rgb; vec3 lightDir = light.position - Position; float distance = length(lightDir); lightDir = normalize(lightDir); vec3 viewDir = normalize(viewPos - Position); vec3 reflectDir = reflect(-lightDir, Normal); // ambient result += 0.1 * baseTexture; // diffuse float diff = max(0.0, dot(Normal, lightDir)); result += light.color * light.strength * diff * baseTexture; // specular float spec = pow(max(dot(viewDir, reflectDir), 0.0), 32); result += light.color * light.strength * spec; return result; } vec3 calculateDirectionalLight(DirectionalLight Sun, vec3 Position, vec3 Normal) { vec3 result = vec3(0.0); vec3 baseTexture = texture(Image, texcoord).rgb; vec3 normal = normalize(Normal); vec3 lightDir = normalize(-Sun.direction); lightDir = normalize(lightDir); vec3 viewDir = normalize(viewPos - Position); vec3 reflectDir = reflect(-lightDir, normal); // ambient result += 0.2 * baseTexture; // diffuse float diffuse = max(0.0, dot(normal, lightDir)); result += Sun.color * diffuse * baseTexture; // specular float specular = pow(max(dot(viewDir, reflectDir), 0.0), 32); result += ShinyLevel * Sun.color * specular * baseTexture; return result; } float calculateShadow(vec4 fragPosLightSpace) { vec3 projCoords = fragPosLightSpace.xyz / fragPosLightSpace.w; projCoords = projCoords * 0.5 + 0.5; float d1 = texture(shadowMap, projCoords.xy).r; float d2 = projCoords.z; vec3 normal = normal; vec3 lightDir = normalize(-10 * sun.direction - position); float bias = max(0.05 * (1.0 - dot(normal, lightDir)), 0.005); float shadow = 0.0; vec2 texelsize = 1.0 / textureSize(shadowMap, 0); for (int x = -1; x <= 1; ++x) { for (int y = -1; y <= 1; ++y) { float pcfDepth = texture(shadowMap, projCoords.xy + vec2(x, y) * texelsize).r; shadow += d2 - bias > pcfDepth ? 1.0 : 0.0; } } shadow /= 9.0; if (projCoords.z > 1.0) { shadow = 0.0; } return shadow; } depth.vert: #version 330 core layout(location = 0) in vec3 vertexPos; uniform mat4 lightSpaceMatrix; uniform mat4 model; void main() { gl_Position = lightSpaceMatrix * model * vec4(vertexPos, 1.0); } depth.frag: #version 330 core void main() { gl_FragDepth = gl_FragCoord.z; } That is my program. non-existent shapes generated by shadow map, screenshot and that's my screenshot of this error. I tried to make shadows in pyopengl, but the shadow map renders non-existent shapes. The program does not return any error. What am I doing wrong? Can somebody help me? Continue reading...