[OpenGL][VAO, VBO] ์ผ๊ฐํ๊ทธ๋ฆฌ๊ธฐ
Open GL es ์ด๋ก ์ ๊ณต๋ถํ๋ฉด์ ๋น์ฃผ์ผ ์คํฌ๋ฆฝํธ๋ฅผ ํตํด Open GL์ค์ต์ ํ๊ณ ์๋ค.
์ด๋ฒ์๋ Open GL ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ๊ฐ์ ธ์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ ๋ด ํจ์๋ก ์ผ๊ฐํ์ ๊ทธ๋ฆฌ๋ ค๊ณ ํ๋ค.
์ฒซ๋ฒ์งธ๋ก๋ GLUT ๋ผ์ด๋ธ๋ฌ๋ฆฌ ๋ด ํจ์๋ฅผ ์ฌ์ฉํด ๋ฒํ
์ค์ ์ขํ, ์์ ๋ฒกํฐ๊ฐ์ ์ง์ ํด์ฃผ์ด
๊ฐ๋จํ๊ฒ ๊ทธ๋ ค๋ณด๋ ์ฝ๋๋ก ์๋์ ๊ฐ์ด ์์ฑํ๋ค.
#include <glut.h>
void Display()
{
//ํ๋ฉด ์ง์ฐ๊ณ ๋ฒํผ ํด๋ฆฌ์ด
glClear(GL_COLOR_BUFFER_BIT);
//์ผ๊ฐํ ๊ทธ๋ฆฌ๊ธฐ ์์ ------------------------------------------------------------
glBegin(GL_TRIANGLES);
//๋ฒํ
์ค ์์, ์์น ์ง์
glColor3f(1, 0, 0);
glVertex2f(-0.6, -0.75);
glColor3f(0, 1, 0);
glVertex2f(0.6, -0.75);
glColor3f(0, 0, 1);
glVertex2f(0, 0.75);
//์ผ๊ฐํ๊ทธ๋ฆฌ๊ธฐ ๋ -----------------------------------------------------------------
glEnd();
// ํ๋ฉด์ ์ผ๊ฐํ ํ์
glFlush();
}
int main(int argc, char** argv)
{
//OpenGL ์๋์ฐ ์ด๊ธฐํ
glutInit(&argc, argv);
//RGB์ฌ์ฉํ๋ ์๋์ฐ ํ๊ฐ ์์ฑ
glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB);
//์๋์ฐ ์์น, ํฌ๊ธฐ, ์ด๋ฆ ์ง์
glutInitWindowPosition(80, 80);
glutInitWindowSize(400, 300);
glutCreateWindow("OpenGL Triangle");
//Display ํจ์ ํธ์ถ
glutDisplayFunc(Display);
glutMainLoop();
}
Disply() ํจ์ ๋ด์์ ์ผ๊ฐํ ์ขํ์ ์์ ๋ฒกํฐ๊ฐ์ ์ง์ ํ๋ค. ์ด๋ ์ผ๊ฐํ์ ๊ทธ๋ฆฌ๋ ์ฝ๋๋ glBegin() ๋ค, glEnd() ์์์ ์์ฑํ์ฌ์ผ ํ๋ค.
ํด๋น ์ฝ๋๊ฐ ์คํ๋๊ธฐ์ํด์๋ GLUT ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ๋ถ๋ฌ์์ผํ๋๋ฐ, ์ด๋ฅผ ์ํด ๋๋ glut.dllํ์ผ์ ํด๋น cppํ์ผ๊ณผ ๊ฐ์ ๊ฒฝ๋ก์์ ๋ฃ์ด๋์๋ค.
๋๋ 400*300 ํฌ๊ธฐ์ ์๋์ฐ์์ ๊ฐ ๋ฒํ
์ค์ ์ขํ์ ์์๊ฐ์ด ๋ค๋ฅธ ์ผ๊ฐํ์ ๊ทธ๋ ค์ฃผ์๋ค.
๊ฒฐ๊ณผ๋ ์๋์ ๊ฐ๋ค.
๋๋ฒ์งธ ๋ฐฉ๋ฒ์ GLFW ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ฌ์ฉํ๋ ๋ฐฉ์์ด๋ค.
์ฌ๊ธฐ์ GLUT ๊ณผ GLFW ์ ์ฐจ์ด์ ๋ํด ์๊ธฐ๋ ์๋ฌธ์ด ์์ํ
๋ฐ ๊ทธ์ ๋ํ ๋ต์ chat gpt์ ๋๋ต์ ์ฐจ์ฉํ๋๋กํ๊ฒ ๋ค.
GLFW๋ OpenGL์ ์ํ C ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ก, ์๋์ฐ ์์ฑ, ์ ๋ ฅ ์ฒ๋ฆฌ, OpenGL ์ปจํ ์คํธ ๊ด๋ฆฌ ๋ฑ์ ๋ด๋นํฉ๋๋ค. GLFW๋ ํฌ๋ก์ค ํ๋ซํผ์ ์ง์ํ๋ฉฐ, ์๋์ฐ ์์คํ ๊ณผ์ ์ํธ ์์ฉ์ ์ฝ๊ฒ ํ ์ ์๋๋ก ๋์์ค๋๋ค. ๋ํ ๋ฉํฐ์ค๋ ๋ฉ ํ๊ฒฝ์์ ์์ ์ ์ผ๋ก ๋์ํ๋ฉฐ, ์๋์ฐ ์ด๋ฒคํธ ์ฒ๋ฆฌ, ํค๋ณด๋ ๋ฐ ๋ง์ฐ์ค ์ ๋ ฅ ์ฒ๋ฆฌ ๋ฑ ๋ค์ํ ๊ธฐ๋ฅ์ ์ ๊ณตํฉ๋๋ค.
๋ฐ๋ฉด, GLUT๋ OpenGL Utility Toolkit์ ์ฝ์๋ก, C/C++์ ์ํ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ ๋๋ค. GLUT๋ ๋จ์ํ๋ API๋ฅผ ์ ๊ณตํ์ฌ OpenGL ํ๋ก๊ทธ๋๋ฐ์ ํธ๋ฆฌํ๊ฒ ๋ง๋ค์ด์ค๋๋ค. ์ฃผ๋ก ๊ฐ๋จํ ๊ทธ๋ํฝ ์ ํ๋ฆฌ์ผ์ด์ ๊ฐ๋ฐ์ ์ฌ์ฉ๋๋ฉฐ, ์๋์ฐ ์์ฑ, ์ด๋ฒคํธ ์ฒ๋ฆฌ, ๊ธฐ๋ณธ์ ์ธ ์ ๋ ฅ ์ฒ๋ฆฌ ๋ฑ์ ์ง์ํฉ๋๋ค. ํ์ง๋ง ๋ฉํฐ์ค๋ ๋ ํ๊ฒฝ์์๋ ์ ํ์ ์ธ ๊ธฐ๋ฅ์ ๊ฐ์ง๊ณ ์์ต๋๋ค.
์์ฝํ์๋ฉด GLFW๋ ๋น๊ต์ ์ ๊ตํ๊ณ ๊ฐ๋ ฅํ ๊ธฐ๋ฅ์ ์ ๊ณตํ๋ OpenGL ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ด๊ณ , GLUT๋ ๋น๊ต์ ๊ฐ๋จํ ๊ทธ๋ํฝ ์ดํ๋ฆฌ์ผ์ด์
์ ๋น ๋ฅด๊ฒ ๊ฐ๋ฐํ๊ธฐ ์ฉ์ดํ OpenGL ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ธ ๊ฒ์ด๋ค.
์ฌ์ค๋งํด๋ณด์๋ฉด ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ ์ด๋ค๊ฒ์ ์ฌ์ฉํ๋ ์ผ๊ฐํ์ ๊ทธ๋ฆฌ๋๋ฐ์๋ ๋ฐฉ์์ด ํฌ๊ฒ ์ฐจ์ด๋์ง๋ ์์๊ฒ์ด๋ค.
๋ค๋ง, ์๋ ๋ฐฉ๋ฒ์ VAO์ VBO๋ฅผ ์ฌ์ฉํด ์ผ๊ฐํ์ ๊ทธ๋ฆฌ๊ณ , ํ์ผ์ ์ฝ์ด์ ์์ด๋๋ฅผ ์ง์ ์ปดํ์ผ ํ๋ ํ์์ผ๋ก ์์ฑ๋์๋ค.
์ธ๋ถ ์ฌํญ์ ์ฃผ์์ ์ฐธ๊ณ ํ๊ธธ ๋ฐ๋๋ค.
//API ํค๋ ํฌํจ
#include <GL/glew.h>
#include <GLFW/glfw3.h>
#include <stdlib.h>
#include <stdio.h>
#include <fstream>
#include <string>
//shader ID ๋ฒํธ ์ ์ฅ์ฉ ๋ณ์ ์ ์ธ
GLuint VBO, VAO, shader;
// ์
ฐ์ด๋ ํ์ผ๋ด์ฉ ์ฝ์ด์ค๊ธฐ -> ๋ฌธ์์ด ๋ฐํ
std::string ReadFile(const char* filePath)
{
std::string content;
std::ifstream fileStream(filePath, std::ios::in);
while (!fileStream.is_open())
{
printf("%s ํ์ผ ์ฝ๊ธฐ๋ฅผ ์คํจํ์ต๋๋ค.\n", filePath);
return "";
}
std::string line = "";
while (!fileStream.eof())
{
std::getline(fileStream, line);
content.append(line + "\n");
}
fileStream.close();
return content;
}
//์์ด๋ ์ปดํ์ผ -> ID ๋ฐํ
GLuint AddShader(const char* shaderCode, GLenum shaderType)
{
GLuint new_shader = glCreateShader(shaderType);
const GLchar* code[1];
code[0] = shaderCode;
glShaderSource(new_shader, 1, code, NULL); //์์ด๋ ์ฝ๋ -> ์์ด๋์ ์ฐ๊ฒฐ
GLint result = 0;
GLchar err_log[1024] = { 0 };
glCompileShader(new_shader); //์์ด๋ ์ปดํ์ผ
glGetShaderiv(new_shader, GL_COMPILE_STATUS, &result); //์ปดํ์ผ ์ฑ๊ณต bool
if (!result)
{
glGetShaderInfoLog(new_shader, sizeof(err_log), NULL, err_log);
printf("Error compiling the %d shader: '%s'\n", shaderType, err_log);
return 0;
}
return new_shader;
}
// ๋ฒํ
์ค, ํ๋๊ทธ๋จผํธ ์์ด๋๋ฅผ ์ปดํ์ผ -> ์์ด๋ ํ๋ก๊ทธ๋จ ๋ง๋๋ ํจ์
// vsCode = ๋ฒํ
์ค ์์ด๋ ์ฝ๋, fsCode = ํ๋๊ทธ๋จผํธ ์์ด๋ ์ฝ๋ -> ๋ฌธ์์ด
void CompileShader(const char* vsCode, const char* fsCode)
{
GLuint vs, fs;
shader = glCreateProgram(); //์์ด๋ ํ๋ก๊ทธ๋จ ์์ฑ ํ ID๋ฐํ
if (!shader)
{
printf("Error: Cannot create shader program.");
return;
}
vs = AddShader(vsCode, GL_VERTEX_SHADER); //ID์์ฑ -> ์ปดํ์ผ
fs = AddShader(fsCode, GL_FRAGMENT_SHADER);
glAttachShader(shader, vs); //๋ฒํ
์ค ์์ด๋ ํ๋ก๊ทธ๋จ ์ฒจ๋ถ
glAttachShader(shader, fs); // ํ๋๊ทธ๋จผํธ ์์ด๋ ํ๋ก๊ทธ๋จ ์ฒจ๋ถ
GLint result = 0;
GLchar err_log[1024] = { 0 };
glLinkProgram(shader); // ์์ด๋ ์ฝ๋๋ฅผ ๋ช
๋ น์ด๋ก ๋ณํ , GPU์คํํ์ผ ์์ฑ
glGetProgramiv(shader, GL_LINK_STATUS, &result); //๋งํฌ ์ฑ๊ณต์ฌ๋ถ ํ์ธ
if (!result)
{
glGetProgramInfoLog(shader, sizeof(err_log), NULL, err_log);
printf("Error linking program: '%s'\n", err_log);
return;
}
}
//๋ฒํ
์ค, ํ๋๊ทธ๋จผํธ ์์ด๋ ๊ฒฝ๋ก๋ฅผ ์ฝ์ด์ ์์ด๋ ํ๋ก๊ทธ๋จ์ ๋ง๋๋ ํจ์
void CreateShaderProgramFromFiles(const char* vsPath, const char* fsPath)
{
std::string vsFile = ReadFile(vsPath);
std::string fsFile = ReadFile(fsPath);
const char* vsCode = vsFile.c_str();
const char* fsCode = fsFile.c_str();
CompileShader(vsCode, fsCode);
}
//์ผ๊ฐํ์ ์์ฑํ๋ ํจ์
void CreateTriangle()
{
//๋ฒํ
์ค ์ขํ ์ ์
GLfloat vertices[] = {
-1.0f, -1.0f, 0.0f,
1.0f, -1.0f, 0.0f,
0.0f, 1.0f, 0.0f
};
//VAO๋ ํ๋ ์ด์์ VBO๋ฅผ ๊ฐ๋ ๊ฐ์ฒด, ๋ชจ๋ ๋ฐ์ดํฐ๋ฅผ ํฌํจ, ID๊ฐ์ง
glGenVertexArrays(1, &VAO); //๋ฒํ
์ค ๋ฐ์ดํฐ ์ํ๋ฅผ์ ์ฅ, ์ฝ๊ธฐ์ํ ๊ฐ์ด๋
glBindVertexArray(VAO); //VAO๋ฅผ ๋ฐ์ธ๋ฉ
//VBO๋ ํ ์ ์ ์ ์์น, ์์, ๋
ธ๋ฉ์ ๋ณด๋ฅผ ๊ฐ์ง, ๊ณ ์ ID
glGenBuffers(1, &VBO); // VBO์์ฑ - ๋ฒํ
์ค ๋ฒํผ ์ค๋ธ์ ํธ
glBindBuffer(GL_ARRAY_BUFFER, VBO); //GL_ARRAY_BUFFER(openGL context)๋ก VBO๋ฅผ ๋ฐ์ธ๋ฉ
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0); //GPU๊ฐ VBO๋ฅผ ์ฝ์ ์ ์๋๋ก ์ ๋ณด ์ ๋ฌ
glEnableVertexAttribArray(0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);
int main(void)
{
// GLFW ๋ผ์ด๋ธ๋ฌ๋ฆฌ ์ด๊ธฐํ
if (!glfwInit())
exit(EXIT_FAILURE);
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4); // OpenGL ๋ฒ์ (x.y ์ค x)
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 6); // 4.6์ ๋ง์ถฐ ์คํ(x.y ์ค y)
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); // ๋ ์ด์ ์ฐ์ด์ง ์๋ ํ์ ํธํ ๊ธฐ๋ฅ๋ค ์๋ฌ ์ฒ๋ฆฌ
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); // ์์ ํธํ์ฑ ์ง์
//GLFW์ฐฝ ์์ฑ
GLFWwindow* window = glfwCreateWindow(1080, 720, "title", NULL, NULL);
if (!window)
{
glfwTerminate();
exit(EXIT_FAILURE);
}
//์ปจํ
์คํธ ์ปค๋ ํธ ์๋์ฐ ์์ฑ
glfwMakeContextCurrent(window);
//ํ๋ ์ ๋ฒํผ ํฌ๊ธฐ๋ฅผ ๊ฐ์ ธ์์ ๋ทฐํฌํธ ์ค์
int framebuf_width, framebuf_height;
glfwGetFramebufferSize(window, &framebuf_width, &framebuf_height);
glViewport(0, 0, framebuf_width, framebuf_height);
//Glew ์ด๊ธฐํ
if (glewInit() != GLEW_OK)
{
glfwTerminate();
exit(EXIT_FAILURE);
}
//์ผ๊ฐํ ์์ฑ
CreateTriangle();
CreateShaderProgramFromFiles("shader.vert", "shader.frag");
//๋ ๋๋ง๋ฃจํ
while (!glfwWindowShouldClose(window))
{
//
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
glUseProgram(shader);
glBindVertexArray(VAO);
glDrawArrays(GL_TRIANGLES, 0, 3);
glBindVertexArray(0);
glUseProgram(0);
glfwSwapBuffers(window);
glfwPollEvents();
}
glfwTerminate();
return 0;
}
OpenGL์ ํ๋์ State Machine์ผ๋ก ์ค๋ช
๋๋๋ฐ, state๋ฅผ ํ๋ฒ ์ค์ ํ๋ฉด ํด๋น state์ ๋ฐ๋ผ ์๋ํ๊ณ ์ด state๊ฐ ๋ฐ๋์ง ์๋ ํ ๊ทธ ์ํ๊ฐ ๊ณ์ ์ ์ง๋๊ธฐ ๋๋ฌธ์ด๋ค.
๋ ๊ฐ๋จํ๊ฒ ๋งํด๋ณด์๋ฉด ๊ฐ์ฅ ์ต๊ทผ์ ์ค์ ๋ state๋ก ๋์๊ฐ๋ ๊ธฐ๊ณ๋ผ๊ณ ์ดํดํ๋ฉด ๋๋ค.
๋ฐ๋ผ์, OpenGL object๋ ์ด state๋ฅผ ๊ฐ๋ ๊ตฌ์กฐ์ฒด์ ๊ฐ๊ณ ์ ์ฝ๋์์ Open GL Object๋ GL_ARRAY_BUFFER ๋ผ๋OpenGL context์ ๋ฐ์ธ๋ ๋์ด context์ ์ฐ๊ฒฐ๋ state๋ฅผ current state๋ก ์ธ์ํ๊ณ , ์ด๋ฅผ ๋ฐํ์ผ๋ก ์๋ํ๊ฒ ๋๋ค.
๊ทธ๋ผ ํด๋น ๊ฒฝ์ฐ์๋ ์ด๋ค OpenGL oject๊ฐ ์ฌ์ฉ๋์์๊น?
์ฒซ๋ฒ์งธ๋ก๋ VBO๋ผ๋ OpenGL๊ฐ์ฒด๋ค.
VBO( Vertex Buffer Object ) ๋ ๋ฒํ
์ค์ ์ ์ ๋ฐ์ดํฐ๋ ์์น, ์์, ํ
์ค์ฒ ์ขํ ๋ฑ์ ์์ฑ์ ํฌํจํ๊ณ ์๋ ๊ฐ์ฒด์ด๋ค.
VBO๋ฅผ ์ ์ฌ์ฉํ๋๊ณ ๋ฌป๋๋ค๋ฉด ์ฑ๋ฅํฅ์์ด ๋ชฉ์ ์ด๋ค.
์ ์ ๋ฐ์ดํฐ๊ฐ ๋ง์์ง ์๋ก GPU๊ฐ ๊ณ์ํด์ ๊ผญ์ง์ ์ ๊ทธ๋ฆฌ๋๊ฒ์ ํจ์จ์ ์ด์ง ์๋ค. ๋ฐ๋ผ์ CPU์์ ๊ทธ๋ํฝ ๋ฉ๋ชจ๋ฆฌ๋ก ์ ์ฅ๋์ด ์ํ๋ ๋์ GPU๊ฐ ๊ทธ๋ฆด ์ ์๋๋ก ํ๊ฒ๋๋๋ฐ ์ด๋ ๊ฒ ์ ์ ๋ฐ์ดํฐ๋ฅผ ์์ฑ, ์ ์ฅํ๋ ๋จ๊ณ๋ฅผ Vertex Specification์ด๋ผ๊ณ ํ๋ค. VBO๋ ์ด ๊ณผ์ ์์ ์ ์ ๋ฐ์ดํฐ๊ฐ ์ ์ฅ๋๋ OpenGL๊ฐ์ฒด๋ก์ ์ฌ์ฉ๋๋ค.
์ ์ฝ๋๋ฅผ ๋ณด๋ฉด VBO๋๋จ์ํ ๋ฐฐ์ด์ ์ง๋์ง ์๋๋ค. ๊ทธ๋ผ GPU๋ ์ด ๋ฐฐ์ด์ ์ด๋ป๊ฒ ์ผ๊ฐํ์ผ๋ก ์ฝ์ ์ ์๋ ๊ฒ์ผ๊น?
//๋ฒํ
์ค ์ขํ ์ ์
GLfloat vertices[] = {
-1.0f, -1.0f, 0.0f,
1.0f, -1.0f, 0.0f,
0.0f, 1.0f, 0.0f
};
์ด ๋ ์ฐ๋ฆฌ๋ Attribute์ ๋ํด ์์์ผํ๋ค.
Vertex Attribute๋ VBO๊ฐ ํฌํจํ๊ณ ์๋ ์ ์ ๋ฐ์ดํฐ๊ฐ ์ด๋ค ํ์์ผ๋ก ์ ์ฅ๋์ด์๋์ง๋ฅผ GPU๊ฐ ์ ์ ์๋๋ก ๋ํ๋ด๋ ๊ฒ์ผ๋ก Attribute๋ฅผ ์ฐ๋์์ผ์ฃผ์ด์ผ GPU๊ฐ ์ผ๊ฐํ์ ์ฌ๋ฐ๋ฅด๊ฒ ๊ทธ๋ฆด ์ ์๋ค.
์ ์ฝ๋์์๋ glVertexAttribPointer() ํจ์๋ฅผ ํตํด ๋ฒํ
์ค ๋ฐ์ดํฐ์ ๋ํ ์ ๋ณด๋ฅผ ์ ๋ฌํ๊ณ ์๋ค.
์ด๋ ๊ฒ ์ ์ฝ๋์์ VBO๋ Gen -> Bind -> Data ์์์๋ก ์ฌ์ฉ ๋๊ณ ์๋ค.
-
๋๋ฒ์งธ๋ก ์ค๋ช
ํ OpenGL Object๋ VAO์ด๋ค.
VAO( Vertex Array Object ) ๋ ์ ๊ณผ์ ์์ VBO๋ฅผ ํตํด ๋ฐ์ดํฐ๋ฅผ ๋ฐ์ธ๋, ์ ์ฅํ๊ณ attribute๋ฅผ ์ค์ ํ๋ ๋ชจ๋ ์์
์ ํ๋์ state๋ก ๋ฌถ์ด์ OpenGL Object์ ์ ์ฅํ๋ ์ญํ ์ ํ๋ ๊ฐ์ฒด์ด๋ค.
์์ ๋งํ๋ฏ ์์
์ ํ๋๋ก ๋ฌถ์ state๋ฅผ current state๋ก ๋ง๋ค์ด ์์
์ ํ๋ฒ๋ง ํ๊ฒ ํ๋ค.
VAO๋ฅผ ์ฌ์ฉํ๋ ๊ณผ์ ์ Gen -> Bind ์ ์์๋ก ์ ์ํ ์ ์๊ณ
VAO๋ฅผ ์์ฑํ ๋ค OpenGL context์ ๋ฐ์ธ๋ ํด์ค ๋๋ง๋ค VAO์ ๊ณผ์ ์ ์ ์ฅํ๊ฒ ๋๋ค.
์ฝ๋๋ฅผ ์์ฑํ๋ฉด์ ๋ฒํ
์ค์์ด๋์ ํ๋๊ทธ๋จผํธ ์์ด๋, VBO, VAO.. ๊ทธ ์ธ์๋ ์์ํ ์ฉ์ด๊ฐ ๋ง์ด๋์
ํ๋ฆ์ ์ดํดํ๋๋ฐ ๋ง์ ์๊ฐ์ ์ด ๊ฒ ๊ฐ๋ค.
ํนํ, VBO์ VAO๋ ๋ฒํ
์ค ๋ฒํผ, ์ธ๋ฑ์ค๋ฒํผ์์ ๊ด๊ณ์ ๋ฎ์์๋ค๊ณ ์ฐฉ์คํด
๋์ ๊ฐ๋
์ ํท๊ฐ๋ คํ๊ณค ํ๋๋ฐ ์ด๋ฒ๊ธฐํ์ ๋ ๊ฐ๋
์ ๋ํ ์ ์๋ฅผ ์ฌ๋ฐ๋ฅด๊ฒ ์ ์ ์์๋ค.
์ค๋ ๊ณต๋ถ ๋ !
