Drawing a few rotated rectangles in one file using OpenGL ES 3.0
It is a very simple example for beginners that shows how to draw a few rotated rectangles using OpenGL ES 3.0 in Python and PySDL3. There are more examples in OpenGL here: Simple OpenGL examples for beginners in PySDL3
pip PySDL3 numpy PyOpenGL PyGLM
py main.py
main.py
import ctypes
import math
import os
import glm
import numpy as np
from OpenGL.GL import *
from OpenGL.GL.shaders import *
os.environ["SDL_MAIN_USE_CALLBACKS"] = "1"
os.environ["SDL_RENDER_DRIVER"] = "opengles2"
import sdl3
glContext = None
window = None
uColorLocation = None
uMvpMatrixLocation = None
projViewMatrix = None
vertexShaderSource = """
#version 300 es
layout(location = 0) in vec2 aPosition;
uniform mat4 uMvpMatrix;
void main()
{
gl_Position = uMvpMatrix * vec4(aPosition, 0.0, 1.0);
}
"""
fragmentShaderSource = """
#version 300 es
precision mediump float;
uniform vec3 uColor;
out vec4 fragColor;
void main()
{
fragColor = vec4(uColor, 1.0);
}
"""
def SDL_AppInit(appstate, argc, argv):
global glContext
global window
global projViewMatrix
global uColorLocation
global uMvpMatrixLocation
if not sdl3.SDL_Init(sdl3.SDL_INIT_VIDEO):
sdl3.SDL_Log("Couldn't initialize SDL: %s".encode() % sdl3.SDL_GetError())
return sdl3.SDL_APP_FAILURE
sdl3.SDL_GL_SetAttribute(sdl3.SDL_GL_MULTISAMPLEBUFFERS, 1) # Enable MULTISAMPLE
sdl3.SDL_GL_SetAttribute(sdl3.SDL_GL_MULTISAMPLESAMPLES, 2) # Can be 2, 4, 8 or 16
sdl3.SDL_GL_SetAttribute(sdl3.SDL_GL_CONTEXT_PROFILE_MASK, sdl3.SDL_GL_CONTEXT_PROFILE_ES)
sdl3.SDL_GL_SetAttribute(sdl3.SDL_GL_CONTEXT_MAJOR_VERSION, 3)
sdl3.SDL_GL_SetAttribute(sdl3.SDL_GL_CONTEXT_MINOR_VERSION, 0)
windowTitle = "PySDL3, PyGLM, PyOpenGL".encode()
window = sdl3.SDL_CreateWindow(windowTitle, 350, 350, sdl3.SDL_WINDOW_OPENGL)
if not window:
sdl3.SDL_Log("Couldn't create a window: %s".encode() % sdl3.SDL_GetError())
return sdl3.SDL_APP_FAILURE
# Create an OpenGL context
glContext = sdl3.SDL_GL_CreateContext(window)
if not glContext:
sdl3.SDL_Log("Couldn't create a glContext: %s".encode() % sdl3.SDL_GetError())
return sdl3.SDL_APP_FAILURE
sdl3.SDL_GL_SetSwapInterval(1) # Turn on vertical sync
glDisable(GL_FRAMEBUFFER_SRGB)
glClearColor(0.2, 0.2, 0.2, 1)
vertPositions = np.array([
-0.5, -0.5, # First triangle
-0.5, 0.5,
0.5, -0.5,
0.5, -0.5, # Second triangle
-0.5, 0.5,
0.5, 0.5
], dtype=np.float32)
vertPosBuffer = glGenBuffers(1)
glBindBuffer(GL_ARRAY_BUFFER, vertPosBuffer)
glBufferData(GL_ARRAY_BUFFER, len(vertPositions) * 4,
vertPositions, GL_STATIC_DRAW)
program = compileProgram(
compileShader(vertexShaderSource, GL_VERTEX_SHADER),
compileShader(fragmentShaderSource, GL_FRAGMENT_SHADER))
glUseProgram(program)
aPositionLocation = glGetAttribLocation(program, "aPosition")
glVertexAttribPointer(aPositionLocation, 2, GL_FLOAT, GL_FALSE, 0, ctypes.c_void_p(0))
glEnableVertexAttribArray(aPositionLocation)
# Access the uniform variables in the shaders
uColorLocation = glGetUniformLocation(program, "uColor")
uMvpMatrixLocation = glGetUniformLocation(program, "uMvpMatrix")
# Create an orthographic projection matrix and a view matrix
projMatrix = glm.ortho(0, 300, 300, 0, 1, -1)
viewMatrix = glm.lookAt(
glm.vec3(0, 0, 1), # Position
glm.vec3(0, 0, 0), # Target
glm.vec3(0, 1, 0)) # Up vector
# Combine them to one projView matrix
projViewMatrix = projMatrix * viewMatrix
return sdl3.SDL_APP_CONTINUE
def SDL_AppEvent(appstate, event):
if sdl3.SDL_DEREFERENCE(event).type == sdl3.SDL_EVENT_QUIT:
return sdl3.SDL_APP_SUCCESS
return sdl3.SDL_APP_CONTINUE
def drawRectangle(pos, size, angle, color):
# Create a model matrix, that is, a matrix combining the
# translation matrix, rotation matrix, and the scale matrix
modelMatrix = glm.translate(glm.mat4(1), glm.vec3(pos.x, pos.y, 0))
modelMatrix = glm.rotate(modelMatrix, math.radians(angle), glm.vec3(0, 0, 1))
modelMatrix = glm.scale(modelMatrix, glm.vec3(size.x, size.y, 1))
# Combine projView matrix and model matrix into one MVP matrix
mvpMatrix = projViewMatrix * modelMatrix
# Send MVP matrix to the vertex shader
glUniformMatrix4fv(uMvpMatrixLocation, 1, GL_FALSE, glm.value_ptr(mvpMatrix))
# Send a color to the fragment shader
glUniform3fv(uColorLocation, 1, glm.value_ptr(color))
# Draw a rectangle
glDrawArrays(GL_TRIANGLES, 0, 6)
def SDL_AppIterate(appstate):
global projViewMatrix
global uMvpMatrixLocation
glClear(GL_COLOR_BUFFER_BIT)
# First rectangle
position = glm.vec2(200, 70)
size = glm.vec2(150, 20)
angle = -20
color = glm.vec3(1, 0.5, 0.5)
drawRectangle(position, size, angle, color)
# Second rectangle
position = glm.vec2(80, 150)
size = glm.vec2(100, 30)
angle = 20
color = glm.vec3(0.5, 1, 0.5)
drawRectangle(position, size, angle, color)
# Third rectangle
position = glm.vec2(150, 250)
size = glm.vec2(200, 20)
angle = 0
color = glm.vec3(0.5, 0.5, 1)
drawRectangle(position, size, angle, color)
sdl3.SDL_GL_SwapWindow(window)
return sdl3.SDL_APP_CONTINUE
def SDL_AppQuit(appstate, result):
global glContext
sdl3.SDL_GL_DestroyContext(glContext)
# SDL will clean up the window/renderer for us