Overview

The provided Python script utilizes Pyglet, a powerful library for creating games and other visually rich applications. The script sets up a window, defines shaders for rendering graphics, and draws a colored triangle on the screen.


Import Statements

import pyglet 
from pyglet.graphics.shader import Shader, ShaderProgram 
from pyglet.gl import GL_TRIANGLES
  • pyglet: The main library used for windowing and rendering.
  • Shader & ShaderProgram: Classes from Pyglet for handling GLSL shaders.
  • GL_TRIANGLES: A constant from OpenGL indicating that the geometry to be drawn consists of triangles.

Window Setup

window = pyglet.window.Window(width=800, height=600, caption='Drawing Shapes') window.set_location(100, 100)
  • Window Creation: Initializes a window with specified dimensions and a title.
  • Positioning: Sets the window’s position on the screen.

Shader Code

Shaders are small programs that run on the GPU to control the rendering pipeline. This script uses two types of shaders:

  1. Vertex Shader: Processes each vertex’s position and attributes.
  2. Fragment Shader: Determines the color of each pixel.

Vertex Shader

#version 330 
layout(location = 0) in vec2 verticies; 
layout(location = 1) in vec4 color;  
out vec4 newColor;  
void main() {     
	gl_Position = vec4(verticies, 0.0f, 1.0f);     
	newColor = color; 
	}

Key Components:

  • version 330: Specifies the GLSL version 3.30, ensuring compatibility with OpenGL 3.3.
  • Input Attributes:
    • layout(location = 0) in vec2 verticies;
      • location 0: Receives vertex positions as 2D vectors (x, y).
    • layout(location = 1) in vec4 color;
      • location 1: Receives color data as 4D vectors (r, g, b, a).
  • Output Variable:
    • out vec4 newColor;
      • Passes the vertex color to the fragment shader.
  • Main Function:
    • gl_Position = vec4(verticies, 0.0f, 1.0f);
      • Transforms the 2D vertex positions to 4D homogeneous coordinates, setting z to 0.0 and w to 1.0 for proper rendering.
    • newColor = color;
      • Forwards the input color to the fragment shader.

Fragment Shader

#version 330 
in vec4 newColor;  
out vec4 outColor;  
void main() {     
	outColor = newColor; 
	}`

Key Components:

  • version 330: Matches the vertex shader’s GLSL version.
  • Input Variable:
    • in vec4 newColor;
      • Receives the interpolated color from the vertex shader.
  • Output Variable:
    • out vec4 outColor;
      • Specifies the final color of the pixel.
  • Main Function:
    • outColor = newColor;
      • Assigns the interpolated color to the output, effectively coloring the pixel.

Compiling Shaders

# compile vertex and fragment sources into a shader program 
vertex_shader = Shader(vertex_src, 'vertex') 
fragment_shader = Shader(fragment_src, 'fragment') 
shader_program = ShaderProgram(vertex_shader, fragment_shader)
  • Shader Compilation:
    • Vertex Shader: Compiled from vertex_src.
    • Fragment Shader: Compiled from fragment_src.
  • Shader Program: Links the compiled vertex and fragment shaders into a single program that can be used for rendering.

Preparing Geometry

# batch objects to draw all shapes at once 
batch = pyglet.graphics.Batch()`
  • Batching: Groups multiple draw calls into a single batch for efficient rendering.

Defining Vertex Data

