Buffer Objects
Buffer Objects
- Until now, a large number of function calls were needed for drawing graphics primitives (such as polygons, lines, or points)
//...
glColor3f(1.0f, 1.0f, 0.0f);
glBegin(GL_POLYGON);
glTexCoord2f(0.25f, 0.50f); glVertex3f(-1.0f, 1.0f, 1.0f);
glTexCoord2f(0.25f, 0.25f); glVertex3f(-1.0f, -1.0f, 1.0f);
glTexCoord2f(0.50f, 0.25f); glVertex3f(-1.0f, -1.0f, 1.0f);
glTexCoord2f(0.50f, 0.50f); glVertex3f(1.0f, 1.0f, 1.0f);
glEnd();
//...
- Each function call is transferring only as small amount of data to the OpenGL pipeline
- This way of graphics leads to slow processing, because the graphics hardware operations can not be executed in parallel on a large number of data, instead the hardware is constantly waiting for data
- Therefore, since OpenGL >= 3.1 the corresponding functions (glBegin, glVertex, etc.) are only supported in the "Compatibility Profile"
- Instead, vertex data is now transferred in larger blocks, which are stored and managed on the graphics card using Buffer Objects
지금까지는 그래픽 프리미티브(다각형, 선 또는 점)를 그리기 위해 많은 수의 함수 호출이 필요했음.
각 함수 호출은 OpenGL 파이프라인으로 소량의 데이터만 전송함.
이러한 그래픽 방식으로는 처리 속도가 저하됨.이는 다수의 데이터에 대해 그래픽 하드웨어 처리를 병렬로 수행할 수 없고 하드웨어가 항상 데이터를 대기하고 있기 때문임.
따라서 OpenGL >= 3.1 이후 대응하는 함수(glBegin, glVertex 등)는 "호환성 프로파일"에서만 지원됨.
대신 정점 데이터는 더 큰 블록으로 전송되고 버퍼 객체를 사용하여 그래픽 카드에 저장 및 관리.
Vertex Buffer Objects (VBO)
Creating VBOs in OpenGL
- Creating VBOs is usually done before the actual rendering, e.g., in the function init( )
- The procedure is analogous to creating textures and involves three steps:
- Step 1: The function glGenBuffers(1, &bufID) is generating a unique identifier
- Step 2: With glBindBuffer(GL_ARRAY_BUFFER, bufID) a new VBO is created. Possible targets are GL_ARRAY_BUFFER or GL_ELEMENT_ARRAY_BUFFER
- Step 3: The function glBufferData(GL_ARRAY_BUFFER, size, inData, GL_STATIC_DRAW) transfers the vertex data
VBO 생성은 일반적으로 init() 함수와 같이 실제 렌더링 전에 수행됨.
이 절차는 텍스처를 만드는 것과 유사하며 다음 세 단계를 포함함.
1단계 : 함수 glGenBuffers(1, &bufID)가 고유 식별자를 생성함.
2단계 : glBindBuffer(GL_ARRAY_BUFFER, bufID) 를 사용하여 새로운 VBO 가 생성됨. GL_ARRAY_BUFFER 또는 GL_ELEMENT_ARRAY_BUFFER
3단계 : 함수 glBufferData(GL_ARRAY_BUFFER, size, inData, GL_STATIC_DRAW)는 정점 데이터를 전송함.
Activating VBOs in OpenGL
- In order to use a VBO for rendering, it must be activated. This also involves three steps:
- Step 1: Binding the buffer with glBindBuffer(GL_ARRAY_BUFFER, bufID)
- Step 2: Defining a stride and a start address for the memory access
- Step 3: Activating the buffer for a certain purpose
glEnableClientState(GL_VERTEX_ARRAY).
Possible parameters are: GL_VERTEX_ARRAY, GL_COLOR_ARRAY, GL_SECONDARY_COLOR_ARRAY, GL_INDEX_ARRAY, GL_NORMAL_ARRAY, GL_TEXTURE_COORD_ARRAY
렌더링에 VBO를 사용하려면 VBO를 활성화해야 함
1단계 : glBindBuffer(GL_ARRAY_BUFFER, bufID)를 이용하여 버퍼 바인딩
2단계 : 메모리 액세스에 대한 시작 주소 정의
3단계 : 특정 목적을 위해 버퍼 활성화
Rendering using VBOs in OpenGL
- After activating a VBO the rendering of all containing data is initiated with a single function call:
Possible parameters are the same as those of glBegin(…), such as GL_POLYGON, GL_LINE_LOOP, GL_LINES, GL_POINTS, etc.
glDrawArrays(GL_POLYGONS, first, count);
- If the VBO is no longer used, the memory can be released by glDeleteBuffers(1, &bufID)
Example: Rendering a Sphere with a Large Number of Points
// This code example is created for educational purpose
// by Thorsten Thormaehlen (contact: www.thormae.de).
// It is distributed without any warranty.
#include <GL/glew.h>
#include <GL/freeglut.h> // we use glut here as window manager
#define _USE_MATH_DEFINES
#include <math.h>
#include <iostream>
#include <string>
#include <sstream>
#include <vector>
using namespace std;
class Renderer {
public:
float t;
int mode;
unsigned ayimutSegs, polarSegs;
private:
GLuint bufID;
int bufSize;
public:
// constructor
Renderer() : t(0.0), mode(0), bufID(0), bufSize(0), ayimutSegs(1000), polarSegs(1000) {}
//destructor
~Renderer() {
if (bufID != 0) glDeleteBuffers(1, &bufID);
}
public:
void init() {
glEnable(GL_DEPTH_TEST);
// generating VBO input data
std::vector<float> dataIn;
float ayimutStep = 2.0f * float(M_PI) / float(ayimutSegs);
float polarStep = float(M_PI) / float(polarSegs);
float r = 1.0;
bufSize = 0;
for (unsigned m = 0; m < ayimutSegs; m++) {
for (unsigned n = 0; n < polarSegs; n++) {
float phi = ayimutStep * m;
float theta = polarStep * n;
// compute xyz from spherical coordinates
float x = r * sin(theta) * cos(phi);
float y = r * sin(theta) * sin(phi);
float z = r * cos(theta);
dataIn.push_back(x);
dataIn.push_back(y);
dataIn.push_back(z);
bufSize++;
}
}
// generating VBO
glGenBuffers(1, &bufID);
glBindBuffer(GL_ARRAY_BUFFER, bufID);
glBufferData(GL_ARRAY_BUFFER, dataIn.size() * sizeof(float), &dataIn[0], GL_STATIC_DRAW);
}
void resize(int w, int h) {
glViewport(0, 0, w, h);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(30.0, (float)w / (float)h, 0.1, 50.0);
}
void display() {
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
// set camera
gluLookAt(3.5, -1.0, 3.5, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0);
// draw scene
glRotatef(t, 0.0f, 0.0f, 1.0f);
// activating VBO
glBindBuffer(GL_ARRAY_BUFFER, bufID);
int stride = 0;
glVertexPointer(3, GL_FLOAT, stride, NULL);
glEnableClientState(GL_VERTEX_ARRAY);
if (mode == 0) {
glColor3f(1.0f, 1.0f, 1.0f);
}
else {
glColorPointer(3, GL_FLOAT, stride, NULL);
glEnableClientState(GL_COLOR_ARRAY);
}
//render VBO
glDrawArrays(GL_POINTS, 0, bufSize);
glDisableClientState(GL_VERTEX_ARRAY);
glDisableClientState(GL_COLOR_ARRAY);
}
};
//this is a static pointer to a Renderer used in the glut callback functions
static Renderer* renderer;
//glut static callbacks start
static void glutResize(int w, int h)
{
renderer->resize(w, h);
}
static void glutDisplay()
{
renderer->display();
glutSwapBuffers();
glutReportErrors();
}
static void timer(int v)
{
float offset = 0.25f;
renderer->t += offset;
glutDisplay();
glutTimerFunc(unsigned(20), timer, ++v);
}
static void glutKeyboard(unsigned char key, int x, int y) {
bool redraw = false;
std::string modeStr;
switch (key) {
case '1':
if (renderer->mode == 1) renderer->mode = 0;
else renderer->mode = 1;
redraw = true;
break;
case '2':
if (renderer->ayimutSegs < 1000) {
renderer->ayimutSegs *= 10;
renderer->polarSegs *= 10;
}
else {
renderer->ayimutSegs = 10;
renderer->polarSegs = 10;
}
renderer->init();
redraw = true;
break;
}
if (redraw) {
glutDisplay();
}
}
int main(int argc, char** argv)
{
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_DEPTH | GLUT_DOUBLE | GLUT_RGBA);
glutInitWindowPosition(100, 100);
glutInitWindowSize(320, 320);
glutCreateWindow("Press 1 to change color, 2 to increase vertex count");
GLenum err = glewInit();
if (GLEW_OK != err) {
fprintf(stderr, "Glew error: %s\n", glewGetErrorString(err));
}
glutDisplayFunc(glutDisplay);
//glutIdleFunc(glutDisplay);
glutReshapeFunc(glutResize);
glutKeyboardFunc(glutKeyboard);
renderer = new Renderer;
renderer->init();
glutTimerFunc(unsigned(20), timer, 0);
glutMainLoop();
}
Using Vertex Indices
Using Vertex Indices
- When drawing polygonal surfaces, it often happens that vertices are used several times for adjacent polygons
- If the vertices shall be stored only once, polygons can be defined using the indices of the vertices
- To use vertex indices with VBOs, usually two VBOs are defined: one for vertex data and another for index data
- A VBO with index data is generated and activated with glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, bufID)
- The rendering is then carried out with the function
glDrawElements(GL_POLYGON, count, GL_UNSIGNED_INT);
Example: Rendering a Spiky Sphere with Vertex Indices
// This code example is created for educational purpose
// by Thorsten Thormaehlen (contact: www.thormae.de).
// It is distributed without any warranty.
#include <GL/glew.h>
#include <GL/freeglut.h> // we use glut here as window manager
#define _USE_MATH_DEFINES
#include <math.h>
#include <iostream>
#include <string>
#include <sstream>
#include <vector>
using namespace std;
class Renderer {
public:
float t;
int mode;
unsigned ayimutSegs;
unsigned polarSegs;
private:
GLuint vertBufID, indicesBufID;
int vertSize;
public:
// constructor
Renderer() : t(0.0), mode(1), ayimutSegs(10), polarSegs(10),
vertBufID(0), indicesBufID(0), vertSize(0) {}
//destructor
~Renderer() {
if (vertBufID != 0) glDeleteBuffers(1, &vertBufID);
if (indicesBufID != 0) glDeleteBuffers(1, &indicesBufID);
}
public:
void init() {
glEnable(GL_DEPTH_TEST);
recreateVBOs();
}
void recreateVBOs() {
if (vertBufID != 0) glDeleteBuffers(1, &vertBufID);
if (indicesBufID != 0) glDeleteBuffers(1, &indicesBufID);
// generating VBO input data
std::vector<float> vertexData;
std::vector<int> indicesData;
int totalSegs = ayimutSegs * polarSegs;
float ayimutStep = 2.0f * M_PI / float(ayimutSegs);
float polarStep = M_PI / float(polarSegs);
float r = 1.0;
vertSize = 0;
for (unsigned m = 0; m < ayimutSegs; m++) {
for (unsigned n = 0; n < polarSegs; n++) {
float phi = ayimutStep * m;
float theta = polarStep * n;
// random radius
int rval = rand();
r = 1.0f - 0.3f * (fabs(float(rval)) / float(RAND_MAX));
// create xyz from spherical coordinates
float x = r * sin(theta) * cos(phi);
float y = r * sin(theta) * sin(phi);
float z = r * cos(theta);
vertexData.push_back(x);
vertexData.push_back(y);
vertexData.push_back(z);
// create vertex indices
indicesData.push_back(vertSize % totalSegs);
indicesData.push_back((vertSize + ayimutSegs) % totalSegs);
indicesData.push_back((vertSize + ayimutSegs + 1) % totalSegs);
indicesData.push_back((vertSize + 1) % totalSegs);
vertSize++;
}
}
// generating vertex VBO
glGenBuffers(1, &vertBufID);
glBindBuffer(GL_ARRAY_BUFFER, vertBufID);
glBufferData(GL_ARRAY_BUFFER, vertexData.size() * sizeof(float), &vertexData[0], GL_STATIC_DRAW);
// generating index VBO
glGenBuffers(1, &indicesBufID);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indicesBufID);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, indicesData.size() * sizeof(int), &indicesData[0], GL_STATIC_DRAW);
}
void resize(int w, int h) {
glViewport(0, 0, w, h);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(30.0, (float)w / (float)h, 0.1, 50.0);
}
void display() {
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
// set camera
gluLookAt(3.5, -1.0, 3.5, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0);
// draw scene
glRotatef(t, 0.0f, 0.0f, 1.0f);
// activating VBO
glBindBuffer(GL_ARRAY_BUFFER, vertBufID);
int stride = 0;
glVertexPointer(3, GL_FLOAT, stride, NULL);
glEnableClientState(GL_VERTEX_ARRAY);
if (mode == 0) {
glColor3f(1.0f, 1.0f, 1.0f);
}
else {
glColorPointer(3, GL_FLOAT, stride, NULL);
glEnableClientState(GL_COLOR_ARRAY);
}
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indicesBufID);
glDrawElements(GL_QUADS, vertSize * 4, GL_UNSIGNED_INT, NULL);
glDisableClientState(GL_VERTEX_ARRAY);
glDisableClientState(GL_COLOR_ARRAY);
}
};
//this is a static pointer to a Renderer used in the glut callback functions
static Renderer* renderer;
//glut static callbacks start
static void glutResize(int w, int h)
{
renderer->resize(w, h);
}
static void glutDisplay()
{
renderer->display();
glutSwapBuffers();
glutReportErrors();
}
static void timer(int v)
{
float offset = 0.25f;
renderer->t += offset;
glutDisplay();
glutTimerFunc(unsigned(20), timer, ++v);
}
static void glutKeyboard(unsigned char key, int x, int y) {
bool redraw = false;
std::string modeStr;
switch (key) {
case '1':
if (renderer->mode == 1) renderer->mode = 0;
else renderer->mode = 1;
redraw = true;
break;
case '2':
if (renderer->ayimutSegs < 1000) {
renderer->ayimutSegs *= 10;
renderer->polarSegs *= 10;
}
else {
renderer->ayimutSegs = 10;
renderer->polarSegs = 10;
}
renderer->recreateVBOs();
redraw = true;
break;
}
if (redraw) {
glutDisplay();
}
}
int main(int argc, char** argv)
{
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_DEPTH | GLUT_DOUBLE | GLUT_RGBA);
glutInitWindowPosition(100, 100);
glutInitWindowSize(320, 320);
glutCreateWindow("Press 1 to change color, 2 to increase vertex count");
GLenum err = glewInit();
if (GLEW_OK != err) {
fprintf(stderr, "Glew error: %s\n", glewGetErrorString(err));
}
glutDisplayFunc(glutDisplay);
//glutIdleFunc(glutDisplay);
glutReshapeFunc(glutResize);
glutKeyboardFunc(glutKeyboard);
renderer = new Renderer;
renderer->init();
glutTimerFunc(unsigned(20), timer, 0);
glutMainLoop();
}
Dynamic Update of VBOs
Dynamic Uodate of VBOs
- If VBOs should be used in a dynamic scene, it is best for fast display when the vertices are static in the VBOs and the motion is realized only by changing the modelview matrix
- In this case, no new vertex data must be transferred to the GPU
- This is possible, for example, for the animation of a car
- However, it often happens that the vertex data must be changed
- For example, to realize a muscle deformation during the animation of a runner
- In this case, new data must be transferred to the GPU
- Complete :
- An option for a dynamic update is to transfer the complete data again with (아래 코드)
- If this happens often, instead of GL_STATIC_DRAW the parameters GL_DYNAMIC_DRWA should be used
- When the content of the VBOs changes with each new frame, the parameter GL_STREAM_DRAW can be employed
glBufferData(GL_ARRAY_BUFFER, size, data, GL_STATIC_DRAW);
- Partial :
- If only a few data within the VBO is affected, it is probably worth to update the new VBO only partially
- This is done by:
glBufferSubData(GL_ARRAY_BUFFER, offset, size, data);
Example: Dynamic Update of the Spiky Sphere
// This code example is created for educational purpose
// by Thorsten Thormaehlen (contact: www.thormae.de).
// It is distributed without any warranty.
#include <GL/glew.h>
#include <GL/freeglut.h> // we use glut here as window manager
#define _USE_MATH_DEFINES
#include <math.h>
#include <iostream>
#include <string>
#include <sstream>
#include <vector>
using namespace std;
class Renderer {
public:
float t;
int mode;
unsigned ayimutSegs;
unsigned polarSegs;
int frameCount;
int currentTime;
int previousTime;
private:
GLuint vertBufID, indicesBufID;
int vertSize;
public:
// constructor
Renderer() : t(0.0), mode(1), ayimutSegs(8), polarSegs(8),
vertBufID(0), indicesBufID(0), vertSize(0), frameCount(0), previousTime(0) {}
//destructor
~Renderer() {
if (vertBufID != 0) glDeleteBuffers(1, &vertBufID);
if (indicesBufID != 0) glDeleteBuffers(1, &indicesBufID);
}
public:
void init() {
glEnable(GL_DEPTH_TEST);
recreateVBOs();
}
void recreateVBOs() {
if (vertBufID != 0) glDeleteBuffers(1, &vertBufID);
if (indicesBufID != 0) glDeleteBuffers(1, &indicesBufID);
// generating VBO input data
std::vector<float> vertexData;
std::vector<int> indicesData;
int totalSegs = ayimutSegs * polarSegs;
float ayimutStep = 2.0f * float(M_PI) / float(ayimutSegs);
float polarStep = float(M_PI) / float(polarSegs);
float r = 1.0;
vertSize = 0;
for (unsigned m = 0; m < ayimutSegs; m++) {
for (unsigned n = 0; n < polarSegs; n++) {
float phi = ayimutStep * m;
float theta = polarStep * n;
// random radius
int rval = rand();
r = 1.0f - 0.3f * (fabs(float(rval)) / float(RAND_MAX));
// create xyz from spherical coordinates
float x = r * sin(theta) * cos(phi);
float y = r * sin(theta) * sin(phi);
float z = r * cos(theta);
vertexData.push_back(x);
vertexData.push_back(y);
vertexData.push_back(z);
// create vertex indices
indicesData.push_back(vertSize % totalSegs);
indicesData.push_back((vertSize + ayimutSegs) % totalSegs);
indicesData.push_back((vertSize + ayimutSegs + 1) % totalSegs);
indicesData.push_back((vertSize + 1) % totalSegs);
vertSize++;
}
}
// generating vertex VBO
glGenBuffers(1, &vertBufID);
glBindBuffer(GL_ARRAY_BUFFER, vertBufID);
glBufferData(GL_ARRAY_BUFFER, vertexData.size() * sizeof(float), &vertexData[0], GL_STATIC_DRAW);
// generating index VBO
glGenBuffers(1, &indicesBufID);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indicesBufID);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, indicesData.size() * sizeof(int), &indicesData[0], GL_STATIC_DRAW);
}
void updateRandomVBOVertex() {
float ayimutStep = 2.0f * float(M_PI) / float(ayimutSegs);
float polarStep = float(M_PI) / float(polarSegs);
float r = 1.0;
// randomly select point
int mval = rand();
int m = int((ayimutSegs - 1) * (fabs(float(mval)) / float(RAND_MAX)));
int nval = rand();
int n = int((polarSegs - 1) * (fabs(float(nval)) / float(RAND_MAX)));
float phi = ayimutStep * m;
float theta = polarStep * n;
// random radius
int rval = rand();
r = 1.0f - 0.3f * (fabs(float(rval)) / float(RAND_MAX));
// create xyz from spherical coordinates
float x = r * sin(theta) * cos(phi);
float y = r * sin(theta) * sin(phi);
float z = r * cos(theta);
int vertPos = (m * polarSegs + n) * 3;
float newVertexData[3];
newVertexData[0] = x;
newVertexData[1] = y;
newVertexData[2] = z;
// update vertex in VBO
glBindBuffer(GL_ARRAY_BUFFER, vertBufID);
glBufferSubData(GL_ARRAY_BUFFER, vertPos * sizeof(float), 3 * sizeof(float), newVertexData);
}
void resize(int w, int h) {
glViewport(0, 0, w, h);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(30.0, (float)w / (float)h, 0.1, 50.0);
}
void calculateFPS() {
frameCount++;
currentTime = glutGet(GLUT_ELAPSED_TIME);
int timeInterval = currentTime - previousTime;
if (timeInterval > 1000) {
float fps = frameCount / (timeInterval / 1000.0f);
cout << "rendering " << ayimutSegs * polarSegs << " quads at fps=" << fps << " (targetFps = 60)" << endl;
previousTime = currentTime;
frameCount = 0;
}
}
void display() {
// update up to 100 random vertices
for (unsigned i = 0; i < 100 && i < (ayimutSegs / 2); i++) {
updateRandomVBOVertex();
}
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
// set camera
gluLookAt(3.5, -1.0, 3.5, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0);
// draw scene
glRotatef(t, 0.0f, 0.0f, 1.0f);
// activating VBO
glBindBuffer(GL_ARRAY_BUFFER, vertBufID);
int stride = 0;
glVertexPointer(3, GL_FLOAT, stride, NULL);
glEnableClientState(GL_VERTEX_ARRAY);
if (mode == 0) {
glColor3f(1.0f, 1.0f, 1.0f);
}
else {
glColorPointer(3, GL_FLOAT, stride, NULL);
glEnableClientState(GL_COLOR_ARRAY);
}
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indicesBufID);
glDrawElements(GL_QUADS, vertSize * 4, GL_UNSIGNED_INT, NULL);
glDisableClientState(GL_VERTEX_ARRAY);
glDisableClientState(GL_COLOR_ARRAY);
calculateFPS();
}
};
//this is a static pointer to a Renderer used in the glut callback functions
static Renderer* renderer;
//glut static callbacks start
static void glutResize(int w, int h)
{
renderer->resize(w, h);
}
static void glutDisplay()
{
renderer->display();
glutSwapBuffers();
glutReportErrors();
}
static void timer(int v)
{
float offset = 0.25f;
renderer->t += offset;
glutDisplay();
glutTimerFunc(unsigned(16), timer, ++v);
}
static void glutKeyboard(unsigned char key, int x, int y) {
bool redraw = false;
std::string modeStr;
switch (key) {
case '1':
if (renderer->mode == 1) renderer->mode = 0;
else renderer->mode = 1;
redraw = true;
break;
case '2':
if (renderer->ayimutSegs < 512) {
renderer->ayimutSegs *= 4;
renderer->polarSegs *= 4;
}
else {
renderer->ayimutSegs = 8;
renderer->polarSegs = 8;
}
renderer->recreateVBOs();
redraw = true;
break;
}
if (redraw) {
glutDisplay();
}
}
int main(int argc, char** argv)
{
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_DEPTH | GLUT_DOUBLE | GLUT_RGBA);
glutInitWindowPosition(100, 100);
glutInitWindowSize(640, 480);
glutCreateWindow("Press 1 to change color, 2 to increase vertex count");
GLenum err = glewInit();
if (GLEW_OK != err) {
fprintf(stderr, "Glew error: %s\n", glewGetErrorString(err));
}
glutDisplayFunc(glutDisplay);
//glutIdleFunc(glutDisplay);
glutReshapeFunc(glutResize);
glutKeyboardFunc(glutKeyboard);
renderer = new Renderer;
renderer->init();
glutTimerFunc(unsigned(20), timer, 0);
glutMainLoop();
}
'전공 > 컴퓨터 그래픽스' 카테고리의 다른 글
버퍼 오브젝트 (Buffer Objects) (3) (0) | 2023.06.17 |
---|---|
버퍼 오브젝트 (Buffer Objects) (2) (0) | 2023.05.29 |
프레임 버퍼(Frame Buffer Object) (0) | 2023.05.28 |
텍스처 (3)3D 텍스쳐 (0) | 2023.05.28 |
텍스처 (2)텍스쳐 매핑 기법 (0) | 2023.05.20 |
댓글