#!/usr/bin/env python3 """Shader Effects Demo - GLSL shader visual effects Interactive controls: 1-4: Focus on specific shader Space: Toggle shaders on/off R: Reset all shaders ESC: Exit demo Shader uniforms available: - float time: Seconds since engine start - float delta_time: Seconds since last frame - vec2 resolution: Texture size in pixels - vec2 mouse: Mouse position in window coordinates """ import mcrfpy import sys class ShaderDemo: def __init__(self): self.scene = mcrfpy.Scene("shader_demo") self.ui = self.scene.children self.shaders_enabled = True self.shader_frames = [] self.shaders = [] self.setup() def setup(self): """Build the demo scene.""" # Background bg = mcrfpy.Frame( pos=(0, 0), size=(1024, 768), fill_color=mcrfpy.Color(15, 15, 20) ) self.ui.append(bg) # Title title = mcrfpy.Caption( text="Shader Effects Demo", pos=(512, 30), font_size=28, fill_color=mcrfpy.Color(255, 255, 255) ) title.outline = 2 title.outline_color = mcrfpy.Color(0, 0, 0) self.ui.append(title) # Create four shader demo quadrants self._create_pulse_shader(50, 100) self._create_vignette_shader(530, 100) self._create_wave_shader(50, 420) self._create_color_shift_shader(530, 420) # Instructions instr = mcrfpy.Caption( text="1-4: Focus shader | Space: Toggle on/off | R: Reset | ESC: Exit", pos=(50, 730), font_size=14, fill_color=mcrfpy.Color(120, 120, 120) ) self.ui.append(instr) # Status self.status = mcrfpy.Caption( text="Shaders: Enabled", pos=(50, 700), font_size=16, fill_color=mcrfpy.Color(100, 200, 100) ) self.ui.append(self.status) def _create_pulse_shader(self, x, y): """Create pulsing glow shader demo.""" # Label label = mcrfpy.Caption( text="1. Pulse Glow", pos=(x + 200, y - 20), font_size=16, fill_color=mcrfpy.Color(150, 150, 150) ) self.ui.append(label) # Frame to apply shader to frame = mcrfpy.Frame( pos=(x, y), size=(420, 280), fill_color=mcrfpy.Color(80, 60, 120), outline_color=mcrfpy.Color(150, 100, 200), outline=2 ) # Add some content content = mcrfpy.Caption( text="Brightness pulses\nusing time uniform", pos=(210, 100), font_size=18, fill_color=mcrfpy.Color(255, 255, 255) ) frame.children.append(content) # Create pulse shader pulse_shader = mcrfpy.Shader(''' uniform sampler2D texture; uniform float time; void main() { vec2 uv = gl_TexCoord[0].xy; vec4 color = texture2D(texture, uv); // Pulse brightness float pulse = 0.7 + 0.3 * sin(time * 2.0); color.rgb *= pulse; gl_FragColor = color * gl_Color; } ''', dynamic=True) frame.shader = pulse_shader self.ui.append(frame) self.shader_frames.append(frame) self.shaders.append(pulse_shader) def _create_vignette_shader(self, x, y): """Create vignette (darkened edges) shader demo.""" # Label label = mcrfpy.Caption( text="2. Vignette", pos=(x + 200, y - 20), font_size=16, fill_color=mcrfpy.Color(150, 150, 150) ) self.ui.append(label) # Frame frame = mcrfpy.Frame( pos=(x, y), size=(420, 280), fill_color=mcrfpy.Color(100, 80, 60), outline_color=mcrfpy.Color(200, 150, 100), outline=2 ) content = mcrfpy.Caption( text="Darkened edges\nclassic vignette effect", pos=(210, 100), font_size=18, fill_color=mcrfpy.Color(255, 255, 255) ) frame.children.append(content) # Create vignette shader vignette_shader = mcrfpy.Shader(''' uniform sampler2D texture; uniform vec2 resolution; void main() { vec2 uv = gl_TexCoord[0].xy; vec4 color = texture2D(texture, uv); // Calculate distance from center vec2 center = vec2(0.5, 0.5); float dist = distance(uv, center); // Vignette effect - darken edges float vignette = 1.0 - smoothstep(0.3, 0.8, dist); color.rgb *= vignette; gl_FragColor = color * gl_Color; } ''', dynamic=False) frame.shader = vignette_shader self.ui.append(frame) self.shader_frames.append(frame) self.shaders.append(vignette_shader) def _create_wave_shader(self, x, y): """Create wave distortion shader demo.""" # Label label = mcrfpy.Caption( text="3. Wave Distortion", pos=(x + 200, y - 20), font_size=16, fill_color=mcrfpy.Color(150, 150, 150) ) self.ui.append(label) # Frame frame = mcrfpy.Frame( pos=(x, y), size=(420, 280), fill_color=mcrfpy.Color(60, 100, 120), outline_color=mcrfpy.Color(100, 180, 220), outline=2 ) content = mcrfpy.Caption( text="Sine wave\nUV distortion", pos=(210, 100), font_size=18, fill_color=mcrfpy.Color(255, 255, 255) ) frame.children.append(content) # Create wave shader wave_shader = mcrfpy.Shader(''' uniform sampler2D texture; uniform float time; void main() { vec2 uv = gl_TexCoord[0].xy; // Apply wave distortion float wave = sin(uv.y * 20.0 + time * 3.0) * 0.01; uv.x += wave; vec4 color = texture2D(texture, uv); gl_FragColor = color * gl_Color; } ''', dynamic=True) frame.shader = wave_shader self.ui.append(frame) self.shader_frames.append(frame) self.shaders.append(wave_shader) def _create_color_shift_shader(self, x, y): """Create chromatic aberration / color shift shader demo.""" # Label label = mcrfpy.Caption( text="4. Color Shift", pos=(x + 200, y - 20), font_size=16, fill_color=mcrfpy.Color(150, 150, 150) ) self.ui.append(label) # Frame frame = mcrfpy.Frame( pos=(x, y), size=(420, 280), fill_color=mcrfpy.Color(80, 80, 80), outline_color=mcrfpy.Color(150, 150, 150), outline=2 ) content = mcrfpy.Caption( text="Chromatic aberration\nRGB channel offset", pos=(210, 100), font_size=18, fill_color=mcrfpy.Color(255, 255, 255) ) frame.children.append(content) # Create color shift shader colorshift_shader = mcrfpy.Shader(''' uniform sampler2D texture; uniform float time; void main() { vec2 uv = gl_TexCoord[0].xy; // Offset for each channel float offset = 0.005 + 0.003 * sin(time); // Sample each channel with slight offset float r = texture2D(texture, uv + vec2(offset, 0.0)).r; float g = texture2D(texture, uv).g; float b = texture2D(texture, uv - vec2(offset, 0.0)).b; float a = texture2D(texture, uv).a; gl_FragColor = vec4(r, g, b, a) * gl_Color; } ''', dynamic=True) frame.shader = colorshift_shader self.ui.append(frame) self.shader_frames.append(frame) self.shaders.append(colorshift_shader) def toggle_shaders(self): """Toggle all shaders on/off.""" self.shaders_enabled = not self.shaders_enabled for frame, shader in zip(self.shader_frames, self.shaders): if self.shaders_enabled: frame.shader = shader self.status.text = "Shaders: Enabled" self.status.fill_color = mcrfpy.Color(100, 200, 100) else: frame.shader = None self.status.text = "Shaders: Disabled" self.status.fill_color = mcrfpy.Color(200, 100, 100) def on_key(self, key, state): """Handle keyboard input.""" if state != "start": return if key == "Escape": sys.exit(0) elif key == "Space": self.toggle_shaders() elif key == "R": # Re-enable all shaders self.shaders_enabled = False self.toggle_shaders() elif key in ("Num1", "Num2", "Num3", "Num4"): # Focus on specific shader (could zoom in) idx = int(key[-1]) - 1 if idx < len(self.shader_frames): self.status.text = f"Focused: Shader {idx + 1}" def activate(self): """Activate the demo scene.""" self.scene.on_key = self.on_key mcrfpy.current_scene = self.scene def main(): """Run the shader demo.""" demo = ShaderDemo() demo.activate() # Headless mode: capture screenshot and exit try: if mcrfpy.headless_mode(): from mcrfpy import automation mcrfpy.Timer("screenshot", lambda rt: ( automation.screenshot("screenshots/features/shader_demo.png"), sys.exit(0) ), 200) except AttributeError: pass if __name__ == "__main__": main()