I am creating a 3D cube from an array made out of vertices and colours. I would like to separate these colours and vertices into their separate arrays and render the cube with the right colors. But I do not know how to properly create the VBO so the program doesn't crash.
So this is my Cube class that holds vertices,
private float[] vertices = {
//Position //Colours
// VO
-0.5f, 0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 1.0f, //Bottom right
// V1
-0.5f, -0.5f, 0.5f, 0.0f, 0.5f, 0.0f, 1.0f, //Top Left
// V2
0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 0.5f, 1.0f, //Top Right
// V3
0.5f, 0.5f, 0.5f, 0.0f, 0.5f, 0.5f, 1.0f, //Bottom left
// V4
-0.5f, 0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 1.0f,
// V5
0.5f, 0.5f, -0.5f, 0.0f, 0.5f, 0.0f, 1.0f,
// V6
-0.5f, -0.5f, -0.5f, 0.0f, 0.0f, 0.5f, 1.0f,
// V7
0.5f, -0.5f, -0.5f, 0.0f, 0.5f, 0.5f, 1.0f,
};
private int[] triangles = {
// Front face
0, 1, 3, 3, 1, 2,
// Top Face
4, 0, 3, 5, 4, 3,
// Right face
3, 2, 7, 5, 3, 7,
// Left face
6, 1, 0, 6, 0, 4,
// Bottom face
2, 1, 6, 2, 6, 7,
// Back face
7, 6, 4, 7, 4, 5,
};
And this is how I want my arrays to be seperated (Same class as above)
private float[] vertices = {
//Position
// VO
-0.5f, 0.5f, 0.5f,
// V1
-0.5f, -0.5f, 0.5f,
// V2
0.5f, -0.5f, 0.5f,
// V3
0.5f, 0.5f, 0.5f,
// V4
-0.5f, 0.5f, -0.5f,
// V5
0.5f, 0.5f, -0.5f,
// V6
-0.5f, -0.5f, -0.5f,
// V7
0.5f, -0.5f, -0.5f,
};
private float[] colours = {
//Colours
// VO
0.5f, 0.0f, 0.0f, 1.0f,
// V1
0.0f, 0.5f, 0.0f, 1.0f,
// V2
0.0f, 0.0f, 0.5f, 1.0f,
// V3
0.0f, 0.5f, 0.5f, 1.0f,
// V4
0.5f, 0.0f, 0.0f, 1.0f,
// V5
0.0f, 0.5f, 0.0f, 1.0f,
// V6
0.0f, 0.0f, 0.5f, 1.0f,
// V7
0.0f, 0.5f, 0.5f, 1.0f,
};
private int[] triangles = {
// Front face
0, 1, 3, 3, 1, 2,
// Top Face
4, 0, 3, 5, 4, 3,
// Right face
3, 2, 7, 5, 3, 7,
// Left face
6, 1, 0, 6, 0, 4,
// Bottom face
2, 1, 6, 2, 6, 7,
// Back face
7, 6, 4, 7, 4, 5,
};
So this is my Mesh class where all the rendering stuff gets procesed. My question is how can I apply the colours array into this class ? I can only render he cube with the right colors only if the vertices class contains the vertices position and colors, not if they are seperate.
public void Init(){
/*
float aspectRation = (float) width / height;
projectionMatrix = new Matrix4f().perspective(FOV, aspectRation, nearPlane, farPlane);
*/
//COMPILE AND LINK SHADERS
//************
// Vertex Shader
//************
vertexID = glCreateShader(GL_VERTEX_SHADER); //Load shader type
glShaderSource(vertexID, vertexShaderSrc); // Pass shader source to GPU
glCompileShader(vertexID); // Compile shader
//Error check in compilation process
int success = glGetShaderi(vertexID,GL_COMPILE_STATUS);
if(success == GL_FALSE){
int lenght = glGetShaderi(vertexID, GL_INFO_LOG_LENGTH);
System.out.println("ERROR: 'defaultShader.glsl: \n\t Vertex shader compilation failed !");
System.out.println(glGetShaderInfoLog(vertexID,lenght));
}
//************
// Fragment Shader
//************
fragmentID = glCreateShader(GL_FRAGMENT_SHADER); //Load shader type
glShaderSource(fragmentID, fragmentShaderSrc); // Pass shader source to GPU
glCompileShader(fragmentID); // Compile shader
//Error check in compilation process
success = glGetShaderi(fragmentID,GL_COMPILE_STATUS);
if(success == GL_FALSE){
int lenght = glGetShaderi(fragmentID, GL_INFO_LOG_LENGTH);
System.out.println("ERROR: 'defaultShader.glsl: \n\t Vertex shader compilation failed !");
System.out.println(glGetShaderInfoLog(fragmentID,lenght));
}
//************
// Link shaders and Check for errors
//************
shaderProgram = glCreateProgram();
glAttachShader(shaderProgram, vertexID);
glAttachShader(shaderProgram, fragmentID);
glLinkProgram(shaderProgram);
//Check for linking errors
success = glGetProgrami(shaderProgram, GL_LINK_STATUS);
if(success == GL_FALSE){
int lenght = glGetProgrami(shaderProgram, GL_INFO_LOG_LENGTH);
System.out.println("ERROR: 'defaultShader.glsl:' \n\t Linking of shaders failed !");
System.out.println(glGetProgramInfoLog(fragmentID,lenght));
assert false : "";
}
int uniformLocation = glGetUniformLocation(shaderProgram,"projectionMatrix");
uniforms.put("projectionMatrix",uniformLocation);
int uniformPosLocation = glGetUniformLocation(shaderProgram,"worldMatrix");
uniformsPosition.put("worldMatrix",uniformPosLocation);
//************
// Generate VAO, VBO and EBO and send them to GPU
//************
// GENERATE VAO
VAO_ID = glGenVertexArrays();
glBindVertexArray(VAO_ID);
//Create float buffer of vertices
FloatBuffer vertexBuffer = MemoryUtil.memAllocFloat(vertices.length);
vertexBuffer.put(vertices).flip();
// GENERATE VBO and upload VertexBuffer
VBO_ID = glGenBuffers();
glBindBuffer(GL_ARRAY_BUFFER, VBO_ID);
glBufferData(GL_ARRAY_BUFFER, vertexBuffer, GL_STATIC_DRAW);
memFree(vertexBuffer);
//Create Indices and upload them
IntBuffer elementBuffer = MemoryUtil.memAllocInt(triangles.length);
elementBuffer.put(triangles).flip();
//Create EBO
EBO_ID = glGenBuffers();
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO_ID);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, elementBuffer, GL_STATIC_DRAW);
//Create vertex attribute pointers
int positionSize = 3;
int colorSize = 4;
int floatSizeInBytes = 4;
int vertexSizeInBytes = (positionSize + colorSize) * floatSizeInBytes;
glVertexAttribPointer(0, positionSize, GL_FLOAT, false, vertexSizeInBytes, 0);
glEnableVertexAttribArray(0);
glVertexAttribPointer(1, colorSize, GL_FLOAT, false, vertexSizeInBytes, positionSize * floatSizeInBytes);
glEnableVertexAttribArray(1);
// glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
}
public void Render(){
//Bind shader program
glUseProgram(shaderProgram);
SetUniform("projectionMatrix", projectionMatrix);
SetUniform2("worldMatrix", worldMatrix);
//Bind VAO currently in use
glBindVertexArray(VAO_ID);
//Enable vertex atribute pointers
glEnableVertexAttribArray(0);
glEnableVertexAttribArray(1);
//Draw triangles
glDrawElements(GL_TRIANGLES,triangles.length, GL_UNSIGNED_INT,0);
//Unbind everything
glDisableVertexAttribArray(0);
glDisableVertexAttribArray(1);
glBindVertexArray(0);
glUseProgram(0);
}
From what I understand about OpenGL, it looks like as if you are closing the current buffers being send to the GPU before it can receive colour data. (I might be completely wrong about this)
I tried this aproach on my project, so you may have to fidle little bit to make it work for your project, but the aproach should be the same.
With this you should be able to use the seperated arrays for color, triangles and vertices
//************
// Generate VAO, VBO and EBO and send them to GPU
//************
// GENERATE VAO
VAO_ID = glGenVertexArrays();
glBindVertexArray(VAO_ID);
//POSITION VBO
// GENERATE VBO and upload VertexBuffer
VBO_ID = glGenBuffers();
//Create float buffer of vertices
FloatBuffer vertexBuffer = MemoryUtil.memAllocFloat(vertices.length);
vertexBuffer.put(vertices).flip();
glBindBuffer(GL_ARRAY_BUFFER, VBO_ID);
glBufferData(GL_ARRAY_BUFFER, vertexBuffer, GL_STATIC_DRAW);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 3, GL_FLOAT, false, 0, 0);
//COLOR VBO
//GENERATE VBO and upload colourBuffer
colourVBO_ID = glGenBuffers();
FloatBuffer colourBuffer = MemoryUtil.memAllocFloat(colours.length);
colourBuffer.put(colours).flip();
glBindBuffer(GL_ARRAY_BUFFER, colourVBO_ID);
glBufferData(GL_ARRAY_BUFFER, colourBuffer, GL_STATIC_DRAW);
glEnableVertexAttribArray(1);
glVertexAttribPointer(1, 4, GL_FLOAT, false, 0, 0);
//TRIANGLE VBO
//Create Indices and upload them
EBO_ID = glGenBuffers();
IntBuffer elementBuffer = MemoryUtil.memAllocInt(triangles.length);
elementBuffer.put(triangles).flip();
//Create EBO
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO_ID);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, elementBuffer, GL_STATIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER,0);
glBindVertexArray(0);
// glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
memFree(vertexBuffer);
memFree(colourBuffer);
memFree(elementBuffer);
Related
I have taken code for two projects. One being the code for creating a cube and another is the code for creating a pyramid. I am now trying to render both of the objects in OpenGL which I have done the problem is the objects are attached to one another. I have added some code heading towards rendering them separately, however I am now stuck where my cube is only showing 3 of the triangles used to create it and the whole pyramid shows. Yet the objects are still attached to one another. Any help or guidance?
#include <iostream> // cout, cerr
#include <cstdlib> // EXIT_FAILURE
#include <GL/glew.h> // GLEW library
#include <GLFW/glfw3.h> // GLFW library
// GLM Math Header inclusions
#include <glm/glm.hpp>
#include <glm/gtx/transform.hpp>
#include <glm/gtc/type_ptr.hpp>
using namespace std; // Standard namespace
using glm::vec3;
using glm::mat4;
/*Shader program Macro*/
#ifndef GLSL
#define GLSL(Version, Source) "#version " #Version " core \n" #Source
#endif
// Unnamed namespace
namespace
{
const char* const WINDOW_TITLE = "3D Scene Troubleshooting"; // Macro for window title
// Variables for window width and height
const int WINDOW_WIDTH = 800;
const int WINDOW_HEIGHT = 600;
// Stores the GL data relative to a given mesh
struct GLMesh
{
GLuint vao; // Handle for the vertex array object 1
GLuint vao2; // Handle for the vertex array object 2
GLuint vbos[2]; // Handles for the vertex buffer objects 1
GLuint vbos2[2]; // Handles for the vertex buffer objects 2
GLuint cubeIndices; // Number of cube indices of the mesh
GLuint pyramidIndices; // Number of pyramid indices of the mesh
};
// Main GLFW window
GLFWwindow* gWindow = nullptr;
// Triangle mesh data
GLMesh gMesh;
// Shader program
GLuint gProgramId;
}
/* User-defined Function prototypes to:
* initialize the program, set the window size,
* redraw graphics on the window when resized,
* and render graphics on the screen
*/
bool UInitialize(int, char* [], GLFWwindow** window);
void UResizeWindow(GLFWwindow* window, int width, int height);
void UProcessInput(GLFWwindow* window);
void UCreateMesh(GLMesh& mesh);
void UDestroyMesh(GLMesh& mesh);
void URender();
bool UCreateShaderProgram(const char* vtxShaderSource, const char* fragShaderSource, GLuint& programId);
void UDestroyShaderProgram(GLuint programId);
/* Vertex Shader Source Code*/
const GLchar* vertexShaderSource = GLSL(440,
layout(location = 0) in vec3 position; // Vertex data from Vertex Attrib Pointer 0
layout(location = 1) in vec4 color; // Color data from Vertex Attrib Pointer 1
out vec4 vertexColor; // variable to transfer color data to the fragment shader
//Global variables for the transform matrices
uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;
void main()
{
gl_Position = projection * view * model * vec4(position, 1.0f); // transforms vertices to clip coordinates
vertexColor = color; // references incoming color data
}
);
/* Fragment Shader Source Code*/
const GLchar* fragmentShaderSource = GLSL(440,
in vec4 vertexColor; // Variable to hold incoming color data from vertex shader
out vec4 fragmentColor;
void main()
{
fragmentColor = vec4(vertexColor);
}
);
int main(int argc, char* argv[])
{
if (!UInitialize(argc, argv, &gWindow))
return EXIT_FAILURE;
// Create the mesh
UCreateMesh(gMesh); // Calls the function to create the Vertex Buffer Object
// Create the shader program
if (!UCreateShaderProgram(vertexShaderSource, fragmentShaderSource, gProgramId))
return EXIT_FAILURE;
// Sets the background color of the window to black (it will be implicitely used by glClear)
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
// render loop
// -----------
while (!glfwWindowShouldClose(gWindow))
{
// input
// -----
UProcessInput(gWindow);
// Render this frame
URender();
glfwPollEvents();
}
// Release mesh data
UDestroyMesh(gMesh);
// Release shader program
UDestroyShaderProgram(gProgramId);
exit(EXIT_SUCCESS); // Terminates the program successfully
}
// Initialize GLFW, GLEW, and create a window
bool UInitialize(int argc, char* argv[], GLFWwindow** window)
{
// GLFW: initialize and configure
// ------------------------------
glfwInit();
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 4);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
#ifdef __APPLE__
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
#endif
// GLFW: window creation
// ---------------------
* window = glfwCreateWindow(WINDOW_WIDTH, WINDOW_HEIGHT, "3D Scene TroubleShooting", NULL, NULL);
if (*window == NULL)
{
std::cout << "Failed to create GLFW window" << std::endl;
glfwTerminate();
return false;
}
glfwMakeContextCurrent(*window);
glfwSetFramebufferSizeCallback(*window, UResizeWindow);
// GLEW: initialize
// ----------------
// Note: if using GLEW version 1.13 or earlier
glewExperimental = GL_TRUE;
GLenum GlewInitResult = glewInit();
if (GLEW_OK != GlewInitResult)
{
std::cerr << glewGetErrorString(GlewInitResult) << std::endl;
return false;
}
// Displays GPU OpenGL version
cout << "INFO: OpenGL Version: " << glGetString(GL_VERSION) << endl;
return true;
}
// process all input: query GLFW whether relevant keys are pressed/released this frame and react accordingly
void UProcessInput(GLFWwindow* window)
{
if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
glfwSetWindowShouldClose(window, true);
}
// glfw: whenever the window size changed (by OS or user resize) this callback function
executes
void UResizeWindow(GLFWwindow* window, int width, int height)
{
glViewport(0, 0, width, height);
}
// Functioned called to render a frame
void URender()
{
// Enable z-depth
glEnable(GL_DEPTH_TEST);
// Clear the frame and z buffers
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// 1. Scales the object by 2
glm::mat4 scale = glm::scale(glm::vec3(2.0f, 2.0f, 2.0f));
// 2. Rotates shape by 15 degrees in the x axis
glm::mat4 rotation = glm::rotate(45.0f, glm::vec3(1.0, 1.0f, 1.0f));
// 3. Place object at the origin
glm::mat4 translation = glm::translate(glm::vec3(0.5f, -0.3f, -0.1f));
// Model matrix: transformations are applied right-to-left order
glm::mat4 model = translation * rotation * scale;
// Transforms the camera: move the camera back (z axis)
glm::mat4 view = glm::translate(glm::vec3(-2.6f, 2.9f, -5.0f));
// Creates a orthographic projection
glm::mat4 projection = glm::ortho(-5.0f, 5.0f, -5.0f, 5.0f, 0.1f, 100.0f);
// Set the shader to be used
glUseProgram(gProgramId);
// Retrieves and passes transform matrices to the Shader program
GLint modelLoc = glGetUniformLocation(gProgramId, "model");
GLint viewLoc = glGetUniformLocation(gProgramId, "view");
GLint projLoc = glGetUniformLocation(gProgramId, "projection");
glUniformMatrix4fv(modelLoc, 1, GL_FALSE, glm::value_ptr(model));
glUniformMatrix4fv(viewLoc, 1, GL_FALSE, glm::value_ptr(view));
glUniformMatrix4fv(projLoc, 1, GL_FALSE, glm::value_ptr(projection));
// Activate the VBOs contained within the mesh's VAO
glBindVertexArray(gMesh.vao);
glBindVertexArray(gMesh.vao2);
// Draws the CUBE
glDrawElements(GL_TRIANGLES, gMesh.cubeIndices, GL_UNSIGNED_SHORT, NULL); // Draws the triangle
glDrawArrays(GL_TRIANGLES, 0, 24);
// Draws the PYRAMID
glDrawElements(GL_TRIANGLES, gMesh.pyramidIndices, GL_UNSIGNED_SHORT, NULL); // Draws the triangle
glDrawArrays(GL_TRIANGLES, 0, 54);
// glfw: swap buffers and poll IO events (keys pressed/released, mouse moved etc.)
glfwSwapBuffers(gWindow); // Flips the the back buffer with the front buffer every frame.
}
void UCylinder(GLUquadric* qobj, GLdouble baseRadius, GLdouble topRadius, GLdouble height, GLint slices, GLint stacks)
{
GLUquadricObj* quadratic;
quadratic = gluNewQuadric();
gluCylinder(quadratic, 0.3f, 0.3f, 3.0f, 32, 32);
glDrawElements(GL_TRIANGLES, gMesh.cubeIndices, GL_UNSIGNED_SHORT, NULL);
}
// Implements the UCreateMesh function
void UCreateMesh(GLMesh& mesh)
{
// Position and Color data
GLfloat verts[] = {
// Vertex Positions // Colors (r,g,b,a)
0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, // Top Right Vertex 0 (effects top right and bottom right top sides of cube)
0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, // Bottom Right corner Vertex 1
-0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, // Bottom center of cube Vertex 2 (effects bottom left and bottomr right sides of cube)
-0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, // Center of cube Vertex 3 (effects top, bottom left and bottom right sides of cube)
0.5f, -0.5f, -1.0f, 0.0f, 0.0f, 0.0f, 1.0f, // Bottomside left of cube Vertex 4 (doesn't effect cube color at all with current orientation)
0.5f, 0.5f, -1.0f, 1.0f, 0.0f, 0.0f, 1.0f, // Top center of cube Vertex 5
-0.5f, 0.5f, -1.0f, 1.0f, 0.0f, 0.0f, 1.0f, // Top left corner cube Vertex 6 (effects both top and bottom left sides of cube)
-0.5f, -0.5f, -1.0f, 1.0f, 0.0f, 0.0f, 1.0f, // Bottom left of cube Vertex 7
// Vertex Positions // Colors (r,g,b,a)
-0.5f, -0.5f, -0.5f, 1.0f, 0.0f, 0.0f, 1.0f, // Vertex 8
0.5f, -0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 1.0f, // Vertex 9
0.0f, 0.5f, 0.0f, 0.0f, 0.0f, 1.0f, 1.0f, // Vertex 10
-0.5f, -0.5f, 0.5f, 1.0f, 1.0f, 0.0f, 1.0f, // Vertex 11
0.5f, -0.5f, 0.5f, 0.0f, 1.0f, 1.0f, 1.0f, // Vertex 12
0.0f, 0.5f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f, // Vertex 13
-0.5f, -0.5f, -0.5f, 0.0f, 0.0f, 1.0f, 1.0f, // Vertex 14
-0.5f, -0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 1.0f, // Vertex 15
0.0f, 0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, // Vertex 16
0.5f, -0.5f, -0.5f, 1.0f, 0.0f, 1.0f, 1.0f, // Vertex 17
0.5f, -0.5f, 0.5f, 0.0f, 1.0f, 1.0f, 1.0f, // Vertex 18
0.0f, 0.5f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f, // Vertex 19
0.5f, -0.5f, -0.5f, 1.0f, 0.0f, 0.0f, 1.0f, // Vertex 20
0.5f, -0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 1.0f, // Vertex 21
0.0f, 0.5f, 0.0f, 0.0f, 0.0f, 1.0f, 1.0f, // Vertex 22
-0.5f, -0.5f, 0.5f, 1.0f, 1.0f, 0.0f, 1.0f, // Vertex 23
0.5f, -0.5f, -0.5f, 0.0f, 1.0f, 1.0f, 1.0f, // Vertex 24
0.0f, 0.5f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f, // Vertex 25
};
// Index data to share position data
GLushort cubeIndices[] = {
0, 1, 3, // Triangle 1
1, 2, 3, // Triangle 2
0, 1, 4, // Triangle 3
0, 4, 5, // Triangle 4
0, 5, 6, // Triangle 5
0, 3, 6, // Triangle 6
4, 5, 6, // Triangle 7
4, 6, 7, // Triangle 8
2, 3, 6, // Triangle 9
2, 6, 7, // Triangle 10
1, 4, 7, // Triangle 11
1, 2, 7, // Triangle 12
};
GLushort pyramidIndices[] = {
8, 9, 10, // Triangle 1
11, 12, 13, // Triangle 2
14, 15, 16, // Triangle 3
17, 18, 19, // Triangle 4
20, 21, 22, // Triangle 5
23, 24, 25 // Triangle 6
};
const GLuint floatsPerVertex = 3;
const GLuint floatsPerColor = 4;
// For CUBE
glGenVertexArrays(1, &mesh.vao); // we can also generate multiple VAOs or buffers at the same time
glBindVertexArray(mesh.vao);
// For PYRAMID
glGenVertexArrays(1, &mesh.vao2);
glBindVertexArray(mesh.vao2);
// Create 2 buffers: first one for the vertex data; second one for the indices for the CUBE
glGenBuffers(2, mesh.vbos);
glBindBuffer(GL_ARRAY_BUFFER, mesh.vbos[0]); // Activates the buffer for CUBE
glBufferData(GL_ARRAY_BUFFER, sizeof(verts), verts, GL_STATIC_DRAW); // Sends vertex or coordinate data to the GPU for CUBE
// Create 2 buffers: first one for the vertex data; second one for the indices for the PYRAMID
glGenBuffers(2, mesh.vbos2);
glBindBuffer(GL_ARRAY_BUFFER, mesh.vbos2[0]); // Activates the buffer for PYRAMID
glBufferData(GL_ARRAY_BUFFER, sizeof(verts), verts, GL_STATIC_DRAW); // Sends vertex or coordinate data to the GPU for PYRAMID
// For CUBE
mesh.cubeIndices = sizeof(cubeIndices) / sizeof(cubeIndices[0]);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mesh.vbos[1]);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(cubeIndices), cubeIndices, GL_STATIC_DRAW);
// For PYRAMID
mesh.pyramidIndices = sizeof(pyramidIndices) / sizeof(pyramidIndices[0]);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mesh.vbos2[1]);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(pyramidIndices), pyramidIndices, GL_STATIC_DRAW);
// Strides between vertex coordinates is 6 (x, y, z, r, g, b, a). A tightly packed stride is 0.
GLint stride = sizeof(float) * (floatsPerVertex + floatsPerColor);// The number of floats before each
// Create Vertex Attribute Pointers
glVertexAttribPointer(0, floatsPerVertex, GL_FLOAT, GL_FALSE, stride, 0);
glEnableVertexAttribArray(0);
glVertexAttribPointer(1, floatsPerColor, GL_FLOAT, GL_FALSE, stride, (char*)(sizeof(float) * floatsPerVertex));
glEnableVertexAttribArray(1);
}
void UDestroyMesh(GLMesh& mesh)
{
// For CUBE
glDeleteVertexArrays(1, &mesh.vao);
glDeleteBuffers(2, mesh.vbos);
// For PYRAMID
glDeleteVertexArrays(1, &mesh.vao2);
glDeleteBuffers(2, mesh.vbos2);
}
// Implements the UCreateShaders function
bool UCreateShaderProgram(const char* vtxShaderSource, const char* fragShaderSource, GLuint& programId)
{
// Compilation and linkage error reporting
int success = 0;
char infoLog[512];
// Create a Shader program object.
programId = glCreateProgram();
// Create the vertex and fragment shader objects
GLuint vertexShaderId = glCreateShader(GL_VERTEX_SHADER);
GLuint fragmentShaderId = glCreateShader(GL_FRAGMENT_SHADER);
// Retrive the shader source
glShaderSource(vertexShaderId, 1, &vtxShaderSource, NULL);
glShaderSource(fragmentShaderId, 1, &fragShaderSource, NULL);
// Compile the vertex shader, and print compilation errors (if any)
glCompileShader(vertexShaderId); // compile the vertex shader
// check for shader compile errors
glGetShaderiv(vertexShaderId, GL_COMPILE_STATUS, &success);
if (!success)
{
glGetShaderInfoLog(vertexShaderId, 512, NULL, infoLog);
std::cout << "ERROR::SHADER::VERTEX::COMPILATION_FAILED\n" << infoLog << std::endl;
return false;
}
glCompileShader(fragmentShaderId); // compile the fragment shader
// check for shader compile errors
glGetShaderiv(fragmentShaderId, GL_COMPILE_STATUS, &success);
if (!success)
{
glGetShaderInfoLog(fragmentShaderId, sizeof(infoLog), NULL, infoLog);
std::cout << "ERROR::SHADER::FRAGMENT::COMPILATION_FAILED\n" << infoLog << std::endl;
return false;
}
// Attached compiled shaders to the shader program
glAttachShader(programId, vertexShaderId);
glAttachShader(programId, fragmentShaderId);
glLinkProgram(programId); // links the shader program
// check for linking errors
glGetProgramiv(programId, GL_LINK_STATUS, &success);
if (!success)
{
glGetProgramInfoLog(programId, sizeof(infoLog), NULL, infoLog);
std::cout << "ERROR::SHADER::PROGRAM::LINKING_FAILED\n" << infoLog << std::endl;
return false;
}
glUseProgram(programId); // Uses the shader program
return true;
}
void UDestroyShaderProgram(GLuint programId)
{
glDeleteProgram(programId);
}
See Vertex Specification. You cannot specify 2 vertex array objects at the same time. You have to do this in a row.
The Vertex Array Binding is a global state. Only one VAO can be bound at a time.
When calling OpenGL instructions like glVertexAttribPointer, glEnableVertexAttribArray and glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,...)`, the state of the currently bound Vertex Array Object is changed. Note that different VAOs can use the same data buffers.
void UCreateMesh(GLMesh& mesh)
{
// [...]
glGenBuffers(1, mesh.vbos);
glGenBuffers(2, mesh.vbos2);
// 1 Vertex Buffer for both objects
glBindBuffer(GL_ARRAY_BUFFER, mesh.vbos[0]);
glBufferData(GL_ARRAY_BUFFER, sizeof(verts), verts, GL_STATIC_DRAW);
// Strides between vertex coordinates is 6 (x, y, z, r, g, b, a). A tightly packed stride is 0.
GLint stride = sizeof(float) * (floatsPerVertex + floatsPerColor);// The number of floats before each
// CUBE
glGenVertexArrays(1, &mesh.vao); // we can also generate multiple VAOs or buffers at the same time
glBindVertexArray(mesh.vao);
// Create Vertex Attribute Pointers
glVertexAttribPointer(0, floatsPerVertex, GL_FLOAT, GL_FALSE, stride, 0);
glEnableVertexAttribArray(0);
glVertexAttribPointer(1, floatsPerColor, GL_FLOAT, GL_FALSE, stride, (char*)(sizeof(float) * floatsPerVertex));
glEnableVertexAttribArray(1);
mesh.cubeIndices = sizeof(cubeIndices) / sizeof(cubeIndices[0]);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mesh.vbos[1]);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(cubeIndices), cubeIndices, GL_STATIC_DRAW);
// PYRAMID
glGenVertexArrays(1, &mesh.vao2);
glBindVertexArray(mesh.vao2);
// Create Vertex Attribute Pointers
glVertexAttribPointer(0, floatsPerVertex, GL_FLOAT, GL_FALSE, stride, 0);
glEnableVertexAttribArray(0);
glVertexAttribPointer(1, floatsPerColor, GL_FLOAT, GL_FALSE, stride, (char*)(sizeof(float) * floatsPerVertex));
glEnableVertexAttribArray(1);
mesh.pyramidIndices = sizeof(pyramidIndices) / sizeof(pyramidIndices[0]);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mesh.vbos2[1]);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(pyramidIndices), pyramidIndices, GL_STATIC_DRAW);
}
Finally you can draw the meshes one after the other. The draw call uses the data from the currently bound Vertex Array Object. As already mentioned, only one VAO can be bound at a time:
glBindVertexArray(gMesh.vao);
glDrawElements(GL_TRIANGLES, gMesh.cubeIndices, GL_UNSIGNED_SHORT, NULL);
glBindVertexArray(gMesh.vao2);
glDrawElements(GL_TRIANGLES, gMesh.pyramidIndices, GL_UNSIGNED_SHORT, NULL);
As the title says, I'm trying to render a cube using the code below (plus a custom Shader class and the actual shaders omitted for brevity) to render a tilted cube turning on the Y axis.
The problem is with the texture: if I just download some .png from Google Images it renders just fine, but if I create some random texture using MS Paint and only change the file path to said MS Paint texture I get an Exception thrown at 0x00007FFA3BEADB68 (nvoglv64.dll) in Untitled_OpenGL_Project.exe: 0xC0000005: Access violation reading location 0x000001F6A94A2000. exception.
What is the problem? Is it something to do with the value for transparent pixels or something? (grasping at straws here)
main.cpp:
#include <iostream>
#include <glad/glad.h>
#include <GLFW/glfw3.h>
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
#include "Shader.h"
#include "utils.h"
#include "stb_image.h"
int main()
{
// Setup
if (!Utility::glfwSetup())
return -1;
GLFWwindow* window = Utility::glfwOpenWindow(Utility::SCREEN_WIDTH, Utility::SCREEN_HEIGHT, "GLFW Window");
glfwMakeContextCurrent(window);
if (!Utility::gladSetup())
return -1;
glfwSetKeyCallback(window, Utility::key_callback);
glEnable(GL_DEPTH_TEST);
// Program
glViewport(0, 0, 800, 600);
float vertices[] = {
// Position // Texel
-0.5f, -0.5f, 0.5f, 0.0f, 0.0f,
-0.5f, 0.5f, 0.5f, 0.0f, 1.0f,
0.5f, -0.5f, 0.5f, 1.0f, 0.0f,
0.5f, 0.5f, 0.5f, 1.0f, 1.0f,
-0.5f, -0.5f, -0.5f, 0.0f, 0.0f,
-0.5f, 0.5f, -0.5f, 0.0f, 1.0f,
0.5f, -0.5f, -0.5f, 1.0f, 0.0f,
0.5f, 0.5f, -0.5f, 1.0f, 1.0f,
0.5f, -0.5f, -0.5f, 0.0f, 0.0f,
0.5f, 0.5f, -0.5f, 0.0f, 1.0f,
0.5f, -0.5f, 0.5f, 1.0f, 0.0f,
0.5f, 0.5f, 0.5f, 1.0f, 1.0f,
-0.5f, -0.5f, -0.5f, 0.0f, 0.0f,
-0.5f, 0.5f, -0.5f, 0.0f, 1.0f,
-0.5f, -0.5f, 0.5f, 1.0f, 0.0f,
-0.5f, 0.5f, 0.5f, 1.0f, 1.0f,
-0.5f, 0.5f, -0.5f, 0.0f, 0.0f,
0.5f, 0.5f, -0.5f, 0.0f, 1.0f,
-0.5f, 0.5f, 0.5f, 1.0f, 0.0f,
0.5f, 0.5f, 0.5f, 1.0f, 1.0f,
-0.5f, -0.5f, -0.5f, 0.0f, 0.0f,
0.5f, -0.5f, -0.5f, 0.0f, 1.0f,
-0.5f, -0.5f, 0.5f, 1.0f, 0.0f,
0.5f, -0.5f, 0.5f, 1.0f, 1.0f
};
unsigned int indices[] = {
0, 1, 3,
0, 2, 3,
4, 5, 7,
4, 6, 7,
8, 9, 11,
8, 10, 11,
12, 13, 15,
12, 14, 15,
16, 17, 19,
16, 18, 19,
20, 21, 23,
20, 22, 23
};
// Buffer setup
unsigned int VAO;
glGenVertexArrays(1, &VAO);
glBindVertexArray(VAO);
unsigned int EBO;
glGenBuffers(1, &EBO);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
unsigned int VBO;
glGenBuffers(1, &VBO);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)(3 * sizeof(float)));
glEnableVertexAttribArray(1);
float tiltAngle = 70.0f;
// MVP
glm::mat4 model = glm::mat4(1.0f);
glm::vec3 zAxis = glm::vec3(0.0f, 0.0f, 1.0f);
model = glm::rotate(model, (float) glm::radians(tiltAngle), zAxis);
glm::mat4 view = glm::mat4(1.0f);
view = glm::translate(view, glm::vec3(0.0f, 0.0f, -3.0f));
glm::mat4 proj = glm::perspective(glm::radians(45.0f), (float) Utility::SCREEN_WIDTH / Utility::SCREEN_HEIGHT, 0.1f, 100.0f);
// Texture
unsigned int texture;
glGenTextures(1, &texture);
glBindTexture(GL_TEXTURE_2D, texture);
Utility::texSetup(GL_REPEAT, GL_REPEAT, GL_NEAREST, GL_LINEAR);
int width, height, numChannels;
unsigned char* data = stbi_load("res/textures/BlackFrame.png", &width, &height, &numChannels, 0);
if (data)
{
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
glGenerateMipmap(GL_TEXTURE_2D);
}
else
{
std::cout << "Failed to load texture" << std::endl;
}
stbi_image_free(data);
// Shader setup
Shader shader("res/shaders/VertexShader.shader", "res/shaders/FragShader.shader");
glUseProgram(shader.ID);
shader.setUniformMat4f(model, "model");
shader.setUniformMat4f(view, "view");
shader.setUniformMat4f(proj, "proj");
shader.setUniform1i(0, "texture1");
// Time
float prevTime = (float)glfwGetTime();
while (!glfwWindowShouldClose(window))
{
// Setup
glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// Input
Utility::processInput(window);
// Rendering
float curTime = (float)glfwGetTime();
float deltaTime = curTime - prevTime;
prevTime = curTime;
// Rotate around Y axis
glm::vec3 yAxis = glm::vec3(0.0f, 1.0f, 0.0f);
model = glm::rotate(model, (float)glm::radians(-tiltAngle), zAxis);
model = glm::rotate(model, deltaTime, yAxis);
model = glm::rotate(model, (float)glm::radians(tiltAngle), zAxis);
shader.setUniformMat4f(model, "model");
glDrawElements(GL_TRIANGLES, 36, GL_UNSIGNED_INT, 0);
// Clean-up
glfwSwapBuffers(window);
glfwPollEvents();
}
glfwTerminate();
return 0;
}
utils.cpp:
int Utility::glfwSetup()
{
if (!glfwInit())
{
std::cerr << "Failed to initialize GLFW" << std::endl;
return 0;
}
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, VERSION_MAJOR);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, VERSION_MINOR);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
std::cout << "GLFW " << VERSION_MAJOR << "." << VERSION_MINOR << " initialized successfully." << std::endl;
return 1;
}
int Utility::gladSetup()
{
if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
{
std::cout << "Failed to initialize GLAD" << std::endl;
glfwTerminate();
return 0;
}
return 1;
}
void Utility::texSetup(int wrapParamS, int wrapParamT, int minParam, int magParam)
{
stbi_set_flip_vertically_on_load(true);
float borderColor[] = { 1.0f, 1.0f, 1.0f, 1.0f };
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, wrapParamS);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, wrapParamT);
glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, borderColor);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, minParam);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, magParam);
}
GLFWwindow* Utility::glfwOpenWindow(int width, int height, const char* title)
{
GLFWwindow* window = glfwCreateWindow(width, height, title, NULL, NULL);
if (window == NULL)
{
std::cerr << "Failed to create GLFW window" << std::endl;
glfwTerminate();
return NULL;
}
return window;
}
void Utility::processInput(GLFWwindow* window)
{
if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
glfwSetWindowShouldClose(window, GLFW_TRUE);
}
It is not guaranteed that a PNG file has 4 channels or that stbi_load will return an image with 4 channels.
stbi_load can be forced to generate an image with 4 color channels, by explicitly pass 4 to the last parameter:
unsigned char* data = stbi_load("BlackFrame.png", &width, &height, &numChannels, 0);
unsigned char* data = stbi_load("BlackFrame.png", &width, &height, &numChannels, 4);
See stb_image.h:
Basic usage (see HDR discussion below for HDR usage):
int x,y,n;
unsigned char *data = stbi_load(filename, &x, &y, &n, 0);
// ... process data if not NULL ...
// ... x = width, y = height, n = # 8-bit components per pixel ...
// ... replace '0' with '1'..'4' to force that many components per pixel
// ... but 'n' will always be the number that it would have been if you said 0
stbi_image_free(data);
I am having some issues trying to rendering a cube using OpenGL and C++. I was able to get a simple square on the screen, after adding the extra vertices, a slightly tilted triangle was shown instead... Now I have tried to use an element buffer. I filled the element_buffer [] with extra values (I admittedly found on a tutorial) and have nothing being displayed. Any help would be great.
#include "CubeAsset.h"
CubeAsset::CubeAsset() {
// model coordinates, origin at centre.
GLfloat vertex_buffer [] {
-0.5f,-0.5f,-0.5f, // triangle 0 : begin
-0.5f,-0.5f, 0.5f,
-0.5f, 0.5f, 0.5f, // triangle 0 : end
0.5f, 0.5f,-0.5f, // triangle 2 : begin
-0.5f,-0.5f,-0.5f,
-0.5f, 0.5f,-0.5f, // triangle 2 : end
0.5f,-0.5f, 0.5f,
-0.5f,-0.5f,-0.5f,
0.5f,-0.5f,-0.5f,
0.5f, 0.5f,-0.5f,
0.5f,-0.5f,-0.5f,
-0.5f,-0.5f,-0.5f,
-0.5f,-0.5f,-0.5f,
-0.5f, 0.5f, 0.5f,
-0.5f, 0.5f,-0.5f,
0.5f,-0.5f, 0.5f,
-0.5f,-0.5f, 0.5f,
-0.5f,-0.5f,-0.5f,
-0.5f, 0.5f, 0.5f,
-0.5f,-0.5f, 0.5f,
0.5f,-0.5f, 0.5f,
0.5f, 0.5f, 0.5f,
0.5f,-0.5f,-0.5f,
0.5f, 0.5f,-0.5f,
0.5f,-0.5f,-0.5f,
0.5f, 0.5f, 0.5f,
0.5f,-0.5f, 0.5f,
0.5f, 0.5f, 0.5f,
0.5f, 0.5f,-0.5f,
-0.5f, 0.5f,-0.5f,
0.5f, 0.5f, 0.5f,
-0.5f, 0.5f,-0.5f,
-0.5f, 0.5f, 0.5f,
0.5f, 0.5f, 0.5f,
-0.5f, 0.5f, 0.5f,
0.5f,-0.5f, 0.5f
};
element_buffer_length = 36;
GLubyte element_buffer [] {
0, 1, 2, 2, 3, 0,
0, 3, 4, 4, 5, 0,
0, 5, 6, 6, 1, 0,
1, 6, 7, 7, 2, 1,
7, 4, 3, 3, 2, 7,
4, 7, 6, 6, 5, 4
};
// Transfer buffers to the GPU
//
// create buffer
glGenBuffers(1, &vertex_buffer_token);
// immediately bind the buffer and transfer the data
glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer_token);
glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * 12, vertex_buffer, GL_STATIC_DRAW);
glGenBuffers(1, &element_buffer_token);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, element_buffer_token);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(GLuint) * element_buffer_length, element_buffer, GL_STATIC_DRAW);
}
CubeAsset::~CubeAsset() {
}
#ifdef DEBUG
#define checkGLError() checkError(__FILE__, __LINE__)
#else
// define symbol to be nothing
#define checkGLError()
#endif
void checkError(std::string file, int line) {
GLenum gl_error = glGetError();
if(GL_NO_ERROR != gl_error) {
std::cerr << "GL error in " << file << " at line " << line << " error: " << gl_error << std::endl;
exit(-1);
}
}
void CubeAsset::Draw(GLuint program_token) {
if(!glIsProgram(program_token)) {
std::cerr << "Drawing Cube with invalid program" << std::endl;
return;
}
GLint validation_ok;
glValidateProgram(program_token);
glGetProgramiv(program_token, GL_VALIDATE_STATUS, &validation_ok);
if(!validation_ok) {
GLint maxLength = 0;
glGetProgramiv(program_token, GL_INFO_LOG_LENGTH, &maxLength);
//The maxLength includes the NULL character
std::vector<char> errorLog(maxLength);
glGetProgramInfoLog(program_token, maxLength, &maxLength, &errorLog[0]);
std::cerr << "Invalid program " << program_token << " with error code " << validation_ok << std::endl;
for(auto c: errorLog) {
std::cerr << c;
}
exit(-1);
}
GLuint position_attrib = glGetAttribLocation(program_token, "position");
checkGLError();
glUseProgram(program_token);
checkGLError();
// use the previously transferred buffer as the vertex array. This way
// we transfer the buffer once -- at construction -- not on every frame.
glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer_token);
glVertexAttribPointer(
position_attrib, /* attribute */
3, /* size */
GL_FLOAT, /* type */
GL_FALSE, /* normalized? */
0, /* stride */
(void*)0 /* array buffer offset */
);
glEnableVertexAttribArray(position_attrib);
checkGLError();
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, element_buffer_token);
glDrawElements(
GL_TRIANGLES,
element_buffer_length,
GL_UNSIGNED_INT,
(GLvoid*) 0
);
checkGLError();
glDisableVertexAttribArray(position_attrib);
}
The header file:
#ifndef CUBEASSET_H
#define CUBEASSET_H
#include <vector>
#include <GL/gl.h>
#include <glm/glm.hpp>
#include <glm/ext.hpp>
#include "GameAsset.h"
class CubeAsset : public GameAsset {
public:
CubeAsset();
~CubeAsset();
virtual void Draw(GLuint);
private:
GLuint element_buffer_length;
GLuint vertex_buffer_token, element_buffer_token;
};
#endif // CUBEASSET_H
You have a type mismatch. You indices in your code are of type GLubyte:
GLubyte element_buffer [] {
0, 1, 2, 2, 3, 0,
...
But the rest of the code treats the as GLuint:
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(GLuint) * element_buffer_length, ...);
...
glDrawElements(GL_TRIANGLES, element_buffer_length, GL_UNSIGNED_INT, ...);
You need to settle on using a consistent type, either GLubyte everywhere, or GLuint everywhere.
I made a VBO cube a while ago, It work's great but it has a lack of performance.
How can I remove invisible faces in a VBO?
I also have a problem with the textures, they are seemed to be messed up :/
If anyone knows something to fix that, it would be great!
My code:
#include <windows.h>
#include <iostream>
#include <glew.h>
#include <GL/gl.h>
#include <GL/glu.h>
#define OFFSET_BUFFER(bytes) ((GLfloat *)NULL + bytes)
PFNGLGENBUFFERSARBPROC glGenBuffers = NULL;
PFNGLBINDBUFFERPROC glBindBuffer = NULL;
PFNGLBUFFERDATAPROC glBufferData = NULL;
GLfloat vertex[];
GLuint m_vertexBuffer;
GLuint m_textureBuffer;
void VBOinit()
{
#ifdef _WIN32
glGenBuffers = (PFNGLGENBUFFERSARBPROC)wglGetProcAddress("glGenBuffers");
glBindBuffer = (PFNGLBINDBUFFERPROC)wglGetProcAddress("glBindBuffer");
glBufferData = (PFNGLBUFFERDATAPROC)wglGetProcAddress("glBufferData");
#else
glGenBuffers = (PFNGLGENBUFFERSARBPROC)glXGetProcAddress((const GLubyte*)"glGenBuffers");
glBindBuffer = (PFNGLBINDBUFFERPROC)glXGetProcAddress((const GLubyte*)"glBindBuffer");
glBufferData = (PFNGLBUFFERDATAPROC)glXGetProcAddress((const GLubyte*)"glBufferData");
#endif
if (!glGenBuffers || !glBindBuffer || !glBufferData)
{
std::cerr << "VBOs are not supported by your graphics card" << std::endl;
return;
}
// TEXTURE VBO
GLfloat texture[] =
{
0, 0,
1, 0,
1, 1,
0, 1,
0, 0,
1, 0,
1, 1,
0, 1,
0, 0,
1, 0,
1, 1,
0, 1,
0, 0,
1, 0,
1, 1,
0, 1,
0, 0,
1, 0,
1, 1,
0, 1,
0, 0,
1, 0,
1, 1,
0, 1,
0, 0,
1, 0,
1, 1,
0, 1,
0, 0,
1, 0,
1, 1,
0, 1,
0, 0,
1, 0,
1, 1,
0, 1,
0, 0,
1, 0,
1, 1,
0, 1
};
glGenBuffers(1, &m_textureBuffer);
glBindBuffer(GL_ARRAY_BUFFER, m_textureBuffer);
glBufferData(GL_ARRAY_BUFFER, sizeof(texture), &texture, GL_STATIC_DRAW);
// GEOMETRIC VBO
GLfloat vertex[] =
{
0.5f, 0.5f, 0.5f, -0.5f, 0.5f, 0.5f, -0.5f, -0.5f, 0.5f, // v0-v0.5f-v2 (front)
-0.5f, -0.5f, 0.5f, 0.5f, -0.5f, 0.5f, 0.5f, 0.5f, 0.5f, // v2-v3-v0
0.5f, 0.5f, 0.5f, 0.5f, -0.5f, 0.5f, 0.5f, -0.5f, -0.5f, // v0-v3-v4 (right)
0.5f, -0.5f, -0.5f, 0.5f, 0.5f, -0.5f, 0.5f, 0.5f, 0.5f, // v4-v5-v0
0.5f, 0.5f, 0.5f, 0.5f, 0.5f, -0.5f, -0.5f, 0.5f, -0.5f, // v0-v5-v6 (top)
-0.5f, 0.5f, -0.5f, -0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, // v6-v0.5f-v0
-0.5f, 0.5f, 0.5f, -0.5f, 0.5f, -0.5f, -0.5f, -0.5f, -0.5f, // v0.5f-v6-v7 (left)
-0.5f, -0.5f, -0.5f, -0.5f, -0.5f, 0.5f, -0.5f, 0.5f, 0.5f, // v7-v2-v0.5f
-0.5f, -0.5f, -0.5f, 0.5f, -0.5f, -0.5f, 0.5f, -0.5f, 0.5f, // v7-v4-v3 (bottom)
0.5f, -0.5f, 0.5f, -0.5f, -0.5f, 0.5f, -0.5f, -0.5f, -0.5f, // v3-v2-v7
0.5f, -0.5f, -0.5f, -0.5f, -0.5f, -0.5f, -0.5f, 0.5f, -0.5f, // v4-v7-v6 (back)
-0.5f, 0.5f, -0.5f, 0.5f, 0.5f, -0.5f, 0.5f, -0.5f, -0.5f // v6-v5-v4
};
glGenBuffers(1, &m_vertexBuffer); //Generate a buffer for the vertices
glBindBuffer(GL_ARRAY_BUFFER, m_vertexBuffer); //Bind the vertex buffer
glBufferData(GL_ARRAY_BUFFER, sizeof(vertex), &vertex[0], GL_STATIC_DRAW); //Send the data to OpenGL
}
void VBOrender(int x, int y, int z)
{
glBindTexture(GL_TEXTURE_2D, Texture::textures[0]);
glTranslatef(x, y, z);
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glBindBuffer(GL_ARRAY_BUFFER, m_vertexBuffer);
glVertexPointer(3, GL_FLOAT, 0, 0);
glBindBuffer(GL_ARRAY_BUFFER, m_textureBuffer);
glTexCoordPointer(2, GL_FLOAT, 0, OFFSET_BUFFER(0));
//glPolygonMode(GL_FRONT, GL_LINE);
glDrawArrays(GL_TRIANGLES, 0, 36);
glDisableClientState(GL_VERTEX_ARRAY);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
glTranslatef(-x, -y, -z);
}
Thank you for reading :)
Adding detail on what we started in the comments above, plus a couple more things.
As was previously suggested, add glEnable(GL_CULL_FACE). I doubt that it addresses your bottleneck, but it can't hurt.
One other thing you should generally do is store your positions and texture coordinates interleaved in a single buffer, instead of storing them in separate buffers. Again, I think you're limited elsewhere right now, just a general recommendation.
To illustrate what I suggested in the comment about avoiding to set redundant state inside your loop. Right now your structure in pseudo code looks like this:
loop over x, y, z
VBOrender(x, y, z)
end loop
Instead, I would structure it like this:
VBObind()
loop over x, y, z
VBOrender(x, y, z)
end loop
VBOunbind()
And split up the code in your current VBOrender function like this:
void VBObind()
{
glBindTexture(GL_TEXTURE_2D, Texture::textures[0]);
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glBindBuffer(GL_ARRAY_BUFFER, m_vertexBuffer);
glVertexPointer(3, GL_FLOAT, 0, 0);
glBindBuffer(GL_ARRAY_BUFFER, m_textureBuffer);
glTexCoordPointer(2, GL_FLOAT, 0, OFFSET_BUFFER(0));
}
void VBOunbind()
{
glDisableClientState(GL_VERTEX_ARRAY);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
}
void VBOrender(int x, int y, int z)
{
glTranslatef(x, y, z);
glDrawArrays(GL_TRIANGLES, 0, 36);
glTranslatef(-x, -y, -z);
}
I expect that this will give you a significant performance improvement. To get massively better performance, you would need something more dramatic, like packing all the cubes in a single draw call. This looks slightly tricky, though, because from what I can see in your code on pastebin, the rendering of each cube is conditional.
If you're willing to write your own shaders, you can make the translation an attribute, which would be much faster to update than the fixed function transformation matrix.
You can turn on back-face culling by calling:
glFrontFace(GL_CCW); // <- says faces are defined in counter-clockwise order. Change if yours are in clockwise order
glEnable(GL_CULL_FACE);
glCullFace(GL_BACK);
As for your textures, it looks like you've defined texture coordinates for 11 cube faces, but you you have 36 triangles you're drawing. 36 triangles would be 18 quads, so I think you need more texture coordinates.
I don't understood how do OpenGL's buffers work. I learn OpenGL, by means of OpenGL Redbook 8th edition.
For example, I have an array of position, an array of color and an array of indices:
static const GLfloat strip_position[] =
{
-4.0f, 0.0f, -1.0f, 1.0f, //0
-3.5f, -1.0f, -1.0f, 1.0f, //1
-3.0f, 0.0f, -1.0f, 1.0f, //2
-2.5f, -1.0f, -1.0f, 1.0f, //3
-2.0f, 0.0f, -1.0f, 1.0f, //4
-1.5f, -1.0f, -1.0f, 1.0f, //5
-1.0f, 0.0f, -1.0f, 1.0f, //6
-0.5f, -1.0f, -1.0f, 1.0f, //7
0.0f, 0.0f, -1.0f, 1.0f //8
};
static const GLfloat strip_colors[] =
{
1.0f, 1.0f, 1.0f, 1.0f,
1.0f, 1.0f, 0.0f, 1.0f,
1.0f, 0.0f, 1.0f, 1.0f,
1.0f, 0.0f, 0.0f, 1.0f,
0.0f, 1.0f, 1.0f, 1.0f,
0.0f, 1.0f, 0.0f, 1.0f,
0.0f, 0.0f, 1.0f, 1.0f,
1.0f, 0.0f, 1.0f, 1.0f,
1.0f, 1.0f, 0.0f, 1.0f,
};
static const GLushort strip_indices[] =
{
0, 1, 2, 3, 4, 5, 6, 7, 8
};
Good.Then I create Vertex Array Object is follows:
GLuint vao[1]; // vertex array object
glGenVertexArrays(1, vao);
glBindVertexArray(vao[0]);
In my understanding, first parameter (GLsizei n) is number of an arrays of position(or coordinate of vertices of ONE my object).
Then I create Element Array Buffer is follows:
GLuint ebo[1]; // element buffer object
glGenBuffers(1, ebo);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo[0]);
glBufferData(
GL_ELEMENT_ARRAY_BUFFER,
sizeof(strip_indices),
strip_indices,
GL_STATIC_DRAW
);
Then I create Vertex Buffer Object is follows:
GLuint vbo[1]; // vertex buffer object
glGenBuffers(1, vbo);
glBindBuffer(GL_ARRAY_BUFFER, vbo[0]);
glBufferData(
GL_ARRAY_BUFFER,
sizeof(strip_position) + sizeof(strip_colors),
NULL,
GL_STATIC_DRAW
);
glBufferSubData(
GL_ARRAY_BUFFER,
0, //offset
sizeof(strip_position), //size date
strip_position //data
);
glBufferSubData(
GL_ARRAY_BUFFER,
sizeof(strip_position), //offset
sizeof(strip_colors), //size data
strip_colors //data
);
Next I call glVertexAttribPointer() is follows:
glVertexAttribPointer(
0, //index
4, //size
GL_FLOAT, //type
GL_FALSE, //normalized
0, //stride
NULL //pointer
);
glVertexAttribPointer(
1,
4,
GL_FLOAT,
GL_FALSE,
0,
(const GLvoid*)sizeof(strip_position)
);
glEnableVertexAttribArray(0);
glEnableVertexAttribArray(1);
What does that function?(glVertexAttribPointer() and glEnableVertexAttribArray())
Okay. I finished initialize a my data. Now I can draw it's follows:
glBindVertexArray(vao[0]);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo[0]);
glDrawElements(GL_TRIANGLE_STRIP, 8, GL_UNSIGNED_SHORT, NULL);
How OpenGL understood, which buffer need to use and where it is? Word "bind" is mean a relation? i.e. something bind with something? And If I want to display a two object, what do I do?
For example, I have a two arrays of position, a two arrays of position and a two arrays of indices?
static const GLfloat TWOstrip_colors[] =
{
1.0f, 1.0f, 1.0f, 1.0f,
1.0f, 1.0f, 0.0f, 1.0f,
1.0f, 0.0f, 1.0f, 1.0f,
1.0f, 0.0f, 0.0f, 1.0f,
0.0f, 1.0f, 1.0f, 1.0f,
0.0f, 1.0f, 0.0f, 1.0f,
0.0f, 0.0f, 1.0f, 1.0f,
1.0f, 0.0f, 1.0f, 1.0f,
1.0f, 1.0f, 0.0f, 1.0f
};
static const GLfloat TWOstrip_colors[] =
{
1.0f, 1.0f, 1.0f, 1.0f,
1.0f, 1.0f, 0.0f, 1.0f,
1.0f, 0.0f, 1.0f, 1.0f,
1.0f, 0.0f, 0.0f, 1.0f,
0.0f, 1.0f, 1.0f, 1.0f,
0.0f, 1.0f, 0.0f, 1.0f,
0.0f, 0.0f, 1.0f, 1.0f,
1.0f, 0.0f, 1.0f, 1.0f,
1.0f, 1.0f, 0.0f, 1.0f,
};
static const GLushort TWOstrip_indices[] =
{
0, 1, 2, 3, 4, 5, 6, 7, 8
};
How do this?
OpenGL has the notion of so called objects. Those are not models or geometrical objects, but encapsulations of internal state. If you're familiar with object oriented programming, and the C++ STL OpenGL objects can be thought of kind of class instances.
The call glGenBuffers(count, out_names) could be roughtly interpreted into something like
std::map<GLuint, openglobject*> bufferobjects;
glGenBuffers(GLuint count, std::vector<GLuint> *out_names)
{
out_names->resize(count);
for(int i=0; i < count; i++) {
GLuint name = get_next_free_handle_ID();
bufferobjects[name] = NULL;
out_names.set(i, name);
}
}
So what it does is, it reserves a handle ID (OpenGL calls them names) and allocates a slot for it in the internal mapping between handles and bufferobject instance pointers.
The call glBindBuffer actually creates the buffer object, something like that
glBindBuffer(GLenum target, GLuint name)
{
openglobject *objinstance = NULL;
if( name != 0 ) {
if( !bufferobjects.has_key(name) ) {
push_openglerror( INVALID_NAME );
return;
}
objinstance = bufferobjects[name];
if( NULL == bufferobjects[name] ) {
switch(target) {
case GL_ARRAY_BUFFER:
objinstance = new OpenGLArrayBuffer; break;
case GL_ELEMENT_ARRAY_BUFFER:
objinstance = new OpenGLElementArrayBuffer; break;
/* ... and so on */
default:
push_openglerror( INVALID_TARGET ); return;
}
bufferobjects[name] = objinstance;
}
}
}
if( objinstance != NULL && target_of(objinstance) != target ) {
opengl_pusherror( INVALID_TARGET );
}
switch( target ) {
case GL_ARRAY_BUFFER:
/* this would be a static function of the subclass setting
* global singleton instance pointer
*/
OpenGLArrayBuffer::make_current(objinstance);
break;
/* ... and so on */
}
}
I think you can see there this is going: The buffer target specifies the type of subclass the instance is you're working with and its static members.
glBufferData then actually allocates memory of the particular object, and can initialize it with the contents of a buffer you pass to it. glBufferSubData just copies data to the internal storage.
So much for the Buffer Objects (of which there are several kinds).
The other part are the Vertex Array Objects. Those are special OpenGL objects that create an association between vertex attributes, which are per-vertex data passed to the shaders based on their attribute index and the array buffer objects from which this data is takes.
When you call glGenVertexArray something like this happens:
std::map<GLuint, openglobject*> vertexarrayobjects;
glGenVertexArrays(GLuint count, std::vector<GLuint> *out_names)
{
out_names->resize(count);
for(int i=0; i < count; i++) {
GLuint name = get_next_free_handle_ID();
vertexarrayrobjects[name] = NULL;
out_names.set(i, name);
}
}
Looks familiar, doesn't it? The only difference is, that a different mapping structure is used. glBindVertexArray does the allocation of an instance and so on.
Now the calls glEnableVertexAttribute and glVertexAttribPointer can be thought as the following:
glEnableVertexAttribute(GLuint idx)
{
((OpenGLVertexArrayObject*)currentvertexarray)->add_attribute(idx);
}
glVertexAttribPointer(GLuint idx, ..., void *ptr)
{
((OpenGLVertexArrayObject*)currentvertexarray)->bind_attribute(
idx,
OpenGLArrayBuffer::get_current(),
(off_t)ptr );
}
Okay, that last bit requires some explanation. That you pass a pointer to glVertexAttribPointer is a legacy from OpenGL-1.1 where there were no OpenGL buffer objects and instead you pointed directly to memory of your program. Then buffer objects got introduced and those don't require a pointer but a byte sized offset when binding. So the OpenGL devs went the dirty route and just lied to the compilers about it. I did explain the details in my answer to the question "What is the result of NULL + int?"
Note that OpenGL-4 introduced a new, much more powerfull and flexible API to create VAO attribute ←→ VBO bindings.
there is always a "current Buffer" of each target set by glBindBuffer(target, id) on which most the buffer operations know to operate.
openGL uses glEnableVertexAttribArray to know which attributes it should look for, if not called then openGL will not use the data.
glVertexAttribPointer tells openGL where in the currently bound GL_ARRAY_BUFFER the attributes must be found for the current vertexArrays. in your example: (assuming vbo[0] is still bound to GL_ARRAY_BUFFER)
attribute for index 0 is found in vbo[0] with 4 floats per vertex tightly packed and starting from 0
attribute for index 1 is found in vbo[0] with 4 floats per vertex tightly packed and starting from sizeof(strip_position)
these bindings persist over glBindBuffer calls so if you want to rebind then you'll need to bind the other buffer call glVertexAttribPointer and then you can unbind again
I suggest you always call glBindBuffer with a 0 buffer so openGL knows you don't want to work with the current buffer anymore and avoid strange behaviors
to create the second object you can refill the various buffers each time you switch objects
or you can either create 2 sets of buffers:
GLuint vao[2]; // vertex array object
glGenVertexArrays(2, vao);
glBindVertexArray(vao[0]);
GLuint ebo[2]; // element buffer object
glGenBuffers(2, ebo);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo[0]);
glBufferData(
GL_ELEMENT_ARRAY_BUFFER,
sizeof(strip_indices),
strip_indices,
GL_STATIC_DRAW
);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo[1]);
glBufferData(
GL_ELEMENT_ARRAY_BUFFER,
sizeof(strip_indices),
TWO_strip_indices,
GL_STATIC_DRAW
);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
GLuint vbo[2]; // vertex buffer object
glGenBuffers(2, vbo);
glBindBuffer(GL_ARRAY_BUFFER, vbo[0]);
glBufferData(
GL_ARRAY_BUFFER,
sizeof(strip_position) + sizeof(strip_colors),
NULL,
GL_STATIC_DRAW
);
glBufferSubData(
GL_ARRAY_BUFFER,
0, //offset
sizeof(strip_position), //size date
strip_position //data
);
glBufferSubData(
GL_ARRAY_BUFFER,
sizeof(strip_position), //offset
sizeof(strip_colors), //size data
strip_colors //data
);
//fill other buffer (assuming the first TWOstrip_colors was actually TWOstrip_position
glBindBuffer(GL_ARRAY_BUFFER, vbo[1]);
glBufferData(
GL_ARRAY_BUFFER,
sizeof(TWOstrip_position) + sizeof(TWOstrip_colors),
NULL,
GL_STATIC_DRAW
);
glBufferSubData(
GL_ARRAY_BUFFER,
0, //offset
sizeof(TWOstrip_position), //size date
strip_position //data
);
glBufferSubData(
GL_ARRAY_BUFFER,
sizeof(TWOstrip_position), //offset
sizeof(TWOstrip_colors), //size data
strip_colors //data
);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(vao[0]);
glBindBuffer(GL_ARRAY_BUFFER, vbo[0])
glVertexAttribPointer(
0, //index
4, //size
GL_FLOAT, //type
GL_FALSE, //normalized
0, //stride
NULL //pointer
);
glVertexAttribPointer(
1,
4,
GL_FLOAT,
GL_FALSE,
0,
(const GLvoid*)sizeof(strip_position)
);
glBindVertexArray(vao[1]);
glBindBuffer(GL_ARRAY_BUFFER, vbo[1]);
glVertexAttribPointer(
0, //index
4, //size
GL_FLOAT, //type
GL_FALSE, //normalized
0, //stride
NULL //pointer
);
glVertexAttribPointer(
1,
4,
GL_FLOAT,
GL_FALSE,
0,
(const GLvoid*)sizeof(TWOstrip_position)
);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);
then to draw:
glEnableVertexAttribArray(0);
glEnableVertexAttribArray(1);
glBindVertexArray(vao[0]);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo[0]);
glDrawElements(GL_TRIANGLE_STRIP, 8, GL_UNSIGNED_SHORT, NULL);
glBindVertexArray(vao[1]);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo[1]);
glDrawElements(GL_TRIANGLE_STRIP, 8, GL_UNSIGNED_SHORT, NULL);