Seasons Greetings everyone! I adapted the code from this tutorial to support using a VAO/VBO, but now I get this:
Instead of a nice round circle. Here's the code:
#define GLEW_STATIC
#include <GLEW/glew.h>
#include <GLFW/glfw3.h>
#include <corecrt_math_defines.h>
#include <cmath>
#include <vector>
#define SCREEN_WIDTH 3000
#define SCREEN_HEIGHT 1900
void drawPolygon(GLuint& vao, GLuint& vbo, GLfloat x,
GLfloat y, GLdouble radius, GLint numberOfSides);
int main(void) {
if (!glfwInit()) {
return -1;
}
GLFWwindow* window = glfwCreateWindow(SCREEN_WIDTH,
SCREEN_HEIGHT, "Hello World", NULL, NULL);
if (!window) {
glfwTerminate();
return -1;
}
glfwMakeContextCurrent(window);
glewExperimental = GL_TRUE;
glewInit();
glGetError();
glViewport(0.0f, 0.0f, SCREEN_WIDTH, SCREEN_HEIGHT);
GLuint vao, vbo;
glGenVertexArrays(1, &vao);
glGenBuffers(1, &vbo);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(0, SCREEN_WIDTH, 0, SCREEN_HEIGHT, -1, 1);
while (!glfwWindowShouldClose(window)) {
glClear(GL_COLOR_BUFFER_BIT);
drawPolygon(vao, vbo, SCREEN_WIDTH / 2,
SCREEN_HEIGHT / 2, 250.0f, 50);
glfwSwapBuffers(window);
glfwPollEvents();
}
glfwTerminate();
return 0;
}
void drawPolygon(GLuint& vao, GLuint& vbo, GLfloat x,
GLfloat y, GLdouble radius, GLint numberOfSides) {
int numVertices = numberOfSides + 2;
GLdouble twicePi = 2.0f * M_PI;
vector<GLdouble> circleVerticesX;
vector<GLdouble> circleVerticesY;
circleVerticesX.push_back(x);
circleVerticesY.push_back(y);
for (int i = 1; i < numVertices; i++) {
circleVerticesX.push_back(x + (radius *
cos(i * twicePi / numberOfSides)));
circleVerticesY.push_back(y + (radius *
sin(i * twicePi / numberOfSides)));
}
vector<GLdouble> vertices;
for (int i = 0; i < numVertices; i++) {
vertices.push_back(circleVerticesX[i]);
vertices.push_back(circleVerticesY[i]);
}
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBufferData(GL_ARRAY_BUFFER, numVertices * sizeof(
GLdouble), vertices.data(), GL_STATIC_DRAW);
glBindVertexArray(vao);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 2, GL_DOUBLE, GL_FALSE,
2 * sizeof(GLdouble), (void*)0);
glDrawArrays(GL_TRIANGLE_FAN, 0, 27);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);
}
What the heck have I done wrong?! Using the original code works, so I am absolutely baffled by this ridiculous result! MTIA to anyone who can help :-)
The size of your VBO is numVertices * sizeof(GLdouble), which is half the actual size of your vertex data (there is an x and a y component for each vertex). Thus, you end up drawing twice as many vertices as your VBO actually has vertex data for. Reading out of bounds of your VBO seems to result in just zeroes in your OpenGL implementation, which is why all vertices of the lower half of your circle are just the bottom left corner (this is not guaranteed unless you explicitly enable robust buffer access, just what your driver and GPU seem to be doing anyways)…
couple of notes:
You generally don't want to use double unless you need it. double takes twice the memory bandwidth, and arithmetic on double is generally at least a bit slower than float (yes, even on an x86 CPU since floating point arithmetic is not really done using the x87 FPU anymore nowadays). GPUs in particular are built for float arithmetic. Especially on consumer GPUs, double arithmetic is significantly (an order of magnitude) slower than float.
Why not simply push the vertex data directly into vertices rather than first into circleVerticesX and circleVerticesY and then copying it over into vertices from there?
You know exactly how many vertices are going to be generated. Thus, there's no need to dynamically grow your vertex container in the loop that generates the coordinates (among other things, the .push_back() will almost certainly prevent vectorization of the loop). I would suggest to at least .reserve() the corresponding number of elements (assuming this is an std::vector) before entering the loop. Personally, I would just allocate an array of the appropriate, fixed size via
auto vertex_data = std::unique_ptr<GLfloat[]> { new GLfloat[numVertices * 2] };
in this case.
You don't actually need a center point. Since a circle is a convex shape, you can simply use one of the points on the circle as the central vertex of your fan.
This is not necessarily the most efficient way to draw a circle (a lot of long, thin triangles; more on that here)
You probably don't want to generate and upload your vertex data again and again every frame unless something about it changes.
Apart from that, you will probably want to make your glDrawArrays call draw the actual number of vertices rather than just always 27…
Related
Currently working on a personal implementation of the Boids Flocking simulation to test what I've learned about OpenGL, I've been looking to see how high I can get the # of bonds drawn upon the screen above 30 FPS.
Unfortunately I've hit a road-block with how at 2^16 and beyond boids (Where each boid is three float vertices each of size 3, where the z value is maintained to be 0) the graphical output begins to flicker with boids disappearing and reappearing upon the screen.
Interestingly this only seems to happens in the beginning stages of the app, with the flickering seemingly ending upon the boids forming their "flocks" and coincidentally clumping together visually.
I am also suspect of the number 2^16 as it matches with the GLshort bit count of 16, so potentially an overflow of the values?
Here is the area I thought was most pertinent to the issue
std::vector<float> vertices(flock.boids.size()*9, 0.f);
unsigned int VAO, VBO;
glGenVertexArrays(1, &VAO);
glGenBuffers(1, &VBO);
glBindVertexArray(VAO);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices[0])*static_cast<uint>(vertices.size()), vertices.data(), GL_DYNAMIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3*sizeof(float), (void*)0);
glEnableVertexAttribArray(0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);
float FPS_sum = 0;
size_t frames = 0;
float CPS_sum = 0;
std::chrono::high_resolution_clock::time_point start;
// render loop
while(!glfwWindowShouldClose(window))
{
processInput(window);
//Begin CPS timer
start = std::chrono::high_resolution_clock::now();
//Computation Step
std::vector<point_bucket<Boid> > tree;
point_bucket<Boid> base(0, 0, 2, 2, flock.boids);
base_split(base, 16, tree, 20);
for(auto& elm: tree)
{
flock.Update(elm.bucket);
}
flock.Mirror();
CPS_sum+=1000.f/std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::high_resolution_clock::now()-start).count();
// rendering commands here
glClearColor(0.2f, 0.3f, 0.2f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
glUseProgram(shaderProgram);
//Begin FPS timer
start = std::chrono::high_resolution_clock::now();
glBindVertexArray(VAO);
updateVertices(flock.boids, window, vertices);
updateBuffer(VBO, 0, vertices.data(), sizeof(vertices[0])*static_cast<uint>(vertices.size()), GL_ARRAY_BUFFER);
size_t draw_running_total = vertices.size()/3;
GLint draw_offset = 0;
while(draw_running_total > 0)
{
glDrawArrays(GL_TRIANGLES, draw_offset, draw_running_total > draw_size? draw_size: draw_running_total);
draw_running_total -= draw_size;
draw_offset += draw_size;
}
// check and call events and swap the buffers
glfwSwapBuffers(window);
glfwPollEvents();
// PULL FPS FOR DRAW
FPS_sum+=1000.f/std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::high_resolution_clock::now()-start).count();
frames++;
if(frames==100)
{
std::cout << "Average across 100 frames... \n" << FPS_sum/frames << " FPS\n"
<< CPS_sum/frames << " CPS\n";
FPS_sum = 0;
CPS_sum = 0;
frames = 0;
}
//END PULL FOR FPS
}
void updateBuffer(uint &id, uint offset, void *data, uint size, GLenum shaderType)
{
glBindBuffer(shaderType, id);
glBufferSubData(shaderType, offset, size, data);
}
Personally looking online myself I was able to find questions like...
https://stackoverflow.com/questions/24099139/c-opengl-flickering-issues
... which seem only to deal with extraneous buffer swapping, whereas I only swap buffers at the end of the draw loop.
A reddit post addressed a similar issue and found a potential solution in a FBO, but still couldn't discover the root cause of the problem
Any help on would be greatly appreciated!
The GitHub repo for this project is also available for those interested.
I'm having my vertices clipped on the edged as shown on this album:
http://imgur.com/a/VkCrJ
When my terrain size if 400 x 400 i get clipping, yet at 40x40 or anything less, i don't get any clipping.
This is my code to fill the position and indices:
void Terrain::fillPosition()
{
//start from the top right and work your way down to 1,1
double x = -1, y = 1, z = 1;
float rowValue = static_cast<float>((1.0f / _rows) * 2.0); // .05 if 40
float colValue = static_cast<float>((1.0f / _columns) * 2.0); // .05 if 40
for (y; y > -1; y -= colValue)
{
for (x; x < 1; x += rowValue)
{
_vertexPosition.emplace_back(glm::vec3(x, y, z));
}
x = -1;
}
}
This properly sets my position, I've tested it with GL_POINTS. It works fine at 400x400 and 40x40 and other values in between.
Index code:
void Terrain::fillIndices()
{
glm::ivec3 triangle1, triangle2;
for (int y = 0; y < _columns - 1; y++)
{
for (int x = 0; x < _rows - 1; x++)
{
// Triangle 1
triangle1.x = x + y * _rows;
triangle1.y = x + (y + 1) * _rows;
triangle1.z =(x + 1) + y * _rows;
// Triangle 2
triangle2.x = triangle1.y;
triangle2.y = (x + 1) + (y + 1) * _rows;
triangle2.z = triangle1.z;
// add our data to the vector
_indices.emplace_back(triangle1.x);
_indices.emplace_back(triangle1.y);
_indices.emplace_back(triangle1.z);
_indices.emplace_back(triangle2.x);
_indices.emplace_back(triangle2.y);
_indices.emplace_back(triangle2.z);
}
}
}
_indices is std::vector.I'm not sure what's causing this, But I'm pretty sure it's the way I'm filling the indices for the mesh. I've re-written my algorhithm and it ends up with the same result, small values work perfectly fine, and large values over ~144 get clipped. I fill my buffers like this:
void Terrain::loadBuffers()
{
// generate the buffers and vertex arrays
glGenVertexArrays(1, &_vao);
glGenBuffers(1, &_vbo);
glGenBuffers(1, &_ebo);
// bind the vertex array
glBindVertexArray(_vao);
// bind the buffer to the vao
glBindBuffer(GL_ARRAY_BUFFER, _vbo);
glBufferData(GL_ARRAY_BUFFER, _vertexPosition.size() * sizeof(_vertexPosition[0]), _vertexPosition.data(), GL_STATIC_DRAW);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _ebo);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, _indices.size() * sizeof(_indices[0]), _indices.data(), GL_STATIC_DRAW);
// enable the shader locations
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, nullptr);
// unbind our data
glBindVertexArray(0);
}
and my draw call:
void Terrain::renderTerrain(ResourceManager& manager, ResourceIdTextures id)
{
// set the active texture
glActiveTexture(GL_TEXTURE0);
// bind our texture
glBindTexture(GL_TEXTURE_2D, manager.getTexture(id).getTexture());
_shaders.use();
// send data the our uniforms
glUniformMatrix4fv(_modelLoc, 1, GL_FALSE, glm::value_ptr(_model));
glUniformMatrix4fv(_viewLoc, 1, GL_FALSE, glm::value_ptr(_view));
glUniformMatrix4fv(_projectionLoc, 1, GL_FALSE, glm::value_ptr(_projection));
glUniform1i(_textureLoc, 0);
glBindVertexArray(_vao);
// Draw our terrain;
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
glDrawElements(GL_TRIANGLES, _indices.size(), GL_UNSIGNED_INT, 0);
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
glBindVertexArray(0);
_shaders.unuse();
}
I thought it was because of my transformations to the model, so i removed all transformations and it's the same result. I tried debugging by casting the glm::vec3 to_string but the data looks fine, My projectionMatrix is:
glm::perspective(glm::radians(_fov), _aspRatio, 0.1f, 1000.0f);
So i doubt it's my perspective doing the clipping. _aspRatio is 16/9.
It's really strange that it works fine with small rowsxcolumns and not large ones, I'm really not sure what the problem is.
I would check the length of _vertexPosition; I suspect the problem is that you are (depending on the number of _rows) generating an extra point at the end of your inner loop (and your outer loop too, depending on _columns).
The reason is that the termination condition of your vertex loops depends on the exact behavior of your floating point math. Specifically, you divide up the range [-1,1] into _rows segments, then add them together and use them as a termination test. It is unclear whether you expect a final point (yielding _rows+1 points per inner loop) or not (yielding a rectangle which doesn't cover the entire [-1,1] range). Unfortunately, floating point is not exact, so this is a recipe for unreliable behavior: depending on the direction of your floating point error, you might get one or the other.
For a larger number of _rows, you are adding more (and significantly smaller) numbers to the same initial value; this will aggravate your floating point error.
At any rate, in order to get reliable behavior, you should use integer loop variables to determine loop termination. Accumulate your floating point coordinates separately, so that exact accuracy is not required.
I am trying to create a heightmap from 25 float values like so:
#define HEIGHT_VERTS 5
#define VALS_PER_VERT_HEIGHT 5
float heightmapVerts[ HEIGHT_VERTS*VALS_PER_VERT_HEIGHT ] = {
//5
-0.9, -0.6, -0.4, -0.6, -0.9,
-0.2, 0.1, 0.3, 0.1, -0.3,
0, 0.4, 0.8, 0.4, 0,
-0.2, 0.1, 0.3, 0.1, -0.3,
0.5, -0.6, -0.4, -0.6, -0.9,
};
I am getting a segmentation fault when calling:
glDrawArrays(GL_TRIANGLES, 0, HEIGHT_VERTS);
I have been suggested that it's because the size argument of glVertexAttribPointer() must be 1, 2, 3, or 4. I pass 5 with:
glVertexAttribPointer(vertLocHeight, VALS_PER_VERT_HEIGHT, GL_FLOAT, GL_FALSE, 0, 0);
but I get another error saying that I have too many vertices if these values are smaller (eg: #define VALS_PER_VERT_HEIGHT 3)
error: too many initializers for ‘float [15]’
I have attached the rest of my code for some context, I am very very new to OpenGL so I apologize if the code is messy.
#include <stdio.h>
// GLEW loads OpenGL extensions. Required for all OpenGL programs.
#include <GL/glew.h>
#ifdef __APPLE__
#include <GLUT/glut.h>
#else
#include <GL/glut.h>
#endif
// Utility code to load and compile GLSL shader programs
#include "shader.hpp"
#include <iostream>
#include <fstream>
#include <vector>
#include "glm/glm.hpp"
#define WINDOW_WIDTH 400
#define WINDOW_HEIGHT 400
#define VALS_PER_VERT_HEIGHT 5//5
#define VALS_PER_COLOUR_HEIGHT 4
#define HEIGHT_VERTS 5 //height map vertices per line
using namespace std;
// Handle to our VAO generated in setShaderData method
//heightmap
unsigned int vertexVaoHandleHeight;
// Handle to our shader program
unsigned int programID;
/**
* Sets the shader uniforms and vertex data
* This happens ONCE only, before any frames are rendered
* #param id, Shader program object to use
* #returns 0 for success, error otherwise
*/
int setShaderData(const unsigned int &id)
{
float heightmapVerts[ HEIGHT_VERTS*VALS_PER_VERT_HEIGHT ] = {
//5
-0.9, -0.6, -0.4, -0.6, -0.9,
-0.2, 0.1, 0.3, 0.1, -0.3,
0, 0.4, 0.8, 0.4, 0,
-0.2, 0.1, 0.3, 0.1, -0.3,
0.5, -0.6, -0.4, -0.6, -0.9,
};
// Colours for each vertex; red, green, blue and alpha
// This data is indexed the same order as the vertex data, but reads 4 values
// Alpha will not be used directly in this example program
float heightColours[ HEIGHT_VERTS*VALS_PER_COLOUR_HEIGHT ] = {
0.8f, 0.7f, 0.5f, 1.0f,
0.3f, 0.7f, 0.1f, 1.0f,
0.8f, 0.2f, 0.5f, 1.0f,
};
// heightmap stuff ##################################################
// Generate storage on the GPU for our triangle and make it current.
// A VAO is a set of data buffers on the GPU
glGenVertexArrays(1, &vertexVaoHandleHeight);
glBindVertexArray(vertexVaoHandleHeight);
// Generate new buffers in our VAO
// A single data buffer store for generic, per-vertex attributes
unsigned int bufferHeight[2];
glGenBuffers(2, bufferHeight);
// Allocate GPU memory for our vertices and copy them over
glBindBuffer(GL_ARRAY_BUFFER, bufferHeight[0]);
glBufferData(GL_ARRAY_BUFFER, sizeof(float)*HEIGHT_VERTS*VALS_PER_VERT_HEIGHT, heightmapVerts, GL_STATIC_DRAW);
// Do the same for our vertex colours
glBindBuffer(GL_ARRAY_BUFFER, bufferHeight[1]);
glBufferData(GL_ARRAY_BUFFER, sizeof(float)*HEIGHT_VERTS*VALS_PER_COLOUR_HEIGHT, heightColours, GL_STATIC_DRAW);
// Now we tell OpenGL how to interpret the data we just gave it
// Tell OpenGL what shader variable it corresponds to
// Tell OpenGL how it's formatted (floating point, 3 values per vertex)
int vertLocHeight = glGetAttribLocation(id, "a_vertex");
glBindBuffer(GL_ARRAY_BUFFER, bufferHeight[0]);
glEnableVertexAttribArray(vertLocHeight);
glVertexAttribPointer(vertLocHeight, VALS_PER_VERT_HEIGHT, GL_FLOAT, GL_FALSE, 0, 0);
// Do the same for the vertex colours
int colourLocHeight = glGetAttribLocation(id, "a_colour");
glBindBuffer(GL_ARRAY_BUFFER, bufferHeight[1]);
glEnableVertexAttribArray(colourLocHeight);
glVertexAttribPointer(colourLocHeight, VALS_PER_COLOUR_HEIGHT, GL_FLOAT, GL_FALSE, 0, 0);
// heightmap stuff ##################################################
// An argument of zero un-binds all VAO's and stops us
// from accidentally changing the VAO state
glBindVertexArray(0);
// The same is true for buffers, so we un-bind it too
glBindBuffer(GL_ARRAY_BUFFER, 0);
return 0; // return success
}
/**
* Renders a frame of the state and shaders we have set up to the window
* Executed each time a frame is to be drawn.
*/
void render()
{
// Clear the previous pixels we have drawn to the colour buffer (display buffer)
// Called each frame so we don't draw over the top of everything previous
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glUseProgram(programID);
// HEIGHT MAP STUFF ###################################
// Make the VAO with our vertex data buffer current
glBindVertexArray(vertexVaoHandleHeight);
// Send command to GPU to draw the data in the current VAO as triangles
//CRASHES HERE
glDrawArrays(GL_TRIANGLES, 0, HEIGHT_VERTS);
glBindVertexArray(0); // Un-bind the VAO
// HEIGHT MAP STUFF ###################################
glutSwapBuffers(); // Swap the back buffer with the front buffer, showing what has been rendered
glFlush(); // Guarantees previous commands have been completed before continuing
}
You are messing things up.
1) With glVertexAttribPointer(), you setup vertex attributes - those are almost always vectors of some kind. For vertex position, if you need to draw scene in 2D, pass size = 2 (because each vertex has x and y coordinates), for 3D - pass 3 (x, y, z).
2) I think your interpretation of heightmap is also quite incorrect. You filled array only with height values (in 3D space, those are Y coordinates). But where are X and Z? You need to render vertices, so you need to pass all x, y and z coords, so OpenGL can know where each point should be rendered.
Your program crashes, because you send not enough data and OpenGL tries to read from memory, that doesn't belong to you.
I assume, that you want a heightmap, which is a 5x5 grid? Init data this way:
float heightmapVerts[25] =
{
//leave this as it is right now
};
vec3 vertices[5][5];
for(int z_num = 0; z_num < 5; ++z_num)
{
for(int x_num = 0; x_num < 5; ++x_num)
{
vertices[z_num][x_num].x = x_num * 0.5f;
vertices[z_num][x_num].z = z_num * 0.5f;
vertices[z_num][x_num].y = heightmapVerts[z_num * 5 + x_num];
}
}
Then you can call:
glVertexAttribPointer(vertLocHeight, 3, GL_FLOAT, GL_FALSE, 0, 0);
Update:
vec3 stands for 3-dimensional vector. I wrote it as pseudocode to
illustrate conception, but for the sake of simplicity, you may want to
use this great library: OpenGL Mathematics.
Update 2:
One more thing: color data is also set improperly. You probably want
color to be an RGB value, so every vertex needs additional 3 floats to
represent its color. Also, position and color should be place in
single VBO, there is no need for separating them.
I am not sure, if you got all basics required to do any simple
drawings. You may want to read these articles:
OpenGL Wiki
This nice tutorial
Lighthouse 3D
I have a simple OpenGL program which I am trying to utilize Vertex Buffer Objects for rendering instead of the old glBegin() - glEnd(). Basically the user clicks on the window indicating a starting point, and then presses a key to generate subsequent points which OpenGL draws as a line.
I've implemented this using glBegin() and glEnd() but have not been successful using a VBO. I am wondering if the problem is that after I initialize the VBO, I'm adding more vertices which it doesn't have memory allocated for, and thus doesn't display them.
Edit: Also, I'm a bit confused as to how it knows exactly which values in the vertex struct to use for x and y, as well as for r, g, b. I haven't been able to find a clear example of this.
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <Math.h>
#include <iostream>
#include <vector>
#include <GL/glew.h>
#include <GL/glut.h>
struct vertex {
float x, y, u, v, r, g, b;
};
const int D = 10; // distance
const int A = 10; // angle
const int WINDOW_WIDTH = 500, WINDOW_HEIGHT = 500;
std::vector<vertex> vertices;
boolean start = false;
GLuint vboId;
void update_line_point() {
vertex temp;
temp.x = vertices.back().x + D * vertices.back().u;
temp.y = vertices.back().y + D * vertices.back().v;
temp.u = vertices.back().u;
temp.v = vertices.back().v;
vertices.push_back(temp);
}
void update_line_angle() {
float u_prime, v_prime;
u_prime = vertices.back().u * cos(A) - vertices.back().v * sin(A);
v_prime = vertices.back().u * sin(A) + vertices.back().v * cos(A);
vertices.back().u = u_prime;
vertices.back().v = v_prime;
}
void initVertexBuffer() {
glGenBuffers(1, &vboId);
glBindBuffer(GL_ARRAY_BUFFER, vboId);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertex) * vertices.size(), &vertices[0], GL_STATIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, 0);
}
void displayCB() {
glClear(GL_COLOR_BUFFER_BIT);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluOrtho2D(0, WINDOW_WIDTH, 0, WINDOW_HEIGHT);
if (start) {
glBindBuffer(GL_ARRAY_BUFFER, vboId);
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_COLOR_ARRAY);
glVertexPointer(2, GL_FLOAT, sizeof(vertex), &vertices[0]);
glColorPointer(3, GL_FLOAT, sizeof(vertex), &vertices[0]);
glDrawArrays(GL_LINE_STRIP, 0, vertices.size());
glDisableClientState(GL_VERTEX_ARRAY);
glDisableClientState(GL_COLOR_ARRAY);
glBindBuffer(GL_ARRAY_BUFFER, 0);
}
/***** this is what I'm trying to achieve
glColor3f(1, 0, 0);
glBegin(GL_LINE_STRIP);
for (std::vector<vertex>::size_type i = 0; i < vertices.size(); i++) {
glVertex2f(vertices[i].x, vertices[i].y);
}
glEnd();
*****/
glFlush();
glutSwapBuffers();
}
void mouseCB(int button, int state, int x, int y) {
if (state == GLUT_DOWN) {
vertices.clear();
vertex temp = {x, WINDOW_HEIGHT - y, 1, 0, 1, 0, 0}; // default red color
vertices.push_back(temp);
start = true;
initVertexBuffer();
}
glutPostRedisplay();
}
void keyboardCB(unsigned char key, int x, int y) {
switch(key) {
case 'f':
if (start) {
update_line_point();
}
break;
case 't':
if (start) {
update_line_angle();
}
break;
}
glutPostRedisplay();
}
void initCallbackFunc() {
glutDisplayFunc(displayCB);
glutMouseFunc(mouseCB);
glutKeyboardFunc(keyboardCB);
}
int main(int argc, char** argv) {
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_RGB|GLUT_DOUBLE|GLUT_DEPTH);
glutInitWindowSize(WINDOW_WIDTH, WINDOW_HEIGHT);
glutInitWindowPosition(100, 100);
glutCreateWindow("Test");
initCallbackFunc();
// initialize glew
GLenum glewInitResult;
glewExperimental = GL_TRUE;
glewInitResult = glewInit();
if (GLEW_OK != glewInitResult) {
std::cerr << "Error initializing glew." << std::endl;
return 1;
}
glClearColor(1, 1, 1, 0);
glutMainLoop();
return 0;
}
If you have a VBO bound then the pointer argument to the gl*Pointer() calls is interpreted as a byte offset from the beginning of the VBO, not an actual pointer. Your usage is consistent with vertex array usage though.
So for your vertex struct x starts at byte zero and r starts at byte sizeof(float) * 4.
Also, your mouse callback reset your vertex vector on every call so you would never be able have more than one vertex in it at any given time. It also leaked VBO names via the glGenBuffers() in initVertexBuffer().
Give this a shot:
#include <GL/glew.h>
#include <GL/glut.h>
#include <iostream>
#include <vector>
struct vertex
{
float x, y;
float u, v;
float r, g, b;
};
GLuint vboId;
std::vector<vertex> vertices;
void mouseCB(int button, int state, int x, int y)
{
y = glutGet( GLUT_WINDOW_HEIGHT ) - y;
if (state == GLUT_DOWN)
{
vertex temp = {x, y, 1, 0, 1, 0, 0}; // default red color
vertices.push_back(temp);
glBindBuffer(GL_ARRAY_BUFFER, vboId);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertex) * vertices.size(), &vertices[0], GL_STATIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, 0);
}
glutPostRedisplay();
}
void displayCB()
{
glClearColor(1, 1, 1, 0);
glClear(GL_COLOR_BUFFER_BIT);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
double w = glutGet( GLUT_WINDOW_WIDTH );
double h = glutGet( GLUT_WINDOW_HEIGHT );
glOrtho( 0, w, 0, h, -1, 1 );
glMatrixMode( GL_MODELVIEW );
glLoadIdentity();
if ( vertices.size() > 1 )
{
glBindBuffer(GL_ARRAY_BUFFER, vboId);
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_COLOR_ARRAY);
glVertexPointer(2, GL_FLOAT, sizeof(vertex), (void*)(sizeof( float ) * 0));
glColorPointer(3, GL_FLOAT, sizeof(vertex), (void*)(sizeof( float ) * 4));
glDrawArrays(GL_LINE_STRIP, 0, vertices.size());
glDisableClientState(GL_VERTEX_ARRAY);
glDisableClientState(GL_COLOR_ARRAY);
glBindBuffer(GL_ARRAY_BUFFER, 0);
}
glutSwapBuffers();
}
int main(int argc, char** argv)
{
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_RGB|GLUT_DOUBLE|GLUT_DEPTH);
glutInitWindowSize(500, 500);
glutInitWindowPosition(100, 100);
glutCreateWindow("Test");
// initialize glew
glewExperimental = GL_TRUE;
GLenum glewInitResult = glewInit();
if (GLEW_OK != glewInitResult) {
std::cerr << "Error initializing glew." << std::endl;
return 1;
}
glGenBuffers(1, &vboId);
glutDisplayFunc(displayCB);
glutMouseFunc(mouseCB);
glutMainLoop();
return 0;
}
A VBO is a buffer located somewhere in memory (almost always in dedicated GPU memory - VRAM) of a fixed size. You specify this size in glBufferData, and you also simultaneously give the GL a pointer to copy from. The key word here is copy. Everything you do to the vector after glBufferData isn't reflected in the VBO.
You should be binding and doing another glBufferData call after changing the vector. You will also probably get better performance from glBufferSubData or glMapBuffer if the VBO is already large enough to handle the new data, but in a small application like this the performance hit of calling glBufferData every time is basically non-existent.
Also, to address your other question about the values you need to pick out x, y, etc. The way your VBO is set up is that the values are interleaved. so in memory, your vertices will look like this:
+-------------------------------------------------
| x | y | u | v | r | g | b | x | y | u | v | ...
+-------------------------------------------------
You tell OpenGL where your vertices and colors are with the glVertexPointer and glColorPointer functions respectively.
The size parameter specifies how many elements there are for each vertex. In this case, it's 2 for vertices, and 3 for colors.
The type parameter specifies what type each element is. In your case it's GL_FLOAT for both.
The stride parameter is how many bytes you need to skip from the start of one vertex to the start of the next. With an interleaved setup like yours, this is simply sizeof(vertex) for both.
The last parameter, pointer, isn't actually a pointer to your vector in this case. When a VBO is bound, pointer becomes a byte offset into the VBO. For vertices, this should be 0, since the first vertex starts at the very first byte of the VBO. For colors, this should be 4 * sizeof(float), since the first color is preceded by 4 floats.
I'm having a problem currently with a particle engine I'm making. With the engine you can add more than one emitter in to the engine, the idea being that each particle system can emit its own particles.
The problem I'm getting however is that when I add a second particle system, the drawing of the first seems to be affected, by which I mean it's not drawn at all. The draw call of each particle system is being called correctly.
What I am thinking the issue is however is that although multiple VBOs are created, only one is actually used.
I'll show the important parts of my functions that affect the VBOs. My shader uses a uniform location to store WVP matrices. I should also mention each particle system should be using its own shader program.
This below is my initializeBuffers function called when the particle system is created:
void ParticleSystem::InitializeBuffers()
{
glGenVertexArrays(1, &VaoId);
glBindVertexArray(VaoId);
//glGenBuffers(1, &VboId);
glGenBuffers(1, &PositionBufferId);
glGenBuffers(1, &IndexBufferId);
glGenBuffers(1, &WVPId);
std::list<Particle>::iterator iterator = particles.begin();
//positions.reserve(5);
for (std::list<Particle>::iterator iterator = particles.begin(), end = particles.end(); iterator != end; ++iterator)
{
positions.push_back(iterator->GetPosition());
//verticesToDraw.insert(verticesToDraw.end(), iterator->GetVertices()->begin(), iterator->GetVertices()->end());
indicesToDraw.insert(indicesToDraw.end(), iterator->GetIndices()->begin(), iterator->GetIndices()->end());
}
//glBindBuffer(GL_ARRAY_BUFFER, VboId);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, IndexBufferId);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indicesToDraw[0]) * indicesToDraw.size(), &indicesToDraw[0], GL_STATIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, WVPId);
for (unsigned int i = 0; i < 4 ; i++) {
glEnableVertexAttribArray(WVP_LOCATION + i);
glVertexAttribPointer(WVP_LOCATION + i, 4, GL_FLOAT, GL_FALSE, sizeof(Matrix4f), (const GLvoid*)(sizeof(GLfloat) * i * 4));
glVertexAttribDivisor(WVP_LOCATION + i, 1);
}
for(std::list<BaseBuildingBlock*>::iterator iterator = buildingBlocks.begin(), end = buildingBlocks.end(); iterator != end; ++iterator)
{
(*iterator)->InitializeBuffer(programId);
}
/*
glBindBuffer(GL_ARRAY_BUFFER, WorldId);
for (unsigned int i = 0; i < 4 ; i++) {
glEnableVertexAttribArray(WORLD_LOCATION + i);
glVertexAttribPointer(WORLD_LOCATION + i, 4, GL_FLOAT, GL_FALSE, sizeof(Matrix4f), (const GLvoid*)(sizeof(GLfloat) * i * 4));
glVertexAttribDivisor(WORLD_LOCATION + i, 1);
}
*/
//return GLCheckError();
}
This is the draw function and the code that actually draws the instanced elements, the wvp matrices are formed by the particle system earlier in the function.
void ParticleSystem::Draw(Matrix4f perspectiveCameraMatrix)
{
// scale TEST
//GLint gScaleLocation = glGetUniformLocation(program, "gScale");
//assert(gScaleLocation != 0xFFFFFFFF);
//glUniform1f(gScaleLocation, scale);
//Pipeline p;
//Matrix4f* WVPMatrices = new Matrix4f[particles.size()];
//Matrix4f* WorldMatrices = new Matrix4f[particles.size()];
WVPMatrices.clear();
WorldMatrices.clear();
glUseProgram(0);
glUseProgram(programId);
//Matrix4f perspectiveMatrix;
//perspectiveMatrix.BuildPerspProjMat(90,1, 0.01, 200, 100 - 0 /*getWidth() / 32*/, 100 - 0 /*getHeight() / 32*/);
//********************************************************************************************************
// Method 1
// Think I need to next define a camera position.
if(particles.size() == 0)
{
return;
}
verticesToDraw.clear();
Matrix4f scaleMatrix;
Matrix4f worldMatrix;
Matrix4f rotateMatrix;
Matrix4f finalMatrix;
//ColourId = glGetUniformLocation(programId, "UniformColour");
int i = 0;
for (std::list<Particle>::iterator iterator = particles.begin(), end = particles.end(); iterator != end; ++iterator)
{
verticesToDraw = *iterator->GetVertices();
indicesToDraw = *iterator->GetIndices();
//positions.push_back(iterator->GetPosition());
worldMatrix.InitTranslationTransform(iterator->GetPosition().x, iterator->GetPosition().y, iterator->GetPosition().z);
rotateMatrix.InitRotateTransform(iterator->GetRotation().x, iterator->GetRotation().y, iterator->GetRotation().z);
scaleMatrix.InitScaleTransform(iterator->GetScale().x, iterator->GetScale().y, iterator->GetScale().z);
finalMatrix = perspectiveCameraMatrix * worldMatrix * rotateMatrix * scaleMatrix;
//p.WorldPos(iterator->GetPosition());
//p.Rotate(iterator->GetRotation());
WVPMatrices.push_back(finalMatrix.Transpose());
/*glUniform4f(ColourId, iterator->GetColour().r, iterator->GetColour().g, iterator->GetColour().b,
iterator->GetColour().a);*/
//WorldMatrices[i] = p.GetWorldTrans();
i++;
//iterator->Draw();
}
//glEnableVertexAttribArray(0);
if(colourOverLifeBuildingBlock != NULL)
{
colourOverLifeBuildingBlock->Test();
}
glBindBuffer(GL_ARRAY_BUFFER, VboId);
glBufferData(GL_ARRAY_BUFFER, verticesToDraw.size() * sizeof(verticesToDraw[0]), &verticesToDraw.front(), GL_STATIC_DRAW);
glEnableVertexAttribArray(POSITION_LOCATION);
glVertexAttribPointer(POSITION_LOCATION, 3, GL_FLOAT, GL_FALSE, 0, 0);
int size = particles.size();
glBindBuffer(GL_ARRAY_BUFFER, WVPId);
glBufferData(GL_ARRAY_BUFFER, sizeof(Matrix4f) * size, &WVPMatrices.front(), GL_DYNAMIC_DRAW);
glDrawElementsInstanced(GL_TRIANGLES, indicesToDraw.size(), GL_UNSIGNED_BYTE, 0, particles.size());
glBindBuffer(GL_ARRAY_BUFFER, 0);
//glDisableVertexAttribArray(0);
//glFlush();
}
The particle system entire header is below:
#include <gl\glew.h>
#include <array>
#include <vector>
class ParticleSystem
{
public:
ParticleSystem(Vector3 pos, Quaternion rot, float spawnRate, int particlesToSpawn); // Constructs a particle system.
~ParticleSystem(); // Destructor.
void Update(float elapsedTime); // Updates the particle system.
void Draw(Matrix4f perspectiveMatrix); // Draw the particle system
void CreateShaders();
void InitializeBuffers();
// Long amount of get sets.
/*float* GetMinLifeTime();
void SetMinLifeTime(float lt);
float* GetMaxLifeTime();
void SetMaxLifeTime(float lt);*/
int* GetParticlesToSpawnAtATime();
void SetParticlesToSpawnAtATime(int particlesToSpawn);
float* GetSpawnRate();
void SetSpawnRate(float spawnRate);
Vector3* GetPosition();
void SetPosition(Vector3 newPosition);
Quaternion* GetRotation();
void SetRotation(Quaternion rotation);
std::list<BaseBuildingBlock*> GetBuildingBlocks();
VelocityBuildingBlock* GetVelocityBuilding();
ColourOverLifeBuildingBlock* GetColourOverLifeBuildingBlock();
LifeTimeBuildingBlock* GetLifeTimeBuildingBlock();
UniformColourBuildingBlock* GetUniformColourBuildingBlock();
ScaleBuildingBlock* GetScaleBuildingBlock();
/*Vector3* GetMinVelocity();
void SetMinVelocity(Vector3 min);
Vector3* GetMaxVelocity();
void SetMaxVelocity(Vector3 maxVelocity);*/
Vector3 GetMinParticleStartPoint();
void SetMinParticleStartPoint(Vector3 minParticleStartPoint);
Vector3 GetMaxParticleStartPoint();
void SetMaxParticleStartPoint(Vector3 maxParticleStartPoint);
bool CreateColourOverLifeBuildingBlock();
bool DeleteColourOverLifeBuildingBlock();
bool CreateUniformColourBuildingBlock();
bool DeleteUniformColourBuildingBlock();
bool CreateScaleBuildingBlock();
bool DeleteScaleBuildingBlock();
/*Colour GetStartColour();
void SetStartColour(Colour startColour);
Colour GetEndColour();
void SetEndColour(Colour endColour);*/
Vector3* GetMinParticleRotationAmountPerFrame();
void SetMinParticleRotationAmountPerFrame(Vector3 minParticleRotationAmount);
Vector3* GetMaxParticleRotationAmountPerFrame();
void SetMaxParticleRotationAmountPerFrame(Vector3 maxParticleRotationAmount);
void Save(TiXmlElement* element);
private:
// Spawns a particle.
void SpawnParticle();
GLuint VaoId;
GLuint VboId;
GLuint IndexBufferId;
GLuint PositionBufferId;
GLuint WVPId;
GLenum programId;
std::vector<GLfloat> verticesToDraw;
std::vector<GLubyte> indicesToDraw;
std::vector<Vector3> positions;
std::vector<Matrix4f> WVPMatrices;
std::vector<Matrix4f> WorldMatrices;
std::list<Particle> particles; // List of particles
Vector3 position; // position of the emitter
Quaternion rotation; // rotation of the emitter.
float spawnRate; // spawnrate of the emitter.
int particlesToSpawnAtATime; // The amount of particles to spawn at a time.
float minLifeTime; // The minimum time a particle can live for.
float maxLifeTime; // The maximum time a particle can live for.
float timer; // Timer
ShaderCreator* shaderCreator;
//Vector3 minVelocity; // The minimum velocity a particle can have.
//Vector3 maxVelocity; // The maximum velocity a particle can have/
//std::list<BaseBuildingBlock> buildingBlocks;
// I'm thinking of eventually making a list of baseBuildingBlocks.
std::list<BaseBuildingBlock*> buildingBlocks;
VelocityBuildingBlock* velocityBuildingBlock;
ColourOverLifeBuildingBlock* colourOverLifeBuildingBlock;
LifeTimeBuildingBlock* lifeTimeBuildingBlock;
UniformColourBuildingBlock* uniformColourBuildingBlock;
ScaleBuildingBlock* scaleBuildingBlock;
Vector3 minParticleStartPoint; // The minimum position a particle can start at.
Vector3 maxParticleStartPoint; // The maximum position a particle can start at.
Vector3 minParticleRotationAmountPerFrame; // The minimum amount of rotation that a particle can rotate every frame.
Vector3 maxParticleRotationAmountPerFrame; // The maximum amount of rotation that a particle can rotate every frame.
Colour startColour; // StartColour is the colour that a particle will start with.
Colour endColour; // EndColour is the colour that a particle will end with.
//TEST
float scale;
};
#endif
Now I'm wondering, is there some way I have to switch the active VBO? or am I totally on the wrong track. I used a shader debugger and both VBOs defiantely exist.
you'll need to correctly set up your vertex attribs before each draw call - i.e., you have to call glBindBuffer followed by glEnableVertexArray & glVertexAttribPointer for each of your attributes before each draw call. in the code you posted, this happens only for the particle position, but not for the 'WVP_LOCATION' attribute which apparently contains your transformation matrices ( you do upload the data to the GPU via glBufferData, but don't set up the attribute ) - meaning that once you have more than one particle system, only the transformation matrices of your second particle system are ever going to be accessed for rendering.
one a side not, what you're trying to do here seems to be quite inefficient - you're essentially pushing one transformation matrix to the GPU for each of your particles, per frame. Depending on how many particles you want, this is going to kill your performance - you should consider updating the particle's position etc. with a transform feedback.
edit: just realized that the opengl wiki link doen't really explain a lot. a transform feedback is a way to record vertex shader outputs ( or, if a geometry / tessellation shader were present, that output would be recorded instead ). The output variables are written into a VBO - afterwards, they can be used for rendering like any other vertex attribute. The whole concept is extremely similar to using a framebuffer object for recording fragment shader outputs; It allows for particle systems that exist entirely on the GPU, with a vertex shader computing the updated position, life time & other attributes in each frame. A very nice tutorial, which shows the basic setup of such a transform feedback, can be found here