mirror of
https://github.com/Khendi1/PVS.git
synced 2026-02-12 13:40:40 +01:00
226 lines
7.4 KiB
Python
226 lines
7.4 KiB
Python
import cv2
|
|
import numpy as np
|
|
import glfw
|
|
from OpenGL.GL import *
|
|
from OpenGL.GL.shaders import compileShader, compileProgram
|
|
|
|
# --- GLSL Shader Source Code (Inline) ---
|
|
# The shaders remain the same as the rendering logic is OpenGL standard.
|
|
|
|
VERTEX_SHADER_SOURCE = """
|
|
#version 330 core
|
|
layout (location = 0) in vec3 aPos;
|
|
layout (location = 1) in vec2 aTexCoord;
|
|
|
|
out vec2 TexCoord;
|
|
|
|
void main()
|
|
{
|
|
gl_Position = vec4(aPos, 1.0);
|
|
TexCoord = aTexCoord;
|
|
}
|
|
"""
|
|
|
|
# Fragment Shader: Applies the simple 'Cyberpunk Grayscale' effect
|
|
FRAGMENT_SHADER_SOURCE = """
|
|
#version 330 core
|
|
in vec2 TexCoord;
|
|
out vec4 FragColor;
|
|
|
|
uniform sampler2D ourTexture;
|
|
uniform float time; // Time uniform for animation
|
|
|
|
void main()
|
|
{
|
|
vec4 texColor = texture(ourTexture, TexCoord);
|
|
float gray = dot(texColor.rgb, vec3(0.2126, 0.7152, 0.0722));
|
|
|
|
// Apply color shift effect (Cyberpunk/Monochrome look)
|
|
vec3 outputColor = vec3(gray, gray, gray);
|
|
outputColor.g *= 1.0 + sin(time) * 0.1;
|
|
outputColor.b *= 1.2 + cos(time) * 0.1;
|
|
outputColor.r *= 0.8;
|
|
|
|
FragColor = vec4(outputColor, texColor.a);
|
|
}
|
|
"""
|
|
|
|
def compile_and_link_shader(vs_source, fs_source):
|
|
"""Compiles the vertex and fragment shaders and links them into a program."""
|
|
try:
|
|
vertex_shader = compileShader(vs_source, GL_VERTEX_SHADER)
|
|
fragment_shader = compileShader(fs_source, GL_FRAGMENT_SHADER)
|
|
shader_program = compileProgram(vertex_shader, fragment_shader)
|
|
return shader_program
|
|
except Exception as e:
|
|
print(f"Shader compilation/linking failed: {e}")
|
|
return 0
|
|
|
|
def load_opencv_image_as_texture(image_path):
|
|
"""
|
|
Loads an image using OpenCV and converts it into an OpenGL texture ID.
|
|
NOTE: A placeholder image is used if loading fails.
|
|
"""
|
|
try:
|
|
# Load image using OpenCV
|
|
img = cv2.imread(image_path)
|
|
if img is None:
|
|
raise FileNotFoundError(f"Image not found at path: {image_path}. Using placeholder.")
|
|
except Exception:
|
|
# Create a simple placeholder image (512x512 blue gradient)
|
|
img = np.zeros((512, 512, 3), dtype=np.uint8)
|
|
for i in range(512):
|
|
img[:, i, 0] = 255 - i // 2
|
|
img[:, i, 2] = i // 2
|
|
print("Using a generated blue gradient placeholder image.")
|
|
|
|
# Convert BGR (OpenCV default) to RGB, and flip vertically (OpenGL convention)
|
|
img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
|
|
img_flipped = np.flipud(img_rgb)
|
|
|
|
# Generate and bind the texture
|
|
texture_id = glGenTextures(1)
|
|
glBindTexture(GL_TEXTURE_2D, texture_id)
|
|
|
|
# Set texture parameters
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE)
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE)
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)
|
|
|
|
# Upload the image data to the texture
|
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, img_flipped.shape[1], img_flipped.shape[0], 0, GL_RGB, GL_UNSIGNED_BYTE, img_flipped)
|
|
glGenerateMipmap(GL_TEXTURE_2D)
|
|
|
|
return texture_id, img_flipped.shape[1], img_flipped.shape[0]
|
|
|
|
# --- Main Application Logic (Using GLFW) ---
|
|
|
|
def run_shader_app_glfw():
|
|
# --- Configuration ---
|
|
SCREEN_WIDTH = 800
|
|
SCREEN_HEIGHT = 600
|
|
IMAGE_PATH = "sample_image.jpg" # Replace with your image path
|
|
|
|
# --- GLFW Initialization ---
|
|
if not glfw.init():
|
|
print("Failed to initialize GLFW")
|
|
return
|
|
|
|
# Set OpenGL version hints
|
|
glfw.window_hint(glfw.CONTEXT_VERSION_MAJOR, 3)
|
|
glfw.window_hint(glfw.CONTEXT_VERSION_MINOR, 3)
|
|
glfw.window_hint(glfw.OPENGL_PROFILE, glfw.OPENGL_CORE_PROFILE)
|
|
|
|
# Load image first to determine window size
|
|
texture_id, img_width, img_height = load_opencv_image_as_texture(IMAGE_PATH)
|
|
|
|
# Calculate window size based on image aspect ratio
|
|
aspect_ratio = img_width / img_height
|
|
final_width = SCREEN_WIDTH
|
|
final_height = int(SCREEN_WIDTH / aspect_ratio)
|
|
|
|
if final_height > SCREEN_HEIGHT:
|
|
final_height = SCREEN_HEIGHT
|
|
final_width = int(SCREEN_HEIGHT * aspect_ratio)
|
|
|
|
# Create a windowed mode window and its OpenGL context
|
|
window = glfw.create_window(final_width, final_height, "OpenCV Image with GLSL Shader (GLFW)", None, None)
|
|
if not window:
|
|
glfw.terminate()
|
|
print("Failed to create GLFW window")
|
|
return
|
|
|
|
glfw.make_context_current(window)
|
|
|
|
# Set up the viewport
|
|
glViewport(0, 0, final_width, final_height)
|
|
|
|
# Define the key callback function for exiting
|
|
def key_callback(window, key, scancode, action, mods):
|
|
if key == glfw.KEY_ESCAPE and action == glfw.PRESS:
|
|
glfw.set_window_should_close(window, True)
|
|
|
|
glfw.set_key_callback(window, key_callback)
|
|
|
|
# --- Shader Compilation ---
|
|
shader_program = compile_and_link_shader(VERTEX_SHADER_SOURCE, FRAGMENT_SHADER_SOURCE)
|
|
if not shader_program:
|
|
glfw.terminate()
|
|
return
|
|
|
|
# --- Quad Geometry Setup (The screen where the texture is drawn) ---
|
|
quad_vertices = np.array([
|
|
# Positions # Texture Coords
|
|
-1.0, 1.0, 0.0, 0.0, 1.0, # Top-Left
|
|
1.0, 1.0, 0.0, 1.0, 1.0, # Top-Right
|
|
1.0, -1.0, 0.0, 1.0, 0.0, # Bottom-Right
|
|
-1.0, -1.0, 0.0, 0.0, 0.0 # Bottom-Left
|
|
], dtype=np.float32)
|
|
|
|
quad_indices = np.array([0, 1, 2, 2, 3, 0], dtype=np.uint32)
|
|
|
|
# Setup VAO/VBO/EBO (identical to the Pygame script)
|
|
VAO = glGenVertexArrays(1)
|
|
glBindVertexArray(VAO)
|
|
|
|
VBO = glGenBuffers(1)
|
|
glBindBuffer(GL_ARRAY_BUFFER, VBO)
|
|
glBufferData(GL_ARRAY_BUFFER, quad_vertices.nbytes, quad_vertices, GL_STATIC_DRAW)
|
|
|
|
EBO = glGenBuffers(1)
|
|
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO)
|
|
glBufferData(GL_ELEMENT_ARRAY_BUFFER, quad_indices.nbytes, quad_indices, GL_STATIC_DRAW)
|
|
|
|
# Position attribute
|
|
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * quad_vertices.itemsize, ctypes.c_void_p(0))
|
|
glEnableVertexAttribArray(0)
|
|
|
|
# Texture coordinate attribute
|
|
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * quad_vertices.itemsize, ctypes.c_void_p(3 * quad_vertices.itemsize))
|
|
glEnableVertexAttribArray(1)
|
|
|
|
# --- Main Loop ---
|
|
glUseProgram(shader_program)
|
|
|
|
start_time = glfw.get_time()
|
|
|
|
while not glfw.window_should_close(window):
|
|
# Poll and process events (required by GLFW)
|
|
glfw.poll_events()
|
|
|
|
# Clear the screen
|
|
glClearColor(0.1, 0.1, 0.1, 1.0)
|
|
glClear(GL_COLOR_BUFFER_BIT)
|
|
|
|
# Use the compiled shader program
|
|
glUseProgram(shader_program)
|
|
|
|
# Pass the current time to the shader for simple animation
|
|
current_time = glfw.get_time()
|
|
time_uniform_location = glGetUniformLocation(shader_program, "time")
|
|
glUniform1f(time_uniform_location, current_time - start_time)
|
|
|
|
# Bind the texture and sampler uniform
|
|
glActiveTexture(GL_TEXTURE0)
|
|
glBindTexture(GL_TEXTURE_2D, texture_id)
|
|
glUniform1i(glGetUniformLocation(shader_program, "ourTexture"), 0)
|
|
|
|
# Draw the quad
|
|
glBindVertexArray(VAO)
|
|
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, None)
|
|
|
|
# Swap buffers and display the rendered frame
|
|
glfw.swap_buffers(window)
|
|
|
|
# --- Cleanup ---
|
|
glDeleteTextures([texture_id])
|
|
glDeleteProgram(shader_program)
|
|
glDeleteVertexArrays(1, [VAO])
|
|
glDeleteBuffers(1, [VBO, EBO])
|
|
|
|
glfw.terminate()
|
|
|
|
if __name__ == '__main__':
|
|
run_shader_app_glfw()
|