본문 바로가기

그래픽스

OpenGL with Python 2: Drawing a Triangle

OpenGL에서 데이터를 한 번 그래픽 카드로 보내고 그래픽 카드에서 준비된 리소스를 계속 사용한다. 왜냐하면 CPU와 그래픽 카드 간의 연결이 성능의 가장 큰 병목 현상이기 때문이다.

코드

전체코드

import pygame as pg
from OpenGL.GL import *
import numpy as np
import ctypes
from OpenGL.GL.shaders import compileProgram, compileShader

class App:

    def __init__(self):
        #initialize Python
        pg.init()
        pg.display.set_mode((640,480), pg.OPENGL|pg.DOUBLEBUF)
        self.clock = pg.time.Clock()
        #initialize opengl
        glClearColor(0.1, 0.2, 0.2, 1) #RGBA
        self.shader = self.createShader("shader/vertex.txt","shader/fragment.txt")
        glUseProgram(self.shader)
        self.triangle = Triangle()
        self.mainLoop()

    def createShader(self, 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, GL_VERTEX_SHADER),
            compileShader(fragment_src, GL_FRAGMENT_SHADER)
        )

        return shader

    def mainLoop(self):
        running = True
        while(running):
            #check events
            for event in pg.event.get():
                if(event.type ==pg.QUIT):
                    running = False

            # refresh screen
            glClear(GL_COLOR_BUFFER_BIT)

            glUseProgram(self.shader)
            glBindVertexArray(self.triangle.vao)
            glDrawArrays(GL_TRIANGLES, 0 , self.triangle.vertex_count)

            pg.display.flip()

            #timing
            self.clock.tick(60)
        self.quit()

    def quit(self):
        self.triangle.destory()
        glDeleteProgram(self.shader)
        pg.quit()

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 = 3
        self.vao = glGenVertexArrays(1)
        glBindVertexArray(self.vao)
        self.vbo = glGenBuffers(1)
        glBindBuffer(GL_ARRAY_BUFFER, self.vbo)
        glBufferData(GL_ARRAY_BUFFER, self.vertices.nbytes, self.vertices, GL_STATIC_DRAW)
        glEnableVertexAttribArray(0)
        glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 24, ctypes.c_void_p(0))
        glEnableVertexAttribArray(1)
        glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 24, ctypes.c_void_p(12))

    def destory(self):
        glDeleteVertexArrays(1, (self.vao,))
        glDeleteBuffers(1, (self.vbo,))

if __name__ == "__main__":
    myApp = App()

코드 설명

점들의 목록으로 구성된 삼각형 클래스를 만들고, 데이터의 타입을 맞추기 위해 NumPy 배열을 사용하여 점들을 타입을 변환한다. 그리고 정점 버퍼 객체(VBO)와 정점 배열 객체(VAO)를 만든다. VBO는 정점 데이터를 저장하는 기본 저장소이며 VAO는 정점 데이터를 선언하고 VBO와 연결하는 방법이다.

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 = 3
        self.vao = glGenVertexArrays(1)
        glBindVertexArray(self.vao)
        self.vbo = glGenBuffers(1)
        glBindBuffer(GL_ARRAY_BUFFER, self.vbo)
        glBufferData(GL_ARRAY_BUFFER, self.vertices.nbytes, self.vertices, GL_STATIC_DRAW)
        glEnableVertexAttribArray(0)
        glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 24, ctypes.c_void_p(0))
        glEnableVertexAttribArray(1)
        glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 24, ctypes.c_void_p(12))

    def destory(self):
        glDeleteVertexArrays(1, (self.vao,))
        glDeleteBuffers(1, (self.vbo,))

 

정점 셰이더와 프래그먼트 셰이더를 만든다.

#version 330 core

layout (location = 0) in vec3 vertexPos;
layout (location = 1) in vec3 vertexColor;

out vec3 fragmentColor;

void main()
{
    gl_Position = vec4(vertexPos, 1.0);
    fragmentColor = vertexColor;
}
#version 330 core

in vec3 fragmentColor;

out vec4 color;

void main()
{
    color = vec4(fragmentColor, 1.0);
}

 

정점 쉐이더와 프래그먼트 쉐이더 파일을 읽고 삼각형은 그린다. 종료시 쉐이더 메모리 삭제.

class App:

    def __init__(self):
        #initialize Python
        pg.init()
        pg.display.set_mode((640,480), pg.OPENGL|pg.DOUBLEBUF)
        self.clock = pg.time.Clock()
        #initialize opengl
        glClearColor(0.1, 0.2, 0.2, 1) #RGBA
        self.shader = self.createShader("shader/vertex.txt","shader/fragment.txt")
        glUseProgram(self.shader)
        self.triangle = Triangle()
        self.mainLoop()

    def createShader(self, 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, GL_VERTEX_SHADER),
            compileShader(fragment_src, GL_FRAGMENT_SHADER)
        )

        return shader

    def mainLoop(self):
        running = True
        while(running):
            #check events
            for event in pg.event.get():
                if(event.type ==pg.QUIT):
                    running = False

            # refresh screen
            glClear(GL_COLOR_BUFFER_BIT)

            glUseProgram(self.shader)
            glBindVertexArray(self.triangle.vao)
            glDrawArrays(GL_TRIANGLES, 0 , self.triangle.vertex_count)

            pg.display.flip()

            #timing
            self.clock.tick(60)
        self.quit()

    def quit(self):
        self.triangle.destory()
        glDeleteProgram(self.shader)
        pg.quit()

결과

추가설명

VAO: 정점 데이터를 선언, VBO와 연결하는 방법이다. VAO를 사용하면 OpenGL에서 정점 데이터를 효율적으로 관리할 수 있다.

VBO: 정점 데이터를 저장하는 기본 저장소. VBO를 사용하면 그래픽 카드에 정점 데이터를 한 번만 보내고 필요한 경우 언제든지 다시 사용할 수 있다.

VAO와 VBO의 차이점

  • VAO는 정점 데이터의 위치, 색상, 텍스처 좌표 등과 같은 속성을 선언, VBO는 정점 데이터의 실제 값을 저장.
  • VAO는 한 번만 생성하면 되지만 VBO는 여러 번 생성할 수 있다.

'그래픽스' 카테고리의 다른 글

OpenGL with Python 1: Intro and Setup  (0) 2023.10.09