вертексный шейдер:
Код: Выделить всё
#version 330 core
// Uniforms for camera transformations
uniform mat4 projection;
uniform mat4 view;
// Vertex attributes
layout(location = 0) in vec3 position; // Vertex position
layout(location = 1) in vec3 normal; // Vertex normal
layout(location = 2) in vec4 instancePosition; // Instance position (pass this in the buffer)
// Function to reconstruct the transformation matrix from the instance data
mat4 reconstructMatrix(vec4 instancePosition) {
mat4 model = mat4(1.0);
model[3] = instancePosition; // Set translation using instance position
return model;
}
void main() {
// Reconstruct the model matrix for this instance
mat4 model = reconstructMatrix(instancePosition);
// Calculate final position
gl_Position = projection * view * model * vec4(position, 1.0);
}
Код: Выделить всё
#version 330 core
out vec4 fragColor;
void main() {
fragColor = vec4(0.0, 1.0, 0.0, 1.0); // Green color
}
Код: Выделить всё
from OpenGL.GL import *
from OpenGL.GLUT import *
SHADER_CACHE = {}
def load_shader(filename, shader_type):
"""Load and compile shader from file."""
with open(filename, 'r') as file:
shader_source = file.read()
shader = glCreateShader(shader_type)
glShaderSource(shader, shader_source)
glCompileShader(shader)
# Error checking after compilation
if glGetShaderiv(shader, GL_COMPILE_STATUS) != GL_TRUE:
error_message = glGetShaderInfoLog(shader)
raise RuntimeError(f"Shader compilation failed: {error_message}")
return shader
def create_shader_program(vertex_shader_file, fragment_shader_file):
"""Create shader program and cache it."""
cache_key = (vertex_shader_file, fragment_shader_file)
if cache_key in SHADER_CACHE:
return SHADER_CACHE[cache_key]
vertex_shader = load_shader(vertex_shader_file, GL_VERTEX_SHADER)
fragment_shader = load_shader(fragment_shader_file, GL_FRAGMENT_SHADER)
program = glCreateProgram()
glAttachShader(program, vertex_shader)
glAttachShader(program, fragment_shader)
glLinkProgram(program)
# Error checking after linking
if glGetProgramiv(program, GL_LINK_STATUS) != GL_TRUE:
error_message = glGetProgramInfoLog(program)
raise RuntimeError(f"Program linking failed: {error_message}")
glDeleteShader(vertex_shader)
glDeleteShader(fragment_shader)
SHADER_CACHE[cache_key] = program
return program
Код: Выделить всё
import pygame
from OpenGL.GL import *
from OpenGL.GLUT import *
import numpy as np
import chunk_system
import camera_system
import pstats
from pyrr import Vector3
import input_system
import cProfile
import ctypes
import load_shaders
from math import *
last_projection_matrix = None
last_view_matrix = None
MATRIX_UPDATE_THRESHOLD = 1e-6
def set_projection_matrix(matrix):
global last_projection_matrix
if last_projection_matrix is None or not np.allclose(last_projection_matrix, matrix, atol=MATRIX_UPDATE_THRESHOLD):
glUniformMatrix4fv(glGetUniformLocation(shader_program, "projection"), 1, GL_TRUE, matrix)
last_projection_matrix = matrix.copy()
def set_view_matrix(matrix):
global last_view_matrix
if last_view_matrix is None or not np.allclose(last_view_matrix, matrix, atol=MATRIX_UPDATE_THRESHOLD):
glUniformMatrix4fv(glGetUniformLocation(shader_program, "view"), 1, GL_TRUE, matrix)
last_view_matrix = matrix.copy()
def set_shader_uniforms(shader_program, camera):
"""Sets shader uniforms for rendering."""
projection = camera.get_projection_matrix()
view_matrix = camera.get_view_matrix()
set_projection_matrix(projection)
set_view_matrix(view_matrix)
# Set model matrix
model = np.identity(4, dtype=np.float32)
glUniformMatrix4fv(glGetUniformLocation(shader_program, "model"), 1, GL_TRUE, model)
def matrices_are_different(mat1, mat2, threshold=1e-5):
return np.linalg.norm(mat1 - mat2) > threshold
def initialize_pygame(display_size):
"""Initializes Pygame and sets up the OpenGL context."""
pygame.init()
screen = pygame.display.set_mode(display_size, pygame.OPENGL | pygame.DOUBLEBUF)
pygame.mouse.set_visible(False)
return screen
def initialize_opengl(display_size):
"""Configures OpenGL settings."""
glViewport(0, 0, display_size[0], display_size[1])
glClearColor(0, 0, 0, 1)
glEnable(GL_DEPTH_TEST)
glDepthFunc(GL_LESS)
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE)
CHUNK_SIZE = 4
VIEW_DISTANCE = 2
class ChunkRenderer:
def __init__(self):
self.vao = glGenVertexArrays(1)
self.vbo = glGenBuffers(1)
self.ebo = glGenBuffers(1)
self.instance_vbo = glGenBuffers(1)
# Preallocate data buffers with realistic sizes based on typical usage
self.vertex_data = np.zeros((87380, 6), dtype=np.float32)
self.index_data = np.zeros(43690, dtype=np.uint32)
self.instance_data = np.zeros((1024, 4), dtype=np.float32)
self.vertex_count = 0
self.index_count = 0
self.instance_count = 0
# Persistent mapping pointers
self.vertex_ptr = None
self.index_ptr = None
self.instance_ptr = None
# Hashes to track data changes
self.vertex_data_hash = None
self.index_data_hash = None
self.instance_data_hash = None
def setup_buffers(self):
MAX_VERTEX_BUFFER_SIZE = 4 * 1024 * 1024 # 4 MB
MAX_INDEX_BUFFER_SIZE = 2 * 1024 * 1024 # 2 MB
MAX_INSTANCE_BUFFER_SIZE = 2 * 1024 * 1024 # 2 MB
glBindVertexArray(self.vao)
version = glGetString(GL_VERSION).decode('utf-8')
major, minor = map(int, version.split(' ')[0].split('.')[:2])
persistent_mapping_supported = (major > 4) or (major == 4 and minor >= 4)
buffer_flags = GL_DYNAMIC_STORAGE_BIT
if persistent_mapping_supported:
buffer_flags |= GL_MAP_WRITE_BIT | GL_MAP_PERSISTENT_BIT | GL_MAP_COHERENT_BIT
def allocate_buffer(buffer_type, buffer, size, flags):
glBindBuffer(buffer_type, buffer)
glBufferStorage(buffer_type, size, None, flags)
buffer_size = glGetBufferParameteriv(buffer_type, GL_BUFFER_SIZE)
if buffer_size == 0:
raise RuntimeError(f"Buffer allocation failed for {buffer_type} with size {size}.")
# Allocate buffers
allocate_buffer(GL_ARRAY_BUFFER, self.vbo, MAX_VERTEX_BUFFER_SIZE, buffer_flags)
allocate_buffer(GL_ELEMENT_ARRAY_BUFFER, self.ebo, MAX_INDEX_BUFFER_SIZE, buffer_flags)
allocate_buffer(GL_ARRAY_BUFFER, self.instance_vbo, MAX_INSTANCE_BUFFER_SIZE, buffer_flags)
# Set up vertex attributes
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * 4, ctypes.c_void_p(0))
glEnableVertexAttribArray(0)
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * 4, ctypes.c_void_p(3 * 4))
glEnableVertexAttribArray(1)
# Instance data attributes
for i in range(4):
glVertexAttribPointer(2 + i, 4, GL_FLOAT, GL_FALSE, 16 * 4, ctypes.c_void_p(i * 4 * 4))
glEnableVertexAttribArray(2 + i)
glVertexAttribDivisor(2 + i, 1)
glBindVertexArray(0)
print("Buffers setup successfully.")
def update_buffers(self, vertices, indices, instance_data):
def update_buffer(buffer_type, data, data_hash, ptr):
new_hash = hash(data.tobytes())
if data_hash != new_hash:
print(f"Updating {buffer_type} data")
if ptr is not None:
ctypes.memmove(ptr, data.ctypes.data_as(ctypes.POINTER(ctypes.c_float)), data.nbytes)
return new_hash, True
return data_hash, False
# Update vertex, index, and instance buffers separately
glBindBuffer(GL_ARRAY_BUFFER, self.vbo)
self.vertex_data_hash, updated_vertices = update_buffer(GL_ARRAY_BUFFER, vertices, self.vertex_data_hash,
self.vertex_ptr)
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, self.ebo)
self.index_data_hash, updated_indices = update_buffer(GL_ELEMENT_ARRAY_BUFFER, indices, self.index_data_hash,
self.index_ptr)
glBindBuffer(GL_ARRAY_BUFFER, self.instance_vbo)
self.instance_data_hash, updated_instances = update_buffer(GL_ARRAY_BUFFER, instance_data,
self.instance_data_hash, self.instance_ptr)
# Update only if any of the buffers have changed
if updated_vertices or updated_indices or updated_instances:
print("Buffers updated successfully.")
def draw_chunks(self):
print(f"VAO: {self.vao}, VBO: {self.vbo}, EBO: {self.ebo}, Instance VBO: {self.instance_vbo}")
glBindVertexArray(self.vao)
glDrawElementsInstanced(GL_TRIANGLES, self.index_count, GL_UNSIGNED_INT, None, self.instance_count)
glBindVertexArray(0)
def delete_buffers(self):
if self.vertex_ptr:
glUnmapBuffer(GL_ARRAY_BUFFER)
if self.index_ptr:
glUnmapBuffer(GL_ELEMENT_ARRAY_BUFFER)
glDeleteBuffers(3, [self.vbo, self.ebo, self.instance_vbo])
glDeleteVertexArrays(1, [self.vao])
self.vertex_ptr = None
self.index_ptr = None
self.instance_ptr = None
# Define voxel face offsets and directions
face_offsets = np.array([
[0, 0, 0], [1, 0, 0], [1, 1, 0], [0, 1, 0], # Front face (0)
[0, 0, 1], [1, 0, 1], [1, 1, 1], [0, 1, 1] # Back face (1)
], dtype=np.int32)
# Normals for each of the 6 cube faces
face_normals = np.array([
[0, 0, -1], # Front
[0, 0, 1], # Back
[0, 1, 0], # Top
[0, -1, 0], # Bottom
[-1, 0, 0], # Left
[1, 0, 0] # Right
], dtype=np.int32)
# Combining positions and normals for cube faces
positions = np.concatenate([face_offsets[indices] for indices in [
[0, 1, 2, 3], # Front face
[4, 5, 6, 7], # Back face
[3, 2, 6, 7], # Top face
[0, 1, 5, 4], # Bottom face
[0, 3, 7, 4], # Left face
[1, 2, 6, 5] # Right face
]])
# Repeating the normals for each of the 4 vertices of a cube face
normals = np.repeat(face_normals, 4, axis=0)
# Now combine position and normal into the final vertices
cube_vertices = np.hstack((positions, normals))
# Constants
NEIGHBORS = [(0, 0, -1), (0, 0, 1), (0, 1, 0), (0, -1, 0), (-1, 0, 0), (1, 0, 0)]
# Precompute face normals
FACE_NORMALS = np.array(NEIGHBORS, dtype=np.float32)
# Make sure to clear memory after use:
def clear_memory():
global positions, normals, FACE_NORMALS
print("Debug: Clearing memory for positions, normals, and FACE_NORMALS.")
del positions
del normals
del FACE_NORMALS
# Efficient bitmask-based face exposure check
def get_exposed_faces(chunk_data, x, y, z):
neighbor_offsets = np.array(NEIGHBORS, dtype=np.int32)
neighbor_coords = np.array([x, y, z]) + neighbor_offsets
valid_neighbors = np.all((neighbor_coords >= 0) & (neighbor_coords < chunk_data.shape), axis=1)
neighbors = np.zeros(len(NEIGHBORS), dtype=bool)
neighbors[valid_neighbors] = chunk_data[tuple(neighbor_coords[valid_neighbors].T)] == 0
face_bitmask = np.packbits(neighbors.astype(np.uint8))[0]
return face_bitmask
# Precompute chunk transformations
def precompute_chunk_transform(chunk_pos, chunk_size):
print(f"Debug: Precomputing chunk transform for position {chunk_pos} and size {chunk_size}")
chunk_transform = np.eye(4, dtype=np.float32)
chunk_transform[:3, 3] = np.array(chunk_pos) * chunk_size # Position adjustment
return chunk_transform
def add_vertices_bulk(vertices_block, vertices, vertex_map, vertex_count, tol=1e-6):
vertex_positions = np.round(vertices_block[:, :3], decimals=6) # Round positions to prevent floating-point errors
unique_vertices, indices = np.unique(vertex_positions, axis=0, return_inverse=True)
# Iterate through unique vertices and add them if not already in the map
for i, pos in enumerate(unique_vertices):
pos_tuple = tuple(pos) # Convert to tuple for immutability and fast lookup in vertex_map
if pos_tuple not in vertex_map:
vertex_map[pos_tuple] = vertex_count
vertices[vertex_count] = vertices_block[indices == i][0] # Add the first matching vertex
vertex_count += 1
# Map the vertices' positions to their indices
vertex_indices = np.array([vertex_map[tuple(pos)] for pos in vertex_positions], dtype=np.uint32)
return vertex_indices, vertex_count
# Generate quad indices
def generate_quad_indices(vertex_ids):
# Ensure there are at least 4 vertices, and only create quads if there are enough vertices
if len(vertex_ids) < 4:
raise ValueError("Insufficient vertex count for generating quad indices.")
quads = np.array([
[vertex_ids[0], vertex_ids[1], vertex_ids[2]],
[vertex_ids[0], vertex_ids[2], vertex_ids[3]]
], dtype=np.uint32)
return quads.flatten()
# Example: Precompute exposure mask more efficiently
def compute_exposure_mask(chunk_data):
padded_chunk = np.pad(chunk_data, pad_width=1, mode='constant', constant_values=0)
exposure_mask = np.zeros((*chunk_data.shape, len(NEIGHBORS)), dtype=bool)
for i, (dx, dy, dz) in enumerate(NEIGHBORS):
exposure_mask[..., i] = padded_chunk[1 + dx:1 + dx + chunk_data.shape[0],
1 + dy:1 + dy + chunk_data.shape[1],
1 + dz:1 + dz + chunk_data.shape[2]] == 0
return exposure_mask
# Global counters to accumulate counts across all chunks
total_vertex_count = 0
total_indices_count = 0
total_quad_count = 0
# Reuse memory for `quad_vertices` and `vertex_map`:
def greedy_mesh_faces(chunk_data, chunk_pos, chunk_size, vertices, indices, index_pointer):
global total_vertex_count, total_indices_count, total_quad_count
exposure_mask = compute_exposure_mask(chunk_data)
vertex_map = {}
quad_vertices = np.zeros((4, 6),
dtype=np.float32) # Prepare space for 4 vertices with 6 values (x, y, z, u, v, face_index)
for face_index in range(6):
direction_bit = 1
Подробнее здесь: [url]https://stackoverflow.com/questions/79337546/problem-with-nothing-being-rendered-in-pygame-and-opengl[/url]
Мобильная версия