다각형 그리기

대인공·2022년 7월 29일
0

OpenGL

목록 보기
2/8
post-thumbnail

삼각형 그리기

준비

  • 정점 데이터 준비
  • Vertex Buffer object (VBO) 준비
  • Vertex Buffer object에 정점 데이터 입력 ( CUP메모리상에 있는 데이터를 GPU로 옮기는 작업 )
  • Vertex Array object (VAO) 준비 ( 우리의 정점 데이터의 구조를 알려주는 descriptor object라고도 부른다. )
  • Program, VBO, VAO를 사용하여 그림그리기
  • VBO
  • 정점 데이터를 담은 버퍼 오브젝트
  • 정점에 대한 다양한 데이터를 GPU가 접근 가능한 메모리에 저장해둔다
    ex) position, normal, tangent, color, texture, coordinate ...
  • VAO
  • 정점의 데이터 형식에 대한 정보를 묘사해준다.
    ex) 2차원이냐( x, y ), 3차원이냐( x, y, z )
  • 준비된 정점위치 , src / context.cpp 작성
    bool Context :: Init()
    {
    	float Vertices[] = {
    	-0.5f, -0.5f, 0.0f,
    	0.5f, -0.5f, 0.0f,
    	0.0f, 0.5f, -0.5f,
    };

    3차원 정점 3개이다.

VBO (Vertex Buffer Object)

  • VBO를 담아둘 m_vertexBuffer 선언 , src / context.h 작성

    private:
    	uint32_t m_vertexBuffer;
  • 생성된 VBO와 정점 데이터를 GPU메모리에 복사 , src / context.cpp 작성

    bool Context :: Init()
    {
    	float Vertices[] = {
    	-0.5f, -0.5f, 0.0f,
    	0.5f, -0.5f, 0.0f,
    	0.0f, 0.5f, -0.5f,
      }
      //위에서 작성된 좌표아래에 추가한다.
      
      glGenBuffers( 1, &m_vertexBuffer );
      glBindBuffer( GL_ARRAY_BUFFER, m_vertexBuffer );
      glBufferData( GL_ARRAY_BUFFER, Sizeof(float) * 9(정점의 갯수), vertices, GL_STATIC_DRAW );
    };

    설명

    • glGenBuffers() : 새로운 Buffer Object를 생성해준다.
    • glBindBuffer() : 지금부터 사용할 Buffer Object를 지정한다.
    • GL_ARRAY_BUFFER : 사용할 Buffer Object는 vertex data를 저장할 용도임을 알려줌
    • glBufferData(GL_ARRAY_BUFFER, 데이터의 총크기, 데이터 포인터, '용도') : 지정된 Buffer에 데이터를 복사한다.

      '용도' : 앞 " STAIC | DYNAMIC | STREAM ", 뒤 " DRAW | COPY | READ "의 조합
      ex) GL_STATIC_DRAW, GL_DYNAMIC_DRAW, GL_STATIC_READ ...

      • flag
      1. GL_STATIC_DRAW : 딱 한번만 세팅되고 앞으로 계속 사용할 예정
      2. GL_DYNAMIC_DRAW : 앞으로 데이터가 자주 바뀔 예정
      3. GL_STREAM_DRAW : 딱 한번만 세팅되고 몇번 사용하다 버려질 예정
        -> 용도에 맞는 flag를 지정해야 효율이 올라간다.
    • VBO에 복사한 데이터의 구조

      하나의 VERTEX당 12bute를 차지하고있다
    • 데이터 구조
      - 정점이 총 3개
      - 각 정점의 위치가 기록
      - 위치에 대해서 x, y, z좌표값을 가짐
      - 각 좌표값마다 float 4byte 의 크기를 가짐
      - 첫번째 정점과 두번째 정점간의 간격이 12byte만큼 차이남
      ->VBO가 가진 점점에 대한 구조(layout)를 GPU에게 알려줄 방법이 있어야함 = VAO

VAO (Vertex Array Object)

  • 정점데이터의 구조를 알려주는 오브젝트
    • 데이터의 구조
      - 각 정점은 몇 byte로 구성되어 있는가?
      - 두 정점은 몇 byte만큼 떨어져 있는가?
      - 정점의 0번째 데이터는 어떤 사이즈의 데이터가 몇개 있는 형태인가?
  • VAO와 VBO
  • VAO 담아둘 m_vertexBuffer 선언 , src / context.h 작성
