- 정점 데이터 준비
- 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 )
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를 담아둘 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
- GL_STATIC_DRAW : 딱 한번만 세팅되고 앞으로 계속 사용할 예정
- GL_DYNAMIC_DRAW : 앞으로 데이터가 자주 바뀔 예정
- GL_STREAM_DRAW : 딱 한번만 세팅되고 몇번 사용하다 버려질 예정
-> 용도에 맞는 flag를 지정해야 효율이 올라간다.
- VBO에 복사한 데이터의 구조
하나의 VERTEX당 12bute를 차지하고있다
- 데이터 구조
- 정점이 총 3개
- 각 정점의 위치가 기록
- 위치에 대해서 x, y, z좌표값을 가짐
- 각 좌표값마다 float 4byte 의 크기를 가짐
- 첫번째 정점과 두번째 정점간의 간격이 12byte만큼 차이남
->VBO가 가진 점점에 대한 구조(layout)를 GPU에게 알려줄 방법이 있어야함 = VAO
- 데이터의 구조
- 각 정점은 몇 byte로 구성되어 있는가?
- 두 정점은 몇 byte만큼 떨어져 있는가?
- 정점의 0번째 데이터는 어떤 사이즈의 데이터가 몇개 있는 형태인가?
private:
uint32_t m_vertexArrayObject;
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단위)
- 구성
- VAO Binding
- VBO Binding
- Vertex Attribute Setting ( vertex attribute를 설정하기전에 VBO가 바인딩 되어 있을것 )
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 : 그리려는 정점의 총 갯수
vertices 내의 값 바꿔보기
GL_TRIANGLES 대신 다른것 넣어보기 (ex. GL_LINE_STRIP)
shader/simple.fs의 fragColor를 이용하여 색상 바꿔보가 (RGBA)
사각형을 그리려면 삼각형이 두개, 정점이 6개가 필요하지만
정점 6개중 2개가 서로 중복되어 그려진다.
- 파란 삼각형의 정점 0, 1, 3
- 빨간 삼각형의 정점 1, 2, 3
이런 식으로 정점의 중복을 피하여 사각형을 그릴수 있다.
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
private:
uint32_t m_indexBuffer;
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
기능
- 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;
#include "buffer.h"
CLASS_PTR(Context)
class Context{
...
private:
...
BufferUPtr m_vertexBuffer; // 기존의 uint32_t에서 BufferUPtr로 변경해준다.
BufferUPtr m_indexBuffer; // 기존의 uint32_t에서 BufferUPtr로 변경해준다.
}
~Program();
uint32_t Get() const { return m_program; }
void Use() const; // 추가된 함수
...
}
void Program::Use() const //함수 추가
{
glUseProgram(m_program);
}
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 Array Object를 관리하는 클래스이다.
- VAP생성 및 메모리 해제시 VAO제거
- vertex attribute 설정 기능 제공
#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__
#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();
}
#include "vertex_layout.h"
CLASS_PTR(Context)
class Context{
...
private:
VertexLayoutUPtr m_vertexLayout; // 기존의 uint32_t에서 VertexLayoutUPtr로 수정
...
}
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을 이용하여 삼각형을 그리기 위한 방법
- Shader Object 생성 / 소스 컴파일
- Program Object 생성 / Shader Link
- VBO : 정점 데이터를 GPU메모리 상에 위치시킨 Object
- EBO : 인덱스 데이터를 GPU메모리 상에 위치시킨 Object
- VAO : VBO의 구조에 대한 description, 바인딩된 VBO, EBO기억
- 대부분의 OpenGL object는 glBind...()라는 함수를 이용하여 지금부터 사용할 Object를 선택한 뒤 이용함
ex)
- glBindBuffer()
- glBindVertexArray()
- glBindTexture()
- glBindFramebuffer()
- ...