# add vertex lists to the shader batch 
shader_program.vertex_list(3, GL_TRIANGLES,
						   batch=batch,
						   verticies=('f', (-0.5, -0.5, 0.5, -0.5, 0.0, 0.5)),
						   color=('Bn', (255, 0, 0, 255, 0, 255, 0, 255, 0, 0, 255, 255)))
  • vertex_list Parameters:
    • 3: Number of vertices (forming one triangle).
    • GL_TRIANGLES: Specifies that the vertices form a triangle.
    • batch=batch: Associates this vertex list with the previously created batch.
  • Attributes:
    • verticies=('f', (-0.5, -0.5, 0.5, -0.5, 0.0, 0.5))
      • ‘f’: Indicates that vertex positions are floating-point numbers.
      • Coordinates: Defines three 2D points forming a triangle.
        • (-0.5, -0.5), (0.5, -0.5), (0.0, 0.5)
    • color=('Bn', (255, 0, 0, 255, 0, 255, 0, 255, 0, 0, 255, 255))
      • ‘Bn’: Custom attribute format (may denote normalized bytes).
      • Color Values: Defines colors for each vertex in RGBA format.
        • Vertex 1: Red (255, 0, 0, 255)
        • Vertex 2: Green (0, 255, 0, 255)
        • Vertex 3: Blue (0, 0, 255, 255)

Rendering Loop

@window.event 
def on_draw() -> None:     
	window.clear()     
	batch.draw()
  • on_draw Event:
    • window.clear(): Clears the window for new frame rendering.
    • batch.draw(): Renders all batched vertex lists using the compiled shader program.

Running the Application

# pyglet.clock.schedule_interval(update, 1/60.0) 
pyglet.app.run()
  • pyglet.app.run(): Starts the application’s main loop, handling events and rendering.

  • Commented Out Update Function:

    • pyglet.clock.schedule_interval(update, 1/60.0)
      • Intended for scheduling a function (update) at 60 frames per second, typically used for animations or game logic.
      • Currently commented out, indicating no updates are scheduled.

Summary of Shader Workflow

  1. Vertex Shader:
    • Receives vertex positions and colors.
    • Transforms positions to screen coordinates.
    • Passes color data to the fragment shader.
  2. Fragment Shader:
    • Receives interpolated color data.
    • Outputs the final pixel color.
  3. Shader Program:
    • Combines both shaders to render colored geometry.
  4. Rendering:
    • A triangle is drawn with vertices colored red, green, and blue.
    • Colors interpolate across the triangle’s surface, creating a gradient effect.

Additional Notes

  • Coordinate System:
    • The vertex positions are defined in normalized device coordinates (NDC), where (-1, -1) is the bottom-left and (1, 1) is the top-right of the window.
  • Color Normalization:
    • The color values are specified as bytes (0-255) and are likely normalized within the shader or rendering pipeline to 0.0-1.0 range.
  • Extensibility:
    • The shader setup allows for easy addition of more complex effects, such as lighting or textures, by modifying the shader code.
import pyglet
from pyglet.graphics.shader import Shader, ShaderProgram
from pyglet.gl import GL_TRIANGLES
 
window = pyglet.window.Window(width=800, height=600, caption='Drawing Shapes')
window.set_location(100, 100)
 
# create vertex and fragment sources
vertex_src = '''
#version 330
layout(location = 0) in vec2 verticies;
layout(location = 1) in vec4 color;
 
out vec4 newColor;
 
void main() {
    gl_Position = vec4(verticies, 0.0f, 1.0f);
    newColor = color;
}
'''
 
fragment_src = '''
#version 330
in vec4 newColor;
 
out vec4 outColor;
 
void main() {
    outColor = newColor;
}
'''
 
# compile vertex and fragment sources into a shader program
vertex_shader = Shader(vertex_src, 'vertex')
fragment_shader = Shader(fragment_src, 'fragment')
shader_program = ShaderProgram(vertex_shader, fragment_shader)
 
# batch objects to draw all shapes at once
batch = pyglet.graphics.Batch()
 
# add vertex lists to the shader batch
shader_program.vertex_list(3, GL_TRIANGLES,
                           batch=batch,
                           verticies=('f', (-0.5, -0.5, 0.5, -0.5, 0.0, 0.5)),
                           color=('Bn', (255, 0, 0, 255, 0, 255, 0, 255, 0, 0, 255, 255)))
 
 
@window.event
def on_draw() -> None:
    window.clear()
    batch.draw()
 
# pyglet.clock.schedule_interval(update, 1/60.0)
pyglet.app.run()