I’m new to OpenGL 4. All the tutorials I’ve seen draw one object (usually a triangle) in the beginning. I’m having trouble understanding how VAO’s and VBO’s relate to an object. I want to draw two different objects: a spaceship (a triangle) and an asteroid(an octagon). I’ve figured out a way and was wondering if this is the correct way as I go further in my OpenGL adventures. I created one VAO and two VBO’s (asteroid_buffer_object and ship_buffer_object). Then inside InitializeVertexBuffer() I’m generating the two buffer objects and binding them. Then in display() I’m again binding the asteroid_buffer_object, drawing it, and then binding ship_buffer_object and drawing it. So I’m wondering if this is correct or am I doing something redundant?
#include <algorithm>
#include <string>
#include <vector>
#include <cstdio>
#include <iostream>
#include <cmath>
#include <GL/glew.h>
#include <GL/freeglut.h>
using namespace std;
GLuint CreateShader(GLenum eShaderType, const std::string &strShaderFile)
{
GLuint shader = glCreateShader(eShaderType);
const char *strFileData = strShaderFile.c_str();
glShaderSource(shader, 1, &strFileData, NULL);
glCompileShader(shader);
GLint status;
glGetShaderiv(shader, GL_COMPILE_STATUS, &status);
if (status == GL_FALSE)
{
GLint infoLogLength;
glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLogLength);
GLchar *strInfoLog = new GLchar[infoLogLength + 1];
glGetShaderInfoLog(shader, infoLogLength, NULL, strInfoLog);
const char *strShaderType = NULL;
switch(eShaderType)
{
case GL_VERTEX_SHADER: strShaderType = "vertex"; break;
case GL_GEOMETRY_SHADER: strShaderType = "geometry"; break;
case GL_FRAGMENT_SHADER: strShaderType = "fragment"; break;
}
fprintf(stderr, "Compile failure in %s shader:
%s
", strShaderType, strInfoLog);
delete[] strInfoLog;
}
return shader;
}
GLuint CreateProgram(const std::vector<GLuint> &shaderList)
{
GLuint program = glCreateProgram();
for(size_t iLoop = 0; iLoop < shaderList.size(); iLoop++)
glAttachShader(program, shaderList[iLoop]);
glLinkProgram(program);
GLint status;
glGetProgramiv (program, GL_LINK_STATUS, &status);
if (status == GL_FALSE)
{
GLint infoLogLength;
glGetProgramiv(program, GL_INFO_LOG_LENGTH, &infoLogLength);
GLchar *strInfoLog = new GLchar[infoLogLength + 1];
glGetProgramInfoLog(program, infoLogLength, NULL, strInfoLog);
fprintf(stderr, "Linker failure: %s
", strInfoLog);
delete[] strInfoLog;
}
for(size_t iLoop = 0; iLoop < shaderList.size(); iLoop++)
glDetachShader(program, shaderList[iLoop]);
return program;
}
GLuint theProgram;
const std::string strVertexShader(
"#version 330
"
"layout(location = 0) in vec4 position;
"
"void main()
"
"{
"
" gl_Position = position;
"
"}
"
);
const std::string strFragmentShader(
"#version 330
"
"out vec4 outputColor;
"
"void main()
"
"{
"
" outputColor = vec4(1.0f, 1.0f, 1.0f, 1.0f);
"
"}
"
);
void InitializeProgram()
{
std::vector<GLuint> shaderList;
shaderList.push_back(CreateShader(GL_VERTEX_SHADER, strVertexShader));
shaderList.push_back(CreateShader(GL_FRAGMENT_SHADER, strFragmentShader));
theProgram = CreateProgram(shaderList);
std::for_each(shaderList.begin(), shaderList.end(), glDeleteShader);
}
const int num_asteroid_vertices = 8;
const int num_ship_vertices = 3;
GLfloat asteroid_vertices[num_asteroid_vertices][2];
void set_asteroid_vertices()
{
GLfloat angle = 0.0;
GLfloat r = 1.0;
for(int i=0; i < num_asteroid_vertices; i++)
{
angle = i * 2*M_PI/num_asteroid_vertices;
asteroid_vertices[i][0] = r*cos(angle);
asteroid_vertices[i][1] = r*sin(angle);
cout << r*cos(angle) << ", ";
cout << r*sin(angle) << endl;
}
}
GLfloat ship_vertices[][2] = {
{ -0.05f, -0.05f },
{ 0.05f, -0.05f },
{ 0.00f, 0.05f }
};
GLuint asteroid_buffer_object;
GLuint ship_buffer_object;
GLuint vao;
void InitializeVertexBuffer()
{
glGenBuffers(1, &asteroid_buffer_object);
glBindBuffer(GL_ARRAY_BUFFER, asteroid_buffer_object);
glBufferData(GL_ARRAY_BUFFER, sizeof(asteroid_vertices), asteroid_vertices, GL_STATIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glGenBuffers(1, &ship_buffer_object);
glBindBuffer(GL_ARRAY_BUFFER, ship_buffer_object);
glBufferData(GL_ARRAY_BUFFER, sizeof(ship_vertices), ship_vertices, GL_STATIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, 0);
}
//Called after the window and OpenGL are initialized. Called exactly once, before the main loop.
void init()
{
set_asteroid_vertices();
InitializeProgram();
InitializeVertexBuffer();
glGenVertexArrays(1, &vao);
glBindVertexArray(vao);
}
//Called to update the display.
//You should call glutSwapBuffers after all of your rendering to display what you rendered.
//If you need continuous updates of the screen, call glutPostRedisplay() at the end of the function.
void display()
{
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
glClear(GL_COLOR_BUFFER_BIT);
glUseProgram(theProgram);
glBindBuffer(GL_ARRAY_BUFFER, asteroid_buffer_object);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, 0);
glDrawArrays(GL_LINE_LOOP, 0, num_asteroid_vertices);
glDisableVertexAttribArray(0);
glUseProgram(0);
glUseProgram(theProgram);
glBindBuffer(GL_ARRAY_BUFFER, ship_buffer_object);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, 0);
glDrawArrays(GL_LINE_LOOP, 0, num_ship_vertices);
glDisableVertexAttribArray(0);
glutSwapBuffers();
}
//Called whenever the window is resized. The new window size is given, in pixels.
//This is an opportunity to call glViewport or glScissor to keep up with the change in size.
void reshape (int w, int h)
{
glViewport(0, 0, (GLsizei) w, (GLsizei) h);
}
int main(int argc, char** argv)
{
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_RGBA);
glutInitWindowSize(1000, 1000);
glutInitContextVersion(4, 3);
glutInitContextProfile(GLUT_CORE_PROFILE);
glutCreateWindow(argv[0]);
if (glewInit()) {
cerr << "Unable to initialize GLEW ... exiting" << endl;
exit(EXIT_FAILURE);
}
init();
glutDisplayFunc(display);
glutMainLoop();
}