Importing required functions and packages¶

In [1]:
import ctypes as ct
import numpy as np
import pygame as pg
import OpenGL.GL as opg
from OpenGL.GL.shaders import compileProgram, compileShader
pygame 2.1.2 (SDL 2.0.16, Python 3.8.10)
Hello from the pygame community. https://www.pygame.org/contribute.html

This function creates and allocates memory for vertex-shaders and fragment-shaders¶

In [6]:
def createShader(vertexFilepath, fragmentFilepath):

    with open(vertexFilepath,'r') as f:
        vertex_src = f.readlines()

    with open(fragmentFilepath,'r') as f:
        fragment_src = f.readlines()
    
    shader = compileProgram(compileShader(vertex_src, opg.GL_VERTEX_SHADER),
                            compileShader(fragment_src, opg.GL_FRAGMENT_SHADER))
    
    return shader

Definition of Triangle class¶

In [7]:
class Triangle:

    def __init__(self):

        # x, y, z, r, g, b
        self.vertices = [
            -0.5,-0.5, 0.0, 1.0, 0.0, 0.0,
             0.5,-0.5, 0.0, 0.0, 1.0, 0.0,
             0.0, 0.5, 0.0, 0.0, 0.0, 1.0,
        ]

        self.vertices = np.array(self.vertices, dtype=np.float32)
        self.vertex_count = int( len(self.vertices) / 6)

        # vertex array objects
        self.vao = opg.glGenVertexArrays(1)
        opg.glBindVertexArray(self.vao)

        # vertex buffer objects
        self.vbo = opg.glGenBuffers(1)
        opg.glBindBuffer(opg.GL_ARRAY_BUFFER, self.vbo)

        opg.glBufferData(opg.GL_ARRAY_BUFFER, self.vertices.nbytes, self.vertices, opg.GL_STATIC_DRAW)
        opg.glEnableVertexAttribArray(0)
        opg.glVertexAttribPointer(0, 3, opg.GL_FLOAT, opg.GL_FALSE, 24, ct.c_void_p(0))
        opg.glEnableVertexAttribArray(1)
        opg.glVertexAttribPointer(1, 3, opg.GL_FLOAT, opg.GL_FALSE, 24, ct.c_void_p(12))

    def destroy(self):
        opg.glDeleteVertexArrays(1, [self.vao])
        opg.glDeleteBuffers(1, [self.vbo])

Definition of App class¶

In [8]:
class App:
    def __init__(self, title):
        
        # initialize pygame engine
        pg.init()
        pg.display.set_mode((500,600), pg.OPENGL|pg.DOUBLEBUF)
        pg.display.set_caption(title)
        self.clock = pg.time.Clock()

        # defining paths for vertex and fragment shaders
        self.vertexFilepath = "./shaders/vertex.txt"
        self.fragmentFilepath = "./shaders/fragment.txt"

        # adding vertex and fragment buffer data
        self.triangle = Triangle()
        self.shader = createShader(self.vertexFilepath, self.fragmentFilepath)
        opg.glUseProgram(self.shader)
        
        # initialize opengl
        opg.glClearColor(0.17,0.24,0.39,1) # RGBA Color Mode
        self.minLoop()
        
    def minLoop(self):
        opg.running = True
        
        # event loop
        while(opg.running):
            
            # checking events
            for evt in pg.event.get():
                if(evt.type == pg.QUIT):
                    opg.running = False
            
            # refreshing screen
            opg.glClear(opg.GL_COLOR_BUFFER_BIT)

            # draw the triangle
            opg.glUseProgram(self.shader)
            opg.glBindVertexArray(self.triangle.vao)
            opg.glDrawArrays(opg.GL_TRIANGLES, 0, self.triangle.vertex_count)

            pg.display.flip()
            
            # timing
            self.clock.tick(60)
            
        self.quit()
    
    # for deallocation of memory and resources
    def quit(self):
        self.triangle.destroy()
        opg.glDeleteProgram(self.shader)
        pg.quit()
        print("App Closed")

Driver Code¶

In [9]:
if __name__ == "__main__":
    myApp = App("OpenGL Triangle")
App Closed

Output¶

App window