private:
	uint32_t m_vertexArrayObject;
  • VAO생성 및 설정 , src / context.cpp / Init() 작성
	glGenVertexArray( 1, &m_vertexArrayObject );
	glBindVertexArray( m_vertexArrayObject );
    
    // VBO를 만드는곳
    // VAO는 VBO보다 먼저 만든다.
    
    glEnableVertexAttribArray(0);
    glVerTexArrtibPointer( 0, 3, GL_FLOAT, GL_FALSE, sozeof(float) * 3, 0);

설명

  • glGetVertrxArrays() : VAO생성
  • glBindVertexArray() : 지금부터 사용할 VAO설정
  • glEnableVertexAttribArray(n) : 정점 Attribute중 n번째를 사용하도록 설정
  • glVerTexArrtibPointer( n ,size, type, normailzed, stride, offset ) :
    n : 정점의 n번째 Attribute
    size : 해당 Attribute는 몇개의 값으로 구성되어 있는가
    type : 해당 Attribute의 데이터 타입
    normailzed : 0 ~ 1사이의 값인가
    stride : 두 정점간의 데이터 간격 (byte단위)
    offset : 첫 정점의 헤당 Attribute까지의 간격 (byte단위)
  • 구성
  1. VAO Binding
  2. VBO Binding
  3. Vertex Attribute Setting ( vertex attribute를 설정하기전에 VBO가 바인딩 되어 있을것 )

Vertex Shader

  • VAO생성 및 설정 , shader / simple.vs 수정

    layout (location = 0) in vec3 aPos;
    
    void main()
    {
    	  gl_POsition = vec4(aPos, 1.0);
    }

    location = 0 : VAO attribute 0번을 의미

  • 삼각형 설정 , src / context.cpp / Render() 수정

    void Context :: Render()
    {
        glClear(GL_COLOR_BUFFER_BIT);
        
        glUseProgram(m_program->Get());
        glDrawArray(GL_TRIANGLES, 0, 3);
    }

    설명

    • glDrawArray(primitive, offset, count) : 현재 설정된 program, VBO, VAO로 그림을 그린다
      primitive : 그리고자 하는 primitive타입
      offset : 그리고자 하는 첫 정점의 index
      count : 그리려는 정점의 총 갯수

    여러가지 테스트

  1. vertices 내의 값 바꿔보기

  2. GL_TRIANGLES 대신 다른것 넣어보기 (ex. GL_LINE_STRIP)

  3. shader/simple.fs의 fragColor를 이용하여 색상 바꿔보가 (RGBA)

    사각형 그리기

    사각형을 그리려면 삼각형이 두개, 정점이 6개가 필요하지만
    정점 6개중 2개가 서로 중복되어 그려진다.

    • 파란 삼각형의 정점 0, 1, 3
    • 빨간 삼각형의 정점 1, 2, 3
      이런 식으로 정점의 중복을 피하여 사각형을 그릴수 있다.
  • 준비된 정점위치 , src / context.cpp 작성
bool Context :: Init()
{
  	float Vertices[] = {
    0.5f, 0.5f, 0.0f, // Top Right , 0
  	0.5f, -0.5f, 0.0f, // Bottom Right , 1
  	-0.5f, -0.5f, 0.0f, // bottom Left , 2
  	-0.5f, 0.5f, 0.0f, // Top Left , 3
    }
    
    uint32_t indeces[] = {
    0,1,2,
    1,2,3
    }
};

3차원 정점 4개이다.

  • indeces 정점을 모아 하나의삼각형을 구성한다는것을 표기하기 위한 Array

EBO (Element Buffer Object)

  • EBO를 담아둘 m_indexBuffer 선언 , src / context.h 작성
private:
	uint32_t m_indexBuffer;
  • EBO를 버퍼에 담기 , src / context.cpp 작성
bool Context :: Init()
{
  	float Vertices[] = {
  	-0.5f, -0.5f, 0.0f,
  	0.5f, -0.5f, 0.0f,
  	0.0f, 0.5f, -0.5f,
    }
    
    uint32_t indeces[] = {
    0,1,2,
    1,2,3
    }
    
    //위에서 작성된 좌표아래에 추가한다.
    
    glGenBuffers( 1, &m_indexBuffer );
    glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, m_indexBuffer );
    glBufferData( GL_ELEMENT_ARRAY_BUFFER, Sizeof(uint32_t) * 6(배열의 갯수), indeces(배열명), GL_STATIC_DRAW );
};
  • EBO 전달 , src / context.cpp / Render() 작성

    void Context :: Render()
    {
    glClear(...);
    
    glUseProgram(...);
    glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);

    설명

    • glDrawElements( primitive, count, type, pointer / offset ) :
      현재 바인딩된 VAO, VBO, EBO를 바탕으로 그리기
      primitive: 그리고자 하는 primitive타입
      count : 그리고자 하는 EBO내 index의 개수
      type : EBO내 index의 데이터형
      pointer / offset : 그리고자 하는 EBO의 첫 데이터로부터의 오프셋

    다양한 Primitive Type

전체적인 구성

Refactoring

Buffer Class

기능

  • VBO, EBo를 가질수 있음
  • 생성시 정점 데이터 혹은 인덱스 데이터를 제공하면, 해당 데이터를 GPU메모리에 저장한 Buffer Object 생성
  • 메모리 해제시 Buffer Object 제거
  • Buffer Class , src / buffer.h 생성

    #ifndef __BUFFER_H__
    #define __BUFFER_H__
    
    #include "common.h"
    
    CLASS_PTR(Buffer)
    class Buffer
    {
    public:
    	  static BufferUPtr CreateWithData(uint32_t bufferType, uint32_t usage, const* void data, size_t dataSize);
        ~Buffer();
        uint32_t Get() const { return m_buffer; }
        void Bind() const;
        
    private:
    	  Buffer() {}
        bool Init(uint32_t bufferType, uint32_t usage, const* void data, size_t dataSize);
        uint32_t m_buffer { 0 };
        uint32_t m_bufferType { 0 };
        uint32_t m_usage { 0 };
    }
  • Buffer Class , src / buffer.cpp 생성

#include "buffer.h"
  
BufferUPtr Buffer :: CreateWithData(uint32_t bufferType, uint32_t usage, const* void data, size_t dataSize)
{  
  	  auto buffer = BufferUPtr(new Buffer());
  	  if(!buffer->Init(bufferType, usage, data, dataSize)) { return nullptr }
    
  	  return std :: move(buffer);
}
Buffer :: ~Buffer()
{
  	if(m_buffer)
    {
    	glDeleteBuffers(1,&m_buffer)
    }
}

void Buffer :: Bind() const
{
	glBindBuffer(m_buffertype, m_Buffer);
}

void Init(uint32_t bufferType, uint32_t usage, const* void data, size_t dataSize)
{
	m_buffertype = bufferType;
    m_usage = usage;
    glGenBuffer(1, &m_buffer);
    Bind();
    glBufferData(m_bufferType, dataSize, data, usage);
    return true;
  • Buffer 맴버변수 선언 , src / context.h 작성
#include "buffer.h"

CLASS_PTR(Context)
class Context{
...
private:
	...
	BufferUPtr m_vertexBuffer; // 기존의 uint32_t에서 BufferUPtr로 변경해준다.
	BufferUPtr m_indexBuffer; // 기존의 uint32_t에서 BufferUPtr로 변경해준다.
}
  • Program메소드 호출추가 , src / program.h 작성
	~Program();
uint32_t Get() const { return m_program; }
void Use() const; // 추가된 함수
...
}
  • Program메소드 추가 , src / program.cpp 작성
void Program::Use() const //함수 추가
{
    glUseProgram(m_program);
}
  • Context :: Init() 수정 , src / context.cpp 작성
bool Context :: Init()
{
	float vertices[] = {...};
    uint32_t indices[] = {...};
    
    glGenVertexArray(...);
    glBindVertexArray(...);
    
    m_vertexBuffer = Buffer::CreateWithData(GL_ARRAY_BUFFER, GL_STATIC_DRAW, vertices, sizeof(float)*12);
    glEnableVertexAttribArray(...);
    glVertexAttribPointer(...);
    
    m_indexBuffer = Buffer::CreatWithData(GL_ELEMENT_ARRAY_BUFFER, GL_STATIC_DRAW, indices ,sizeof(uint32_t)*6);
	...
}

void Context::Render() 
{
    glClear(GL_COLOR_BUFFER_BIT);
 
    m_program->Use(); // 기존의 호출 변경
    glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
}

대체되는 코드

  • glGenBuffer();
  • glBindBuffer();
  • glbufferData();

=> m_vertexBuffer = Buffer :: CreateWithData() / m_indexBuffer = Buffer :: CreatWithData

  • glUseProgram(m_program->Get());
    => m_program->Use();

빌드 및 실행을 할때는 CMakeList.txt에 src/buffer.h , src/buffer.cpp를 추가, 컴파일 해준다.

Vertex Layout Class

  • Vertex Array Object를 관리하는 클래스이다.
    • VAP생성 및 메모리 해제시 VAO제거
    • vertex attribute 설정 기능 제공
  • Vertex Layout Class , src / vertex_layout.h 생성
#ifndef __VERTEX_LAYOUT_H__
#define __VERTEX_LAYOUT_H__

#include "common.h"

CLASS_PTR(VertexLayout)
class VertexLayout 
{
public:
    static VertexLayoutUPtr Create();
    ~VertexLayout();

    uint32_t Get() const { return m_vertexArrayObject; }
    void Bind() const;
    void SetAttrib(
        uint32_t attribIndex, int count,
        uint32_t type, bool normalized,
        size_t stride, uint64_t offset) const;
    void DisableAttrib(int attribIndex) const;

private:
    VertexLayout() {}
    void Init();
    uint32_t m_vertexArrayObject { 0 };
};

#endif // __VERTEX_LAYOUT_H__
  • Vertex Layout Class , src / vertex_layout.cpp 생성
#include "vertex_layout.h"

VertexLayoutUPtr VertexLayout::Create() 
{
    auto vertexLayout = VertexLayoutUPtr(new VertexLayout());
    vertexLayout->Init();
    return std::move(vertexLayout);
}

VertexLayout::~VertexLayout() 
{
    if (m_vertexArrayObject) {
        glDeleteVertexArrays(1, &m_vertexArrayObject);
    }
}

void VertexLayout::Bind() const 
{
    glBindVertexArray(m_vertexArrayObject);
}

void VertexLayout::SetAttrib(uint32_t attribIndex, int count, 
                             uint32_t type, bool normalized, size_t stride, uint64_t offset) const 
{
    glEnableVertexAttribArray(attribIndex);
    glVertexAttribPointer(attribIndex, count,
        type, normalized, stride, (const void*)offset);
}

void VertexLayout::Init() 
{
    glGenVertexArrays(1, &m_vertexArrayObject);
    Bind();
}
  • Vertex Layout 맴버변수 선언 , src / context.h 작성
#include "vertex_layout.h"

CLASS_PTR(Context)
class Context{
...
private:
	VertexLayoutUPtr m_vertexLayout; // 기존의 uint32_t에서 VertexLayoutUPtr로 수정
    ...
}
  • Context :: Init() 수정 , src / context.cpp 작성
bool Context :: Init()
{
	float vertices[] = {...};
    uint32_t indices[] = {...};
    
	m_vertexLayout= VertexLayout::Create();
    
    m_vertexBuffer = Buffer::CreateWithData(GL_ARRAY_BUFFER, GL_STATIC_DRAW, vertices, sizeof(float)*12);
	
    m_vertexLayout->SetAttrib(0, 3, GL_FLOAT, GL_FALSE, sizeof(float) * 3, 0);
    
    m_indexBuffer = Buffer::CreatWithData(GL_ELEMENT_ARRAY_BUFFER, GL_STATIC_DRAW, indices ,sizeof(uint32_t)*6);
	...
}

void Context::Render() 
{
    glClear(GL_COLOR_BUFFER_BIT);
 
    m_program->Use(); // 기존의 호출 변경
    glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
}

대체되는 코드

  • glGetVertexArrays()
  • glBindVertexArray()

=> m_vertexLayout= VertexLayout::Create();

  • glEnableVertexAttribArray()
  • glVertexArrtibPointer()

=> m_indexBuffer = Buffer::CreatWithData();

빌드 및 실행을 할때는 CMakeList.txt에 src/vertex_layout.h , src/vertex_layout.cpp를 추가, 컴파일 해준다.

정리

  • OpenGL을 이용하여 삼각형을 그리기 위한 방법
  1. Shader Object 생성 / 소스 컴파일
  2. Program Object 생성 / Shader Link
  3. VBO : 정점 데이터를 GPU메모리 상에 위치시킨 Object
  4. EBO : 인덱스 데이터를 GPU메모리 상에 위치시킨 Object
  5. VAO : VBO의 구조에 대한 description, 바인딩된 VBO, EBO기억
  6. 대부분의 OpenGL object는 glBind...()라는 함수를 이용하여 지금부터 사용할 Object를 선택한 뒤 이용함

    ex)

    • glBindBuffer()
    • glBindVertexArray()
    • glBindTexture()
    • glBindFramebuffer()
    • ...
profile
이제 막 시작하는 유니티 클라이언트

0개의 댓글

관련 채용 정보