Related
I have two models: Grid and cube, i set local vertexes for them and try to draw, but my grid is too small. I tried to scale model matrix for grid, but my cubes became huge. My grid inside the cube
Where is mistake? I want to scale grid in order to size of cell was the same as face of cube and I'll can place the cubes inside this cells
My Drawing code
auto functions = this->context()->functions();
//auto additionalFunctions = this->context()->extraFunctions();
functions->glClearColor(0.0f / 255.0f, 25.0f / 255.0f, 53.0f / 255.0f, 1.0f);
functions->glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
QMatrix4x4 projection;
projection.perspective(Zoom, 800.0f / 600.0f, 0.1f, 100.0f);
QMatrix4x4 view;
view.lookAt(cameraPos,
cameraPos + cameraFront,
cameraUp);
unsigned int viewID = m_program->uniformLocation("view");
functions->glUniformMatrix4fv(viewID, 1, GL_FALSE, view.constData());
unsigned int projectionID = m_program->uniformLocation("projection");
functions->glUniformMatrix4fv(projectionID, 1, GL_FALSE, projection.constData());
QVector3D cubePositions[] = {
QVector3D(0.0f, 0.0, 0.0),
QVector3D( 0.5f, 0.0f, 0.5f),
QVector3D( 2.0f, 0.0f, 3.0f),
QVector3D(-1.5f, -2.2f, -2.5f),
QVector3D(-3.8f, -2.0f, -12.3f),
QVector3D( 2.4f, -0.4f, -3.5f),
QVector3D(-1.7f, 3.0f, -7.5f),
QVector3D( 1.3f, -2.0f, -2.5f),
QVector3D( 1.5f, 2.0f, -2.5f),
QVector3D( 1.5f, 0.2f, -1.5f),
QVector3D(-1.3f, 1.0f, -1.5f)
};
for(unsigned int i = 0; i < 2; i++)
{
QMatrix4x4 model(1.0f, 0.0f, 0.0f, 0.0f,
0.0f, 1.0f, 0.0f, 0.0f,
0.0f, 0.0f, 1.0f, 0.0f,
0.0f, 0.0f, 0.0f, 1.0f);
unsigned int modelID = m_program->uniformLocation("model");
std::cout << "I: " << i << " x: " << cubePositions[i].x() << " y: " << cubePositions[i].y() << " z: " << cubePositions[i].z() << std::endl;
model.translate(cubePositions[i]);
//model.scale(1/10);/*Fix Later, it is non const value*/
functions->glUniformMatrix4fv(modelID, 1, GL_FALSE, model.constData());
auto cube3d = new invar::geometry3D::Cube(cubePositions[i], 1*qSqrt(3), m_program);
cube3d->Draw();
}
auto additionalFunctions = this->context()->extraFunctions();
additionalFunctions->glBindVertexArray(0);
/*GRID*/
unsigned int viewID2 = gridShaderProgram->uniformLocation("view");
functions->glUniformMatrix4fv(viewID2, 1, GL_FALSE, view.constData());
unsigned int projectionID2 = gridShaderProgram->uniformLocation("projection");
functions->glUniformMatrix4fv(projectionID2, 1, GL_FALSE, projection.constData());
QMatrix4x4 model2(1.0f, 0.0f, 0.0f, 0.0f,
0.0f, 1.0f, 0.0f, 0.0f,
0.0f, 0.0f, 1.0f, 0.0f,
0.0f, 0.0f, 0.0f, 1.0f);
unsigned int modelID2 = gridShaderProgram->uniformLocation("model");
//model2.scale(10.0f);
model2.translate(QVector3D(0.0f, 0.0f, 0.0f));
functions->glUniformMatrix4fv(modelID2, 1, GL_FALSE, model2.constData());
gridShaderProgram->bind();
additionalFunctions->glBindVertexArray(gridVAO);
functions->glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, gridEBO);
functions->glDrawElements(GL_LINES, lenght, GL_UNSIGNED_INT, 0);
Cube drawing
void Cube::Draw()
{
auto context = QOpenGLContext::currentContext();
auto functions = context->functions();
auto additionalFunctions = context->extraFunctions();
m_program->bind();
additionalFunctions->glBindVertexArray(VAO);
functions->glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
int size;
functions->glGetBufferParameteriv(GL_ELEMENT_ARRAY_BUFFER, GL_BUFFER_SIZE, &size);
functions->glDrawElements(GL_TRIANGLES, size/sizeof(float), GL_UNSIGNED_INT, 0);
}
void Cube::setupShape()
{
auto context = QOpenGLContext::currentContext();
auto functions = context->functions();
auto additionalFunctions = context->extraFunctions();
float vertices[] = {
-0.5f,0.5f,-0.5f, 0.0f, 0.0f, 0.0f,
-0.5f,0.5f,0.5f, 0.0f, 0.0f, 1.0f,
0.5f,0.5f,-0.5f, 0.0f, 1.0f, 0.0f,
0.5f,0.5f,0.5f, 0.0f, 1.0f, 1.0f,
-0.5f,-0.5f,-0.5f, 1.0f, 0.0f, 0.0f,
-0.5f,-0.5f,0.5f, 1.0f, 0.0f, 1.0f,
0.5f,-0.5f,-0.5f, 1.0f, 1.0f, 0.0f,
0.5f,-0.5f,0.5f, 1.0f, 1.0f, 1.0f
};
unsigned int indices[] = {
0,1,2,
1,2,3,
4,5,6,
5,6,7,
0,1,5,
0,4,5,
2,3,7,
2,6,7,
0,2,6,
0,4,6,
1,5,7,
1,3,7
};
functions->glGenBuffers(1, &EBO);
functions->glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
functions->glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
unsigned int VBO;
functions->glGenBuffers(1, &VBO);
additionalFunctions->glGenVertexArrays(1, &VAO);
additionalFunctions->glBindVertexArray(VAO);
functions->glBindBuffer(GL_ARRAY_BUFFER, VBO);
functions->glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
functions->glEnableVertexAttribArray(0);/*Check if need to normilize*/
functions->glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float),(void*)0);
functions->glEnableVertexAttribArray(1);
functions->glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float),
(void*)(3*sizeof(float)));
additionalFunctions->glBindVertexArray(0);
}
Cube::Cube(QVector3D pos, float diagonal, QOpenGLShaderProgram* m_program):
pos(pos), diagonal(diagonal), m_program(m_program)
{
this->setupShape();
}
Grid Drawing
auto additionalFunctions = this->context()->extraFunctions();
unsigned int gridVBO;
std::vector<QVector3D> vertices;
std::vector<unsigned int> indices;
int slices = 10;
for(int j=0; j<=slices; ++j) {
for(int i=0; i<=slices; ++i) {
float x = (float)i/(float)slices;
float y = 0;
float z = (float)j/(float)slices;
vertices.push_back(QVector3D(x, y, z));
}
}
for(int j=0; j<slices; ++j) {
for(int i=0; i<slices; ++i) {
int row1 = j * (slices+1);
int row2 = (j+1) * (slices+1);
indices.push_back(row1+i); indices.push_back(row1+i+1); indices.push_back(row1+i+1); indices.push_back(row2+i+1);
indices.push_back(row2+i+1); indices.push_back(row2+i); indices.push_back(row2+i); indices.push_back(row1+i);
}
}
lenght = (GLuint)indices.size()*4;
functions->glGenBuffers(1, &gridEBO);
functions->glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, gridEBO);
functions->glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.size()*sizeof(unsigned int), indices.data(), GL_STATIC_DRAW);
functions->glGenBuffers(1, &gridVBO);
additionalFunctions->glGenVertexArrays(1, &gridVAO);
additionalFunctions->glBindVertexArray(gridVAO);
functions->glBindBuffer(GL_ARRAY_BUFFER, gridVBO);
functions->glBufferData(GL_ARRAY_BUFFER, vertices.size()*sizeof(QVector3D), vertices.data(), GL_STATIC_DRAW);
functions->glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3*sizeof(float), nullptr);
functions->glEnableVertexAttribArray(0);
gridShaderProgram = new QOpenGLShaderProgram(this);
QOpenGLShader vxShader2(QOpenGLShader::Vertex);
vxShader2.compileSourceFile("/main/stage/home/andreyp/fork_invar/invar/shaders/shaderGrid.vs");
QOpenGLShader frShader2(QOpenGLShader::Fragment);
frShader2.compileSourceFile("/main/stage/home/andreyp/fork_invar/invar/shaders/shaderGrid.fs");
gridShaderProgram->addShader(&vxShader2);
gridShaderProgram->addShader(&frShader2);
gridShaderProgram->link();
If I scale by 10 model matrix of grid I see this
Why after //model2.scale(10.0f); I get one big cube? How to scale grid so, that one cell was equal side of cube?
I'm going through learnopengl.com and trying to make a 3d isometric scene.
I've got it working quite nicely so far.
But something not in the learnopengl course is how to detect mouse clicks in 3d space.
I'm using instanced drawing using a vector of mat4 for drawing each cube of the world.
What I would like to do next is click on each cube and modify it (for now just change the z position).
I've found some articles on using glReadPixels but because of my isometric projection, the x &/or y co-ordinates don't seem correct.
Also, since i'm using instanced drawing, i'm not sure how i will figure out which instance in the vector was clicked on.
My code is on git hub here.
And here is the main class I'm using for drawing the cubes:
#include "worldTile.h"
worldTile::worldTile() : drawable("instance.shader.vs", "instance.shader.fs")
{
drawType = GL_STATIC_DRAW;
baseModel = glm::mat4(1.0f);
baseModel = glm::rotate(baseModel, glm::radians(-45.0f), glm::vec3(1.0f, 0.0f, 0.0f));
baseModel = glm::rotate(baseModel, glm::radians(0.0f), glm::vec3(0.0f, 1.0f, 0.0f));
baseModel = glm::rotate(baseModel, glm::radians(-45.0f), glm::vec3(0.0f, 0.0f, 1.0f));
}
worldTile::~worldTile()
{
cleanup();
}
void worldTile::cleanup()
{
}
void worldTile::setup()
{
float blockSize = 0.81f;
int worldSize = 2;
// 50 gives 15-16fps using drawElements
// 50 gives 480fps using drawElementsInstanced
// 50 gives 380fps using matrix drawElementsInstanced
for (int i = 0; i < worldSize; i++)
{
for (int j = 0; j < worldSize; j++)
{
float xPos = i * blockSize;
float yPos = j * blockSize;
glm::mat4 model = baseModel;
model = glm::translate(baseModel, glm::vec3(xPos, yPos, 0.0f));
modelMatrices.push_back(model);
if (i != 0)
{
model = glm::translate(baseModel, glm::vec3(-xPos, yPos, 0.0f));
modelMatrices.push_back(model);
}
if (j != 0)
{
model = glm::translate(baseModel, glm::vec3(xPos, -yPos, 0.0f));
modelMatrices.push_back(model);
}
if (i != 0 && j != 0)
{
model = glm::translate(baseModel, glm::vec3(-xPos, -yPos, 0.0f));
modelMatrices.push_back(model);
}
}
}
//modelMatrices[0] = glm::translate(modelMatrices[0], glm::vec3(0, 0, 0.2f));
colors = {
glm::vec3(0.55f, 0.71f, 0.29f), // 0 top green
glm::vec3(0.49f, 0.64f, 0.26f), // 1 front green
glm::vec3(0.45f, 0.59f, 0.24f), // 2 side green
glm::vec3(0.68f, 0.51f, 0.34f), // 3 brown
glm::vec3(0.73f, 0.55f, 0.36f), // 4 front brown
glm::vec3(0.62f, 0.47f, 0.35f), // 5 side brown
};
vertices = {
// top face
-0.4f, -0.4f, 0.1f, // 0
0.4f, -0.4f, 0.1f,
0.4f, 0.4f, 0.1f,
-0.4f, 0.4f, 0.1f,
// bottom face
-0.4f, -0.4f, -0.1f, // 4
0.4f, -0.4f, -0.1f,
0.4f, 0.4f, -0.1f,
-0.4f, 0.4f, -0.1f,
// left face - bottom
-0.4f, 0.4f, 0.0f, // 8
-0.4f, 0.4f, -0.1f,
-0.4f, -0.4f, -0.1f,
-0.4f, -0.4f, 0.0f,
// left face - top
-0.4f, 0.4f, 0.1f, // 12
-0.4f, 0.4f, -0.0f,
-0.4f, -0.4f, -0.0f,
-0.4f, -0.4f, 0.1f,
// right face - bottom
0.4f, 0.4f, 0.0f, // 16
0.4f, 0.4f, -0.1f,
0.4f, -0.4f, -0.1f,
0.4f, -0.4f, 0.0f,
// right face - top
0.4f, 0.4f, 0.1f, // 20
0.4f, 0.4f, -0.0f,
0.4f, -0.4f, -0.0f,
0.4f, -0.4f, 0.1f,
// left side face - bottom
-0.4f, -0.4f, -0.1f, // 24
0.4f, -0.4f, -0.1f,
0.4f, -0.4f, 0.0f,
-0.4f, -0.4f, 0.0f,
// left side face - top
-0.4f, -0.4f, -0.0f, // 28
0.4f, -0.4f, -0.0f,
0.4f, -0.4f, 0.1f,
-0.4f, -0.4f, 0.1f,
// back face - bottom
-0.4f, 0.4f, -0.1f, // 32
0.4f, 0.4f, -0.1f,
0.4f, 0.4f, 0.0f,
-0.4f, 0.4f, 0.0f,
// back face - top
-0.4f, 0.4f, -0.0f, // 36
0.4f, 0.4f, -0.0f,
0.4f, 0.4f, 0.1f,
-0.4f, 0.4f, 0.1f,
};
indices = { // note that we start from 0!
0, 1, 3, // first Triangle
1, 2, 3, // second Triangle
4, 5, 7,
5, 6, 7,
8,9,11,
9,10,11,
12,13,15,
13,14,15,
16,17,19,
17,18,19,
20,21,23,
21,22,23,
24,25,27,
25,26,27,
28,29,31,
29,30,31,
32,33,35,
33,34,35,
36,37,39,
37,38,39,
};
std::cout << "world title draw type" << std::endl;
glGenVertexArrays(1, VAO);
glGenBuffers(1, VBO);
glGenBuffers(1, EBO); // for index drawing
glGenBuffers(1, &matrixBuffer);
reloadBuffers();
glEnableVertexAttribArray(0);
glEnableVertexAttribArray(1);
glEnableVertexAttribArray(2);
glEnableVertexAttribArray(3);
glEnableVertexAttribArray(4);
glEnableVertexAttribArray(5);
glBindBuffer(GL_ARRAY_BUFFER, VBO[0]);
// position attribute
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
// color attribute
//glVertexAttribPointer(1, 1, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (void*)(3 * sizeof(float)));
// matrix attribute
// set attribute pointers for matrix (4 times vec4)
glBindBuffer(GL_ARRAY_BUFFER, matrixBuffer);
glVertexAttribPointer(2, 4, GL_FLOAT, GL_FALSE, sizeof(glm::mat4), (void*)0);
glVertexAttribPointer(3, 4, GL_FLOAT, GL_FALSE, sizeof(glm::mat4), (void*)(1 * sizeof(glm::vec4)));
glVertexAttribPointer(4, 4, GL_FLOAT, GL_FALSE, sizeof(glm::mat4), (void*)(2 * sizeof(glm::vec4)));
glVertexAttribPointer(5, 4, GL_FLOAT, GL_FALSE, sizeof(glm::mat4), (void*)(3 * sizeof(glm::vec4)));
glVertexAttribDivisor(2, 1);
glVertexAttribDivisor(3, 1);
glVertexAttribDivisor(4, 1);
glVertexAttribDivisor(5, 1);
glBindBuffer(GL_ARRAY_BUFFER, 0); //unbind
ourShader.use();
ourShader.setVec3("colors", 6, colors.data());
}
bool mouseDown = false;
glm::mat4 aProjection = glm::mat4(1.0f);
glm::mat4 aView = glm::mat4(1.0f);
void worldTile::processInput(GLFWwindow* window)
{
if (glfwGetMouseButton(window, GLFW_MOUSE_BUTTON_LEFT) == GLFW_RELEASE && mouseDown == true)
{
std::cout << "Mouse button released" << std::endl;
mouseDown = false;
}
if (glfwGetMouseButton(window, GLFW_MOUSE_BUTTON_LEFT) == GLFW_PRESS && mouseDown == false)
{
double xpos, ypos;
glfwGetCursorPos(window, &xpos, &ypos);
//ypos = 600 - ypos;
mouseDown = true;
unsigned char pixeldata[4];
GLfloat depth;
// reading pixel data at current cursor position ...
glReadBuffer(GL_COLOR_ATTACHMENT1); // read from second framebuffer layer
glReadPixels(xpos, 600 - ypos, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, pixeldata);
glReadPixels(xpos, 600 - ypos, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &depth);
glm::vec4 viewport = glm::vec4(0, 0, 800, 600);
glm::vec3 wincoord = glm::vec3(xpos, 600 - ypos, depth);
glm::mat4 baseModel = glm::mat4(1.0f);
baseModel = glm::rotate(baseModel, glm::radians(-45.0f), glm::vec3(1.0f, 0.0f, 0.0f));
glm::vec3 objcoord = glm::unProject(wincoord, baseModel, aProjection, viewport);
printf("Coordinates in object space: %f, %f, %f\n", objcoord.x, objcoord.y, objcoord.z);
int modelIndex = 0;
// TODO: find out which model from modelMatrices was clicked on
modelMatrices[0] = glm::translate(modelMatrices[0], glm::vec3(0, 0, 0.2f));
glBindBuffer(GL_ARRAY_BUFFER, matrixBuffer);
glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(glm::mat4), &modelMatrices.data()[0]);
//glBufferData(GL_ARRAY_BUFFER, modelMatrices.size() * sizeof(glm::mat4), &modelMatrices.data()[0], drawType);
// convert pixel color back to (int)ID ...
//unsigned int m_trackedID = (pixeldata[0] << 0) | (pixeldata[1] << 8) | (pixeldata[2] << 16) | (pixeldata[3] << 24);
// ------------
//std::cout << "Mouse button pressed: " << xpos << "-" << ypos << " - " << depth << " - " << m_trackedID << std::endl;
}
if (isPressed(window, GLFW_KEY_Z))
{
for (int i = 0; i < 40; i++)
{
int index = i * 4;
vertices[index] -= 0.01f;
glBindBuffer(GL_ARRAY_BUFFER, VBO[0]);
glBufferSubData(GL_ARRAY_BUFFER, index * sizeof(float), sizeof(float), &vertices[index]);
}
}
else if (isPressed(window, GLFW_KEY_X))
{
for (int i = 0; i < 40; i++)
{
int index = i * 4;
vertices[index] += 0.01f;
glBindBuffer(GL_ARRAY_BUFFER, VBO[0]);
glBufferSubData(GL_ARRAY_BUFFER, index * sizeof(float), sizeof(float), &vertices[index]);
}
}
}
void worldTile::draw(glm::mat4& currentModel, glm::mat4& currentProjection, glm::mat4& currentView)
{
ourShader.use();
ourShader.setMat4("projection", currentProjection);
ourShader.setMat4("view", currentView);
aProjection = currentProjection;
aView = currentView;
glBindVertexArray(VAO[0]);
glDrawElementsInstanced(GL_TRIANGLES, indices.size(), GL_UNSIGNED_INT, 0, modelMatrices.size());
}
void worldTile::reloadBuffers()
{
glBindVertexArray(VAO[0]);
// load vertex buffers
glBindBuffer(GL_ARRAY_BUFFER, VBO[0]);
glBufferData(GL_ARRAY_BUFFER, vertices.size() * sizeof(float), vertices.data(), drawType);
// load index buffers
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO[0]);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.size() * sizeof(unsigned int), indices.data(), drawType);
// load matrix buffer
glBindBuffer(GL_ARRAY_BUFFER, matrixBuffer);
glBufferData(GL_ARRAY_BUFFER, modelMatrices.size() * sizeof(glm::mat4), &modelMatrices.data()[0], drawType);
}
Any help is much appreciated.
I've figured it out.
I have to do to opposite of any translations and rotations i've done on my scene and models when calling unproject.
So i have the following as the model being passed into unproject:
glm::mat4 model = glm::mat4(1.0f);
model = glm::translate(model, glm::vec3(0, 0, zoffset));
model = glm::rotate(model, glm::radians(-45.0f), glm::vec3(1.0f, 0.0f, 0.0f));
model = glm::rotate(model, glm::radians(-45.0f), glm::vec3(0.0f, 0.0f, 1.0f));
glm::vec3 objcoord = glm::unProject(wincoord, model, aProjection, viewport);
I've also had to pass the scene projection, and zoffset (for zooming) to my model.
I also have an extra vector which keeps track of all my object's world positions so after i call unproject, I can use the calculated work coordinates to figure out which cube i clicked on. It's a bit of extra work, but it works. I can clean it up to use a clever algorithm or something to figure out which cube was selected.
But this is the basics of what i've done.
unable to insert more than one element in vector; worked fine when I tested with an integer vector. tried the following:
push_back function
insert function
assign function
The issue is in the createObjects() function this error due to the way i have written the opengl code...?
Thank you very much
// Include standard headers
#include <stdio.h>
#include <stdlib.h>
#include <vector>
#include <array>
#include <sstream>
// Include GLEW
#include <GL/glew.h>
// Include GLFW
#include <glfw3.h>
// Include GLM
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/quaternion.hpp>
#include <glm/gtx/quaternion.hpp>
using namespace glm;
// Include AntTweakBar
#include <AntTweakBar.h>
#include <common/shader.hpp>
#include <common/controls.hpp>
#include <common/objloader.hpp>
#include <common/vboindexer.hpp>
typedef struct Vertex {
float XYZW[4];
float RGBA[4];
void SetCoords(float *coords) {
XYZW[0] = coords[0];
XYZW[1] = coords[1];
XYZW[2] = coords[2];
XYZW[3] = coords[3];
}
void SetColor(float *color) {
RGBA[0] = color[0];
RGBA[1] = color[1];
RGBA[2] = color[2];
RGBA[3] = color[3];
}
};
// ATTN: USE POINT STRUCTS FOR EASIER COMPUTATIONS
typedef struct point {
float x, y, z;
point(const float x = 0, const float y = 0, const float z = 0) : x(x), y(y), z(z) {};
point(float *coords) : x(coords[0]), y(coords[1]), z(coords[2]) {};
point operator -(const point& a)const {
return point(x - a.x, y - a.y, z - a.z);
}
point operator +(const point& a)const {
return point(x + a.x, y + a.y, z + a.z);
}
point operator *(const float& a)const {
return point(x*a, y*a, z*a);
}
point operator /(const float& a)const {
return point(x / a, y / a, z / a);
}
float* toArray() {
float array[] = { x, y, z, 1.0f };
return array;
}
};
// function prototypes
int initWindow(void);
void initOpenGL(void);
void createVAOs(Vertex[], unsigned short[], size_t, size_t, int);
void createObjects(void);
void pickVertex(void);
void moveVertex(void);
void drawScene(void);
void cleanup(void);
static void mouseCallback(GLFWwindow*, int, int, int);
static void keyCallback(GLFWwindow*, int, int, int, int);
// GLOBAL VARIABLES
GLFWwindow* window;
const GLuint window_width = 1024, window_height = 768;
glm::mat4 gProjectionMatrix;
glm::mat4 gViewMatrix;
GLuint gPickedIndex;
std::string gMessage;
GLuint programID;
GLuint pickingProgramID;
GLuint kthLevel = 0;
// ATTN: INCREASE THIS NUMBER AS YOU CREATE NEW OBJECTS
const GLuint NumObjects = 3; // number of different "objects" to be drawn
GLuint VertexArrayId[NumObjects] = { 0, 1, 2 };
GLuint VertexBufferId[NumObjects] = { 0, 1, 2 };
GLuint IndexBufferId[NumObjects] = { 0, 1, 2 };
size_t NumVert[NumObjects] = { 0, 1, 2 };
GLuint MatrixID;
GLuint ViewMatrixID;
GLuint ModelMatrixID;
GLuint PickingMatrixID;
GLuint pickingColorArrayID;
GLuint pickingColorID;
GLuint LightID;
// Define objects
Vertex Vertices[] =
{
{ { 1.0f, 1.0f, 0.0f, 1.0f },{ 0.0f, 0.0f, 0.0f, 1.0f } }, // 0
{ { 0.0f, 1.4f, 0.0f, 1.0f },{ 0.0f, 0.0f, 1.0f, 1.0f } }, // 1
{ { -1.0f, 1.0f, 0.0f, 1.0f },{ 0.0f, 1.0f, 0.0f, 1.0f } }, // 2
{ { -1.4f, 0.0f, 0.0f, 1.0f },{ 0.0f, 1.0f, 1.0f, 1.0f } }, // 3
{ { -1.0f, -1.0f, 0.0f, 1.0f },{ 1.0f, 0.0f, 0.0f, 1.0f } }, // 4
{ { 0.0f, -1.4f, 0.0f, 1.0f },{ 1.0f, 0.0f, 1.0f, 1.0f } },// 5
{ { 1.0f, -1.0f, 0.0f, 1.0f },{ 1.0f, 1.0f, 0.0f, 1.0f } }, // 6
{ { 1.4f, 0.0f, 0.0f, 1.0f },{ 1.0f, 1.0f, 1.0f, 1.0f } },// 7
};
Vertex OriginalVertices[] =
{
{ { 1.0f, 1.0f, 0.0f, 1.0f },{ 0.0f, 0.0f, 0.0f, 1.0f } }, // 0
{ { 0.0f, 1.4f, 0.0f, 1.0f },{ 0.0f, 0.0f, 1.0f, 1.0f } }, // 1
{ { -1.0f, 1.0f, 0.0f, 1.0f },{ 0.0f, 1.0f, 0.0f, 1.0f } }, // 2
{ { -1.4f, 0.0f, 0.0f, 1.0f },{ 0.0f, 1.0f, 1.0f, 1.0f } }, // 3
{ { -1.0f, -1.0f, 0.0f, 1.0f },{ 1.0f, 0.0f, 0.0f, 1.0f } }, // 4
{ { 0.0f, -1.4f, 0.0f, 1.0f },{ 1.0f, 0.0f, 1.0f, 1.0f } },// 5
{ { 1.0f, -1.0f, 0.0f, 1.0f },{ 1.0f, 1.0f, 0.0f, 1.0f } }, // 6
{ { 1.4f, 0.0f, 0.0f, 1.0f },{ 1.0f, 1.0f, 1.0f, 1.0f } },// 7
};
Vertex LineVertices[] =
{
{ { 1.0f, 1.0f, 0.0f, 1.0f },{ 1.0f, 1.0f, 1.0f, 1.0f } }, // 0
{ { 0.0f, 1.4f, 0.0f, 1.0f },{ 1.0f, 1.0f, 1.0f, 1.0f } }, // 1
{ { -1.0f, 1.0f, 0.0f, 1.0f },{ 1.0f, 1.0f, 1.0f, 1.0f } }, // 2
{ { -1.4f, 0.0f, 0.0f, 1.0f },{ 1.0f, 1.0f, 1.0f, 1.0f } }, // 3
{ { -1.0f, -1.0f, 0.0f, 1.0f },{ 1.0f, 1.0f, 1.0f, 1.0f } }, // 4
{ { 0.0f, -1.4f, 0.0f, 1.0f },{ 1.0f, 1.0f, 1.0f, 1.0f } },// 5
{ { 1.0f, -1.0f, 0.0f, 1.0f },{ 1.0f, 1.0f, 1.0f, 1.0f } }, // 6
{ { 1.4f, 0.0f, 0.0f, 1.0f },{ 1.0f, 1.0f, 1.0f, 1.0f } },// 7
};
Vertex *kVertices;
Vertex *kPlusOneVertices;
unsigned short *kIndices;
unsigned short *kPlusOneIndices;
//Vertex VTwo[32];
//Vertex VThree[64];
//unsigned short IOne[];
//unsigned short ITwo[];
//unsigned short IThree[];
std::vector<Vertex>TaskTwoVerticesN;
std::vector<unsigned short>TaskTwoIndicesN;
std::vector<Vertex>TaskTwoVerticesNPlusOne;
std::vector<unsigned short>TaskTwoIndicesNPlusOne;
unsigned short Indices[] = {
0, 1, 2, 3, 4, 5, 6, 7
};
unsigned short LineIndices[] = {
0, 1, 2, 3, 4, 5, 6, 7
};
const size_t IndexCount = sizeof(Indices) / sizeof(unsigned short);
// ATTN: DON'T FORGET TO INCREASE THE ARRAY SIZE IN THE PICKING VERTEX SHADER WHEN YOU ADD MORE PICKING COLORS
float pickingColor[IndexCount] = { 0 / 255.0f, 1 / 255.0f, 2 / 255.0f, 3 / 255.0f, 4 / 255.0f, 5 / 255.0f, 6 / 255.0f, 7 / 255.0f };
// ATTN: ADD YOU PER-OBJECT GLOBAL ARRAY DEFINITIONS HERE
**void createObjects(void)
{
// ATTN: DERIVE YOUR NEW OBJECTS HERE:
// each has one vertices {posCurrent;color} and one indices array (no picking needed here)
if (kthLevel > 4 || kthLevel == 0) {
kthLevel = 0;
TaskTwoVerticesN.clear();
for (int i = 0;i < 8;i++) {
TaskTwoVerticesN.push_back(Vertex());
printf("pushed");
TaskTwoVerticesN[i].XYZW[0] = Vertices[i].XYZW[0];
TaskTwoVerticesN[i].XYZW[1] = Vertices[i].XYZW[1];
TaskTwoVerticesN[i].XYZW[2] = Vertices[i].XYZW[2];
TaskTwoVerticesN[i].XYZW[3] = Vertices[i].XYZW[3];
TaskTwoVerticesN[i].RGBA[0] = Vertices[i].RGBA[0];
TaskTwoVerticesN[i].RGBA[1] = Vertices[i].RGBA[1];
TaskTwoVerticesN[i].RGBA[2] = Vertices[i].RGBA[2];
TaskTwoVerticesN[i].RGBA[3] = Vertices[i].RGBA[3];
}
TaskTwoVerticesN.insert(TaskTwoVerticesN.begin(), Vertices, Vertices + 8);
TaskTwoIndicesN.clear();
TaskTwoIndicesN.insert(TaskTwoIndicesN.begin(), Indices, Indices + (sizeof(Indices) / sizeof(Indices[0])));
printf("\n size of vertices %d\n ", sizeof(TaskTwoVerticesN) / sizeof(TaskTwoVerticesN[0]));
//TaskTwoVerticesNPlusOne = TaskTwoVerticesN;
//TaskTwoIndicesNPlusOne = TaskTwoIndicesN;
kVertices = &TaskTwoVerticesN[0];
kIndices = &TaskTwoIndicesN[0];
//kPlusOneVertices = &TaskTwoVerticesNPlusOne[0];
//kPlusOneIndices = &TaskTwoIndicesNPlusOne[0];
}
else {
GLint numberOfPoints = sizeof(TaskTwoVerticesN) / sizeof(TaskTwoVerticesN[0]);
GLint newPointsLength = (8 * 2 ^ kthLevel);
GLint oldPointsLength = newPointsLength / 2;
printf("\n%d\n", newPointsLength);
Vertex newVertexOne, newVertexTwo;
newVertexOne.RGBA[0] = 0.0f;
newVertexOne.RGBA[1] = 1.0f;
newVertexOne.RGBA[2] = 0.0f;
newVertexOne.RGBA[3] = 1.0f;
newVertexOne.XYZW[2] = 0.0f;
newVertexOne.XYZW[3] = 1.0f;
newVertexTwo = newVertexOne;
for (GLint i = 0; i < oldPointsLength; i++)
{
GLint posMinusTwo = abs(oldPointsLength + i - 2) % oldPointsLength;
GLint posMinusOne = abs(oldPointsLength + i - 1) % oldPointsLength;
GLint posCurrent = abs(i) % oldPointsLength;
GLint posPlusOne = abs(oldPointsLength + i + 1) % oldPointsLength;
GLint newPosOne = abs(2 * i) % newPointsLength;
GLint newPosTwo = abs((2 * i) + 1) % newPointsLength;
float xMinusTwo = TaskTwoVerticesN[posMinusTwo].XYZW[0];
float xMinusOne = TaskTwoVerticesN[posMinusOne].XYZW[0];
float xCurrent = TaskTwoVerticesN[posCurrent].XYZW[0];
float xPlusOne = TaskTwoVerticesN[posPlusOne].XYZW[0];
float yMinusTwo = TaskTwoVerticesN[posMinusTwo].XYZW[1];
float yMinusOne = TaskTwoVerticesN[posMinusOne].XYZW[1];
float yCurrent = TaskTwoVerticesN[posCurrent].XYZW[1];
float yPlusOne = TaskTwoVerticesN[posPlusOne].XYZW[1];
newVertexOne.XYZW[0] = (xMinusTwo + (10 * xMinusOne) + (5 * xCurrent)) / 16;
newVertexOne.XYZW[1] = (yMinusTwo + (10 * yMinusOne) + (5 * yCurrent)) / 16;
TaskTwoVerticesNPlusOne.insert(TaskTwoVerticesNPlusOne.begin() + newPosOne, newVertexOne);
TaskTwoIndicesNPlusOne.insert(TaskTwoIndicesNPlusOne.begin() + newPosOne, newPosOne);
printf("\nIn createObjects");
newVertexTwo.XYZW[0] = (xMinusOne + (10 * xCurrent) + (5 * xPlusOne)) / 16;
newVertexTwo.XYZW[1] = (yMinusOne + (10 * yCurrent) + (5 * yPlusOne)) / 16;
TaskTwoVerticesNPlusOne.insert(TaskTwoVerticesNPlusOne.begin() + newPosTwo, newVertexTwo);
TaskTwoIndicesNPlusOne.insert(TaskTwoIndicesNPlusOne.begin() + newPosTwo, newPosTwo);
}
TaskTwoVerticesN.clear();
TaskTwoVerticesN = TaskTwoVerticesNPlusOne;
TaskTwoIndicesN = TaskTwoIndicesNPlusOne;// is this possible?
//TaskTwoVerticesN.assign(TaskTwoIndicesNPlusOne.begin(), TaskTwoIndicesNPlusOne.end());
kVertices = &TaskTwoVerticesN[0];
kIndices = &TaskTwoIndicesN[0];
kPlusOneVertices = &TaskTwoVerticesNPlusOne[0];
kPlusOneIndices = &TaskTwoIndicesNPlusOne[0];
}
printf("\n%d", kthLevel);
kthLevel++;
}**
void drawScene(void)
{
// Dark blue background
glClearColor(0.0f, 0.0f, 0.4f, 0.0f);
// Re-clear the screen for real rendering
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glUseProgram(programID);
{
glm::mat4 ModelMatrix = glm::mat4(1.0); // TranslationMatrix * RotationMatrix;
glm::mat4 MVP = gProjectionMatrix * gViewMatrix * ModelMatrix;
// Send our transformation to the currently bound shader,
// in the "MVP" uniform
glUniformMatrix4fv(MatrixID, 1, GL_FALSE, &MVP[0][0]);
glUniformMatrix4fv(ModelMatrixID, 1, GL_FALSE, &ModelMatrix[0][0]);
glUniformMatrix4fv(ViewMatrixID, 1, GL_FALSE, &gViewMatrix[0][0]);
glm::vec3 lightPos = glm::vec3(4, 4, 4);
glUniform3f(LightID, lightPos.x, lightPos.y, lightPos.z);
glEnable(GL_PROGRAM_POINT_SIZE);
glBindVertexArray(VertexArrayId[0]); // draw Vertices
glBindBuffer(GL_ARRAY_BUFFER, VertexBufferId[0]);
glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(Vertices), Vertices); // update buffer data
//glDrawElements(GL_LINE_LOOP, NumVert[0], GL_UNSIGNED_SHORT, (void*)0);
glDrawElements(GL_POINTS, NumVert[0], GL_UNSIGNED_SHORT, (void*)0);
// ATTN: OTHER BINDING AND DRAWING COMMANDS GO HERE, one set per object:
//glBindVertexArray(VertexArrayId[<x>]); etc etc
glBindVertexArray(0);
glBindVertexArray(VertexArrayId[1]); // draw Vertices
glBindBuffer(GL_ARRAY_BUFFER, VertexBufferId[1]);
glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(LineVertices), LineVertices); // update buffer data
glDrawElements(GL_LINE_STRIP, NumVert[1], GL_UNSIGNED_SHORT, (void*)0);
glBindVertexArray(1);
glBindVertexArray(VertexArrayId[2]); // draw Vertices
glBindBuffer(GL_ARRAY_BUFFER, VertexBufferId[2]);
glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(kPlusOneIndices), kPlusOneVertices); // update buffer data
glDrawElements(GL_LINE_STRIP, NumVert[2], GL_UNSIGNED_SHORT, (void*)0);
glBindVertexArray(2);
}
glUseProgram(0);
// Draw GUI
TwDraw();
// Swap buffers
glfwSwapBuffers(window);
glfwPollEvents();
}
void pickVertex(void)
{
// Clear the screen in white
glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glUseProgram(pickingProgramID);
{
glm::mat4 ModelMatrix = glm::mat4(1.0); // TranslationMatrix * RotationMatrix;
glm::mat4 MVP = gProjectionMatrix * gViewMatrix * ModelMatrix;
// Send our transformation to the currently bound shader, in the "MVP" uniform
glUniformMatrix4fv(PickingMatrixID, 1, GL_FALSE, &MVP[0][0]);
glUniform1fv(pickingColorArrayID, NumVert[0], pickingColor); // here we pass in the picking marker array
// Draw the ponts
glEnable(GL_PROGRAM_POINT_SIZE);
glBindVertexArray(VertexArrayId[0]);
glBindBuffer(GL_ARRAY_BUFFER, VertexBufferId[0]);
glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(Vertices), Vertices); // update buffer data
glDrawElements(GL_POINTS, NumVert[0], GL_UNSIGNED_SHORT, (void*)0);
glBindVertexArray(0);
}
glUseProgram(0);
// Wait until all the pending drawing commands are really done.
// Ultra-mega-over slow !
// There are usually a long time between glDrawElements() and
// all the fragments completely rasterized.
glFlush();
glFinish();
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
// Read the pixel at the center of the screen.
// You can also use glfwGetMousePos().
// Ultra-mega-over slow too, even for 1 pixel,
// because the framebuffer is on the GPU.
double xpos, ypos;
glfwGetCursorPos(window, &xpos, &ypos);
unsigned char data[4];
glReadPixels(xpos, window_height - ypos, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, data); // OpenGL renders with (0,0) on bottom, mouse reports with (0,0) on top
// Convert the color back to an integer ID
gPickedIndex = int(data[0]);
// Uncomment these lines to see the picking shader in effect
//glfwSwapBuffers(window);
//continue; // skips the normal rendering
}
// fill this function in!
void moveVertex(void)
{
double xpos, ypos;
glfwGetCursorPos(window, &xpos, &ypos);
unsigned char data[4];
glReadPixels(xpos, 768 - ypos, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, data); // OpenGL renders with (0,0) on bottom, mouse reports with (0,0) on top
glm::mat4 ModelMatrix = glm::mat4(1.0);
GLint viewport[4];
glGetIntegerv(GL_VIEWPORT, viewport);
glm::vec4 vp = glm::vec4(viewport[0], viewport[1], viewport[2], viewport[3]);
// retrieve your cursor position
// get your world coordinates
// move points
if (gPickedIndex == 255) { // Full white, must be the background !
gMessage = "background";
}
else {
std::ostringstream oss;
oss << "point " << gPickedIndex;
gMessage = oss.str();
}
if ((glfwGetMouseButton(window, GLFW_MOUSE_BUTTON_LEFT)) == GLFW_PRESS) {
printf("\n pressed");
Vertices[gPickedIndex].RGBA[0] = 0.5f;
Vertices[gPickedIndex].RGBA[1] = 0.5f;
Vertices[gPickedIndex].RGBA[2] = 0.5f;
Vertices[gPickedIndex].RGBA[3] = 1.0f;
GLint viewport[4];
glGetIntegerv(GL_VIEWPORT, viewport);
glm::vec3 vertex = glm::unProject(glm::vec3(xpos, 768 - ypos, 0.0), ModelMatrix, gProjectionMatrix, vp);
Vertices[gPickedIndex].XYZW[0] = -vertex[0];
Vertices[gPickedIndex].XYZW[1] = vertex[1];
LineVertices[gPickedIndex].XYZW[0] = -vertex[0];
LineVertices[gPickedIndex].XYZW[1] = vertex[1];
}
else {
printf("released");
Vertices[gPickedIndex].RGBA[0] = OriginalVertices[gPickedIndex].RGBA[0];
Vertices[gPickedIndex].RGBA[1] = OriginalVertices[gPickedIndex].RGBA[1];
Vertices[gPickedIndex].RGBA[2] = OriginalVertices[gPickedIndex].RGBA[2];
Vertices[gPickedIndex].RGBA[3] = OriginalVertices[gPickedIndex].RGBA[3];
}
}
int initWindow(void)
{
// Initialise GLFW
if (!glfwInit()) {
fprintf(stderr, "Failed to initialize GLFW\n");
return -1;
}
glfwWindowHint(GLFW_SAMPLES, 4);
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
// Open a window and create its OpenGL context
window = glfwCreateWindow(window_width, window_height, "Lastname,FirstName(ufid)", NULL, NULL);
if (window == NULL) {
fprintf(stderr, "Failed to open GLFW window. If you have an Intel GPU, they are not 3.3 compatible. Try the 2.1 version of the tutorials.\n");
glfwTerminate();
return -1;
}
glfwMakeContextCurrent(window);
// Initialize GLEW
glewExperimental = true; // Needed for core profile
if (glewInit() != GLEW_OK) {
fprintf(stderr, "Failed to initialize GLEW\n");
return -1;
}
// Initialize the GUI
TwInit(TW_OPENGL_CORE, NULL);
TwWindowSize(window_width, window_height);
TwBar * GUI = TwNewBar("Picking");
TwSetParam(GUI, NULL, "refresh", TW_PARAM_CSTRING, 1, "0.1");
TwAddVarRW(GUI, "Last picked object", TW_TYPE_STDSTRING, &gMessage, NULL);
// Set up inputs
glfwSetInputMode(window, GLFW_STICKY_KEYS, GL_FALSE);
glfwSetCursorPos(window, window_width / 2, window_height / 2);
glfwSetMouseButtonCallback(window, mouseCallback);
glfwSetKeyCallback(window, keyCallback);
return 0;
}
void initOpenGL(void)
{
// Dark blue background
glClearColor(0.0f, 0.0f, 0.4f, 0.0f);
// Enable depth test
glEnable(GL_DEPTH_TEST);
// Accept fragment if it closer to the camera than the former one
glDepthFunc(GL_LESS);
// Cull triangles which normal is not towards the camera
glEnable(GL_CULL_FACE);
// Projection matrix : 45° Field of View, 4:3 ratio, display range : 0.1 unit <-> 100 units
//glm::mat4 ProjectionMatrix = glm::perspective(45.0f, 4.0f / 3.0f, 0.1f, 100.0f);
// Or, for an ortho camera :
gProjectionMatrix = glm::ortho(-4.0f, 4.0f, -3.0f, 3.0f, 0.0f, 100.0f); // In world coordinates
// Camera matrix
gViewMatrix = glm::lookAt(
glm::vec3(0, 0, -5), // Camera is at (4,3,3), in World Space
glm::vec3(0, 0, 0), // and looks at the origin
glm::vec3(0, 1, 0) // Head is up (set to 0,-1,0 to look upside-down)
);
// Create and compile our GLSL program from the shaders
programID = LoadShaders("StandardShading.vertexshader", "StandardShading.fragmentshader");
pickingProgramID = LoadShaders("Picking.vertexshader", "Picking.fragmentshader");
// Get a handle for our "MVP" uniform
MatrixID = glGetUniformLocation(programID, "MVP");
ViewMatrixID = glGetUniformLocation(programID, "V");
ModelMatrixID = glGetUniformLocation(programID, "M");
PickingMatrixID = glGetUniformLocation(pickingProgramID, "MVP");
// Get a handle for our "pickingColorID" uniform
pickingColorArrayID = glGetUniformLocation(pickingProgramID, "PickingColorArray");
pickingColorID = glGetUniformLocation(pickingProgramID, "PickingColor");
// Get a handle for our "LightPosition" uniform
LightID = glGetUniformLocation(programID, "LightPosition_worldspace");
createVAOs(Vertices, Indices, sizeof(Vertices), sizeof(Indices), 0);
createVAOs(LineVertices, LineIndices, sizeof(LineVertices), sizeof(LineIndices), 1);
createVAOs(kPlusOneVertices, kPlusOneIndices, sizeof(kPlusOneVertices), sizeof(kPlusOneIndices), 2);
printf("\nVAO");
createObjects();
// ATTN: create VAOs for each of the newly created objects here:
// createVAOs(<fill this appropriately>);
}
void createVAOs(Vertex Vertices[], unsigned short Indices[], size_t BufferSize, size_t IdxBufferSize, int ObjectId) {
NumVert[ObjectId] = IdxBufferSize / (sizeof GLubyte);
GLenum ErrorCheckValue = glGetError();
size_t VertexSize = sizeof(Vertices[0]);
size_t RgbOffset = sizeof(Vertices[0].XYZW);
// Create Vertex Array Object
glGenVertexArrays(1, &VertexArrayId[ObjectId]);
glBindVertexArray(VertexArrayId[ObjectId]);
// Create Buffer for vertex data
glGenBuffers(1, &VertexBufferId[ObjectId]);
glBindBuffer(GL_ARRAY_BUFFER, VertexBufferId[ObjectId]);
glBufferData(GL_ARRAY_BUFFER, BufferSize, Vertices, GL_STATIC_DRAW);
// Create Buffer for indices
glGenBuffers(1, &IndexBufferId[ObjectId]);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, IndexBufferId[ObjectId]);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, IdxBufferSize, Indices, GL_STATIC_DRAW);
// Assign vertex attributes
glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, VertexSize, 0);
glVertexAttribPointer(1, 4, GL_FLOAT, GL_FALSE, VertexSize, (GLvoid*)RgbOffset);
glEnableVertexAttribArray(0); // position
glEnableVertexAttribArray(1); // color
// Disable our Vertex Buffer Object
glBindVertexArray(0);
ErrorCheckValue = glGetError();
if (ErrorCheckValue != GL_NO_ERROR)
{
fprintf(
stderr,
"ERROR: Could not create a VBO: %s \n",
gluErrorString(ErrorCheckValue)
);
}
}
void cleanup(void)
{
// Cleanup VBO and shader
for (int i = 0; i < NumObjects; i++) {
glDeleteBuffers(1, &VertexBufferId[i]);
glDeleteBuffers(1, &IndexBufferId[i]);
glDeleteVertexArrays(1, &VertexArrayId[i]);
}
glDeleteProgram(programID);
glDeleteProgram(pickingProgramID);
// Close OpenGL window and terminate GLFW
glfwTerminate();
}
static void mouseCallback(GLFWwindow* window, int button, int action, int mods)
{
if (button == GLFW_MOUSE_BUTTON_LEFT && action == GLFW_PRESS) {
pickVertex();
}
}
static void keyCallback(GLFWwindow* window, int button, int scancode, int action, int mods) {
if (button == GLFW_KEY_1 && action == GLFW_PRESS) {
createObjects();
//printf("\n1 pressed");
}
}
int main(void)
{
// initialize window
int errorCode = initWindow();
if (errorCode != 0)
return errorCode;
// initialize OpenGL pipeline
initOpenGL();
// For speed computation
double lastTime = glfwGetTime();
int nbFrames = 0;
do {
// Measure speed
double currentTime = glfwGetTime();
nbFrames++;
if (currentTime - lastTime >= 1.0) { // If last prinf() was more than 1sec ago
// printf and reset
printf("%f ms/frame\n", 1000.0 / double(nbFrames));
nbFrames = 0;
lastTime += 1.0;
}
glfwSetInputMode(window, GLFW_STICKY_MOUSE_BUTTONS, 1);
// DRAGGING: move current (picked) vertex with cursor
if (glfwGetMouseButton(window, GLFW_MOUSE_BUTTON_LEFT))
moveVertex();
// DRAWING SCENE
glfwSetKeyCallback(window, keyCallback);
//createObjects(); // re-evaluate curves in case vertices have been moved
drawScene();
} // Check if the ESC key was pressed or the window was closed
while (glfwGetKey(window, GLFW_KEY_ESCAPE) != GLFW_PRESS &&
glfwWindowShouldClose(window) == 0);
cleanup();
return 0;
}
have you tried using arrays like this
bool MAP::InsertVertex(long obj, GLdouble x, GLdouble y, GLdouble z,GLfloat r
,GLfloat g, GLfloat b, GLfloat a,GLfloat nx, GLfloat ny
,GLfloat nz, GLfloat fogdepth)
{
MAP_VERTEX new_vertex;
long rgb = GenerateVertexColor(obj);
if(obj>header.max_objects||obj<0)
return (false);
new_vertex.xyz[0] =x;
new_vertex.xyz[1] =y;
new_vertex.xyz[2] =z;
new_vertex.rgba[0] =r;
new_vertex.rgba[1] =g;
new_vertex.rgba[2] =b;
new_vertex.rgba[3] =a;
new_vertex.normal[0] =nx;
new_vertex.normal[1] =ny;
new_vertex.normal[2] =nz;
new_vertex.fogdepth =fogdepth;
new_vertex.select_rgb[0] = GetRValue(rgb);
new_vertex.select_rgb[1] = GetGValue(rgb);
new_vertex.select_rgb[2] = GetBValue(rgb);
if(object[obj].max_vertices==0) object[obj].vertex = new MAP_VERTEX
[object[obj].max_vertices+1];
else{
//Backing up the vertices that are already there
MAP_VERTEX *temp = new MAP_VERTEX[object[obj].max_vertices+1];
for(long i=0;i<object[obj].max_vertices;i++)
temp[i] = object[obj].vertex[i];
//Deleting the old vertices that were allocated earlier
delete [] object[obj].vertex;
//Now allocating new memory for vertex with an extra buffer
object[obj].vertex = new MAP_VERTEX[object[obj].max_vertices+2];
for(long i=0;i<object[obj].max_vertices;i++)
object[obj].vertex[i] =temp[i];
delete [] temp;
temp = NULL;
}
//Insert a new Vertex
object[obj].vertex[object[obj].max_vertices] = new_vertex;
object[obj].max_vertices++;
return (true);
}
My ultimate goal is to render 1 million spheres of different sizes and colors at 60 fps. I want to be able to move the camera around the screen as well.
I have modified the code on this page of the tutorial I am studying to try to instance many spheres. However, I find that at as little as 64 spheres my fps falls below 60, and at 900 spheres my fps is a measly 4. My understanding of instancing is naive, but I believe that I should be getting more frames-per-second than this. 60 fps should be attainable with only 64 spheres. I believe that I am, in some way, causing the CPU and GPU to communicate more often than they should have to. So my question is: How do I instance so many objects (ideally millions) without causing the fps to fall low (ideally 60 fps)?
I am calculating fps by calculating (10 / time_elapsed) every 10 frames, where time_elapsed is the time that has elapsed since the last fps call. I am printing this out using printf on line 118 of my code.
I have been learning OpenGL through this tutorial and so I use 32-bit GLEW and 32-bit GLFW in Visual Studio 2013. I have 8 GB of RAM on a 64-bit operating system (Windows 7) with a 2.30 GHz CPU.
I have tried coding my own example based on the tutorial above. Source code:
(set line #2 to be the number of spheres to be instanced. Make sure line#2 has a whole-number square root. Set line 4 to be the detail of the sphere, the lowest it can go is 0. Higher number = more detailed.)
// Make sure NUM_INS is a square number
#define NUM_INS 1
// Detail up to 4 is probably good enough
#define SPHERE_DETAIL 4
#include <vector>
// GLEW
#define GLEW_STATIC
#include <GL/glew.h>
// GLFW
#include <GLFW/glfw3.h>
// GL includes
#include "Shader.h"
// GLM Mathemtics
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
// Properties
GLuint screenWidth = 800, screenHeight = 600;
// Function prototypes
void key_callback(GLFWwindow* window, int key, int scancode, int action, int mode);
std::vector<GLfloat> create_sphere(int recursion);
// The MAIN function, from here we start our application and run the Game loop
int main()
{
// Init GLFW
glfwInit();
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
glfwWindowHint(GLFW_RESIZABLE, GL_FALSE);
GLFWwindow* window = glfwCreateWindow(screenWidth, screenHeight, "LearnOpenGL", nullptr, nullptr); // Windowed
glfwMakeContextCurrent(window);
// Set the required callback functions
glfwSetKeyCallback(window, key_callback);
// Initialize GLEW to setup the OpenGL Function pointers
glewExperimental = GL_TRUE;
glewInit();
// Define the viewport dimensions
glViewport(0, 0, screenWidth, screenHeight);
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); // Comment to remove wireframe mode
// Setup OpenGL options
glEnable(GL_DEPTH_TEST);
// Setup and compile our shader(s)
Shader shader("core.vs", "core.frag");
// Generate a list of 100 quad locations/translation-vectors
std::vector<glm::vec2> translations(NUM_INS);
//glm::vec2 translations[NUM_INS];
int index = 0;
GLfloat offset = 1.0f / (float)sqrt(NUM_INS);
for (GLint y = -(float)sqrt(NUM_INS); y < (float)sqrt(NUM_INS); y += 2)
{
for (GLint x = -(float)sqrt(NUM_INS); x < (float)sqrt(NUM_INS); x += 2)
{
glm::vec2 translation;
translation.x = (GLfloat)x / (float)sqrt(NUM_INS) + offset;
translation.y = (GLfloat)y / (float)sqrt(NUM_INS) + offset;
translations[index++] = translation;
}
}
// Store instance data in an array buffer
GLuint instanceVBO;
glGenBuffers(1, &instanceVBO);
glBindBuffer(GL_ARRAY_BUFFER, instanceVBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(glm::vec2) * NUM_INS, &translations[0], GL_STATIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, 0);
// create 12 vertices of a icosahedron
std::vector<GLfloat> vv = create_sphere(SPHERE_DETAIL);
GLuint quadVAO, quadVBO;
glGenVertexArrays(1, &quadVAO);
glGenBuffers(1, &quadVBO);
glBindVertexArray(quadVAO);
glBindBuffer(GL_ARRAY_BUFFER, quadVBO);
glBufferData(GL_ARRAY_BUFFER, vv.size() * sizeof(GLfloat), &vv[0], GL_STATIC_DRAW);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(GLfloat), (GLvoid*)0);
glEnableVertexAttribArray(1);
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(GLfloat), (GLvoid*)(2 * sizeof(GLfloat)));
// Also set instance data
glEnableVertexAttribArray(2);
glBindBuffer(GL_ARRAY_BUFFER, instanceVBO);
glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(GLfloat), (GLvoid*)0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glVertexAttribDivisor(2, 1); // Tell OpenGL this is an instanced vertex attribute.
glBindVertexArray(0);
// For printing frames-per-second
float counter = 0;
double get_time = 0;
double new_time;
// Game loop
while (!glfwWindowShouldClose(window))
{
// Print fps by printing (number_of_frames / time_elapsed)
counter += 1;
if (counter > 10) {
counter -= 10;
new_time = glfwGetTime();
printf("fps: %.2f ", (10/(new_time - get_time)));
get_time = new_time;
}
// Check and call events
glfwPollEvents();
// Clear buffers
//glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// Draw 100 instanced quads
shader.Use();
glm::mat4 model;
model = glm::rotate(model, 0.0f, glm::vec3(1.0f, 0.0f, 0.0f));
// Camera/View transformation
glm::mat4 view;
GLfloat radius = 10.0f;
GLfloat camX = sin(glfwGetTime()) * radius;
GLfloat camZ = cos(glfwGetTime()) * radius;
view = glm::lookAt(glm::vec3(camX, 0.0f, camZ), glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f, 1.0f, 0.0f));
// Projection
glm::mat4 projection;
projection = glm::perspective(45.0f, (GLfloat)screenWidth / (GLfloat)screenHeight, 0.1f, 100.0f);
// Get the uniform locations
GLint modelLoc = glGetUniformLocation(shader.Program, "model");
GLint viewLoc = glGetUniformLocation(shader.Program, "view");
GLint projLoc = glGetUniformLocation(shader.Program, "projection");
// Pass the matrices to the shader
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));
glBindVertexArray(quadVAO);
glDrawArraysInstanced(GL_TRIANGLES, 0, vv.size() / 3, NUM_INS); // 100 triangles of 6 vertices each
glBindVertexArray(0);
// Swap the buffers
glfwSwapBuffers(window);
}
glfwTerminate();
return 0;
}
// Is called whenever a key is pressed/released via GLFW
void key_callback(GLFWwindow* window, int key, int scancode, int action, int mode)
{
if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS)
glfwSetWindowShouldClose(window, GL_TRUE);
}
std::vector<GLfloat> add_color(std::vector<GLfloat> sphere) {
// Add color
std::vector<GLfloat> colored_sphere;
for (GLint i = 0; i < sphere.size(); i+=9) {
colored_sphere.push_back(sphere[i]);
colored_sphere.push_back(sphere[i+1]);
colored_sphere.push_back(sphere[i+2]);
colored_sphere.push_back(0.0f);
colored_sphere.push_back(0.0f);
colored_sphere.push_back(0.0f);
colored_sphere.push_back(sphere[i+3]);
colored_sphere.push_back(sphere[i+4]);
colored_sphere.push_back(sphere[i+5]);
colored_sphere.push_back(0.0f);
colored_sphere.push_back(0.0f);
colored_sphere.push_back(0.0f);
colored_sphere.push_back(sphere[i+6]);
colored_sphere.push_back(sphere[i+7]);
colored_sphere.push_back(sphere[i+8]);
colored_sphere.push_back(0.0f);
colored_sphere.push_back(0.0f);
colored_sphere.push_back(0.0f);
}
return colored_sphere;
}
std::vector<GLfloat> tesselate(std::vector<GLfloat> shape, int recursion) {
if (recursion > 0) {
std::vector<GLfloat> new_sphere = {};
for (GLint i = 0; i < shape.size(); i += 9) {
// 1.902113 approximately
GLfloat radius = sqrt(1.0f + pow((1.0f + sqrt(5.0f)) / 2.0f, 2));
// Every 9 points is a triangle. Take 1 triangle and turn it into 4 triangles.
GLfloat p_one[] = {shape[i], shape[i + 1], shape[i + 2]};
GLfloat p_two[] = {shape[i + 3], shape[i + 4], shape[i + 5]};
GLfloat p_thr[] = {shape[i + 6], shape[i + 7], shape[i + 8]};
GLfloat p_one_two[] = { (p_one[0] + p_two[0]) / 2.0f, (p_one[1] + p_two[1]) / 2.0f, (p_one[2] + p_two[2]) / 2.0f };
GLfloat p_one_thr[] = { (p_one[0] + p_thr[0]) / 2.0f, (p_one[1] + p_thr[1]) / 2.0f, (p_one[2] + p_thr[2]) / 2.0f };
GLfloat p_two_thr[] = { (p_two[0] + p_thr[0]) / 2.0f, (p_two[1] + p_thr[1]) / 2.0f, (p_two[2] + p_thr[2]) / 2.0f };
GLfloat r_one_two = sqrt((p_one_two[0]*p_one_two[0]) + (p_one_two[1]*p_one_two[1]) + (p_one_two[2]*p_one_two[2]));
GLfloat r_one_thr = sqrt((p_one_thr[0]*p_one_thr[0]) + (p_one_thr[1]*p_one_thr[1]) + (p_one_thr[2]*p_one_thr[2]));
GLfloat r_two_thr = sqrt((p_two_thr[0]*p_two_thr[0]) + (p_two_thr[1]*p_two_thr[1]) + (p_two_thr[2]*p_two_thr[2]));
GLfloat t_one_two[] = { radius * p_one_two[0] / r_one_two, radius * p_one_two[1] / r_one_two, radius * p_one_two[2] / r_one_two };
GLfloat t_one_thr[] = { radius * p_one_thr[0] / r_one_thr, radius * p_one_thr[1] / r_one_thr, radius * p_one_thr[2] / r_one_thr };
GLfloat t_two_thr[] = { radius * p_two_thr[0] / r_two_thr, radius * p_two_thr[1] / r_two_thr, radius * p_two_thr[2] / r_two_thr };
// Triangle 1:
new_sphere.push_back(p_one[0]);
new_sphere.push_back(p_one[1]);
new_sphere.push_back(p_one[2]);
new_sphere.push_back(t_one_two[0]);
new_sphere.push_back(t_one_two[1]);
new_sphere.push_back(t_one_two[2]);
new_sphere.push_back(t_one_thr[0]);
new_sphere.push_back(t_one_thr[1]);
new_sphere.push_back(t_one_thr[2]);
// Triangle 2:
new_sphere.push_back(p_two[0]);
new_sphere.push_back(p_two[1]);
new_sphere.push_back(p_two[2]);
new_sphere.push_back(t_one_two[0]);
new_sphere.push_back(t_one_two[1]);
new_sphere.push_back(t_one_two[2]);
new_sphere.push_back(t_two_thr[0]);
new_sphere.push_back(t_two_thr[1]);
new_sphere.push_back(t_two_thr[2]);
// Triangle 3:
new_sphere.push_back(p_thr[0]);
new_sphere.push_back(p_thr[1]);
new_sphere.push_back(p_thr[2]);
new_sphere.push_back(t_one_thr[0]);
new_sphere.push_back(t_one_thr[1]);
new_sphere.push_back(t_one_thr[2]);
new_sphere.push_back(t_two_thr[0]);
new_sphere.push_back(t_two_thr[1]);
new_sphere.push_back(t_two_thr[2]);
// Center Triangle:
new_sphere.push_back(t_one_two[0]);
new_sphere.push_back(t_one_two[1]);
new_sphere.push_back(t_one_two[2]);
new_sphere.push_back(t_one_thr[0]);
new_sphere.push_back(t_one_thr[1]);
new_sphere.push_back(t_one_thr[2]);
new_sphere.push_back(t_two_thr[0]);
new_sphere.push_back(t_two_thr[1]);
new_sphere.push_back(t_two_thr[2]);
}
return tesselate(new_sphere, recursion - 1);
}
printf("number of vertices to be rendered: %d || ", shape.size());
return shape;
}
std::vector<GLfloat> create_sphere(int recursion) {
// Define the starting icosahedron
GLfloat t_ = (1.0f + sqrt(5.0f)) / 2.0f;
std::vector<GLfloat> icosahedron = {
-1.0f, t_, 0.0f, -t_, 0.0f, 1.0f, 0.0f, 1.0f, t_,
-1.0f, t_, 0.0f, 0.0f, 1.0f, t_, 1.0f, t_, 0.0f,
-1.0f, t_, 0.0f, 1.0f, t_, 0.0f, 0.0f, 1.0f, -t_,
-1.0f, t_, 0.0f, 0.0f, 1.0f, -t_, -t_, 0.0f, -1.0f,
-1.0f, t_, 0.0f, -t_, 0.0f, -1.0f, -t_, 0.0f, 1.0f,
1.0f, t_, 0.0f, 0.0f, 1.0f, t_, t_, 0.0f, 1.0f,
0.0f, 1.0f, t_, -t_, 0.0f, 1.0f, 0.0f, -1.0f, t_,
-t_, 0.0f, 1.0f, -t_, 0.0f, -1.0f, -1.0f, -t_, 0.0f,
-t_, 0.0f, -1.0f, 0.0f, 1.0f, -t_, 0.0f, -1.0f, -t_,
0.0f, 1.0f, -t_, 1.0f, t_, 0.0f, t_, 0.0f, -1.0f,
1.0f, -t_, 0.0f, t_, 0.0f, 1.0f, 0.0f, -1.0f, t_,
1.0f, -t_, 0.0f, 0.0f, -1.0f, t_,-1.0f, -t_, 0.0f,
1.0f, -t_, 0.0f,-1.0f, -t_, 0.0f, 0.0f, -1.0f, -t_,
1.0f, -t_, 0.0f, 0.0f, -1.0f, -t_, t_, 0.0f, -1.0f,
1.0f, -t_, 0.0f, t_, 0.0f, -1.0f, t_, 0.0f, 1.0f,
0.0f, -1.0f, t_, t_, 0.0f, 1.0f, 0.0f, 1.0f, t_,
-1.0f, -t_, 0.0f, 0.0f, -1.0f, t_,-t_, 0.0f, 1.0f,
0.0f, -1.0f, -t_,-1.0f, -t_, 0.0f,-t_, 0.0f, -1.0f,
t_, 0.0f, -1.0f, 0.0f, -1.0f, -t_, 0.0f, 1.0f, -t_,
t_, 0.0f, 1.0f, t_, 0.0f, -1.0f, 1.0f, t_, 0.0f,
};
// Tesselate the icososphere the number of times recursion
std::vector<GLfloat> colorless_sphere = tesselate(icosahedron, recursion);
// Add color and return
return add_color(colorless_sphere);
}
Vertex Shader: (named core.vs)
#version 330 core
layout (location = 0) in vec3 position;
layout (location = 1) in vec3 color;
layout (location = 2) in vec2 offset;
out vec3 fColor;
uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;
void main()
{
gl_Position = projection * view * model * vec4(position.x + offset.x, position.y + offset.y, position.z, 1.0f);
fColor = color;
}
Fragment Shader: (named core.frag)
#version 330 core
in vec3 fColor;
out vec4 color;
void main()
{
color = vec4(fColor, 1.0f);
}
Shader class: (named Shader.h)
#ifndef SHADER_H
#define SHADER_H
#include <string>
#include <fstream>
#include <sstream>
#include <iostream>
#include <GL/glew.h>
class Shader
{
public:
GLuint Program;
// Constructor generates the shader on the fly
Shader(const GLchar* vertexPath, const GLchar* fragmentPath)
{
// 1. Retrieve the vertex/fragment source code from filePath
std::string vertexCode;
std::string fragmentCode;
std::ifstream vShaderFile;
std::ifstream fShaderFile;
// ensures ifstream objects can throw exceptions:
vShaderFile.exceptions(std::ifstream::badbit);
fShaderFile.exceptions(std::ifstream::badbit);
try
{
// Open files
vShaderFile.open(vertexPath);
fShaderFile.open(fragmentPath);
std::stringstream vShaderStream, fShaderStream;
// Read file's buffer contents into streams
vShaderStream << vShaderFile.rdbuf();
fShaderStream << fShaderFile.rdbuf();
// close file handlers
vShaderFile.close();
fShaderFile.close();
// Convert stream into string
vertexCode = vShaderStream.str();
fragmentCode = fShaderStream.str();
}
catch (std::ifstream::failure e)
{
std::cout << "ERROR::SHADER::FILE_NOT_SUCCESFULLY_READ" << std::endl;
}
const GLchar* vShaderCode = vertexCode.c_str();
const GLchar * fShaderCode = fragmentCode.c_str();
// 2. Compile shaders
GLuint vertex, fragment;
GLint success;
GLchar infoLog[512];
// Vertex Shader
vertex = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertex, 1, &vShaderCode, NULL);
glCompileShader(vertex);
// Print compile errors if any
glGetShaderiv(vertex, GL_COMPILE_STATUS, &success);
if (!success)
{
glGetShaderInfoLog(vertex, 512, NULL, infoLog);
std::cout << "ERROR::SHADER::VERTEX::COMPILATION_FAILED\n" << infoLog << std::endl;
}
// Fragment Shader
fragment = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragment, 1, &fShaderCode, NULL);
glCompileShader(fragment);
// Print compile errors if any
glGetShaderiv(fragment, GL_COMPILE_STATUS, &success);
if (!success)
{
glGetShaderInfoLog(fragment, 512, NULL, infoLog);
std::cout << "ERROR::SHADER::FRAGMENT::COMPILATION_FAILED\n" << infoLog << std::endl;
}
// Shader Program
this->Program = glCreateProgram();
glAttachShader(this->Program, vertex);
glAttachShader(this->Program, fragment);
glLinkProgram(this->Program);
// Print linking errors if any
glGetProgramiv(this->Program, GL_LINK_STATUS, &success);
if (!success)
{
glGetProgramInfoLog(this->Program, 512, NULL, infoLog);
std::cout << "ERROR::SHADER::PROGRAM::LINKING_FAILED\n" << infoLog << std::endl;
}
// Delete the shaders as they're linked into our program now and no longer necessery
glDeleteShader(vertex);
glDeleteShader(fragment);
}
// Uses the current shader
void Use()
{
glUseProgram(this->Program);
}
};
#endif
My ultimate goal is to render 1 million spheres of different sizes and colors at 60 fps.
This is an unreasonable expectation.
Let's say that each sphere consists of 50 triangles. Kinda small for a good sphere shape, but lets assume they're that small.
1 million spheres at 50 tris per sphere is 50 million triangles per frame. At 60 FPS, that's 3 billion triangles per second.
No commercially available GPU is good enough to do that. And that's just a 50 triangle sphere; your 4x tessellated icosahedron will be over 5,000 triangles.
Now yes, drawing 60 such spheres is only ~300,000 triangles per frame. But even that at 60 FPS is ~18 million triangles per second. Hardware does exist that can handle that many triangles, but it's very clearly a lot. And you're definitely not going to get 1 million of them.
This is not a matter of GPU/CPU communication or overhead. You're simply throwing more work at your GPU than it could handle. You might be able to improve a couple of things here and there, but nothing that's going to get you even one tenth of what you want.
At least, not with this overall approach.
For your particular case of wanting to draw millions of spheres, I would use raytraced impostors rather than actual geometry of spheres. That is, you draw quads, who's positions are generated by the vertex (or geometry) shader. You generate a quad per sphere, such that the quad circumscribes the sphere. Then the fragment shader does a simple ray-sphere intersection test to see if the fragment in question (from the direction of the camera view) hits the sphere or not. If the ray doesn't hit the sphere, you discard the fragment.
You would also need to modify gl_FragDepth to give the impostor the proper depth value, so that intersecting spheres can work.
I'm trying to run my first OpenGL program. In the main() function I have infinity loop:
do {
glClear(GL_COLOR_BUFFER_BIT);
glUseProgram(programID);
_collection[0].draw();
_collection[1].draw();
glfwSwapBuffers(window);
glfwPollEvents();
} while(glfwGetKey(window, GLFW_KEY_ESCAPE) != GLFW_PRESS && glfwWindowShouldClose(window) == 0)
The function _collection[].draw() should draw rectangles:
static const GLfloat g_vertex_buffer_data[] = {
x, y, 0.0f, // lewy górny
x, y - 0.4f, 0.0f, // lewy dolny
x + 0.4f, y - 0.4f, 0.0f, // prawy dolny
x + 0.4f, y, 0.0f, // lewy górny
x + 0.02f, y - 0.02f, 0.0f, // lewy górny
x + 0.02f, y - 0.4f + 0.02f, 0.0f, // lewy dolny
x + 0.4f - 0.02f, y - 0.4f + 0.02f, 0.0f, // prawy dolny
x + 0.4f - 0.02f, y - 0.02f, 0.0f, // lewy górny
};
static const GLfloat g_color_buffer_data[] = {
1.0f, 1.0f, 1.0f, // lewy górny
1.0f, 1.0f, 1.0f, // lewy dolny
1.0f, 1.0f, 1.0f, // prawy dolny
1.0f, 1.0f, 1.0f, // lewy górny
0.0f, 0.0f, 1.0f,
0.0f, 0.0f, 1.0f,
0.0f, 0.0f, 1.0f,
0.0f, 0.0f, 1.0f,
};
GLuint vertexbuffer;
glGenBuffers(1, &vertexbuffer);
glBindBuffer(GL_ARRAY_BUFFER, vertexbuffer);
glBufferData(GL_ARRAY_BUFFER, sizeof(g_vertex_buffer_data), g_vertex_buffer_data, GL_STATIC_DRAW);
GLuint colorbuffer;
glGenBuffers(1, &colorbuffer);
glBindBuffer(GL_ARRAY_BUFFER, colorbuffer);
glBufferData(GL_ARRAY_BUFFER, sizeof(g_color_buffer_data), g_color_buffer_data, GL_STATIC_DRAW);
glEnableVertexAttribArray(vertexPosition_modelspaceID);
glBindBuffer(GL_ARRAY_BUFFER, vertexbuffer);
glVertexAttribPointer(
vertexPosition_modelspaceID, // The attribute we want to configure
3, // size
GL_FLOAT, // type
GL_FALSE, // normalized?
0, // stride
(void*)0 // array buffer offset
);
// przekazuję kolory wierzchołków
glEnableVertexAttribArray(vertexColorID);
glBindBuffer(GL_ARRAY_BUFFER, colorbuffer);
glVertexAttribPointer(
vertexColorID, // The attribute we want to configure
3, // size
GL_FLOAT, // type
GL_FALSE, // normalized?
0, // stride
(void*)0 // array buffer offset
);
// rysuję wszystko
glDrawArrays(GL_QUADS, 0, 8);
glDisableVertexAttribArray(vertexPosition_modelspaceID);
glDisableVertexAttribArray(vertexColorID);
My problem is that: When I run the program I see only the effect of a run the first function draw() - this with index 0. Then I change places these functions:
_collection[1].draw();
_collection[0].draw();
I still see the effect of the first function - in this case with index number 1.
It looks like there is something blocking the code from the second draw() function to run.
What is the problem? How can I fix it?
The second draw function isn't being blocked from executing. Since your vertice and color information is defined as static inside the body of your draw() function, those values won't change regardless of which element of _collection you are drawing. That's why drawing the two collections yields the same result -- you are drawing your vertices in the same location, and with the same colors.
To fix the problem, you only want to store vertex and color information once. Each of your collections should only contain x and y values, indicating their position. You don't want multiple collections of vertices and colors, you want a single collection of vertices and colors which you draw in several different locations.
You should create your vertex and color arrays in your main function before you enter your main loop. You should also use glGenBuffers and glBindBuffer followed by glBufferData to tell OpenGL about your vertex and color arrays in your main program before your main loop as well. Then you can take the calls to glGenBuffers and glBufferData out of your draw function. You should also call glVertexAttribPointer for both the vertex and color arrays in your main function and remove them from your draw() function.
// Note that your vertex data isn't contingent on 'x' and 'y' positions.
// You will use the vertex shader to move your boxes around later.
GLfloat g_vertex_buffer_data[] = {
0.0f, 0, 0.0f, // lewy górny
0.0f, 0.4f, 0.0f, // lewy dolny
0.4f, 0.4f, 0.0f, // prawy dolny
0.4f, 0.0f, 0.0f, // lewy górny
0.02f, 0.02f, 0.0f, // lewy górny
0.02f, 0.4f + 0.02f, 0.0f, // lewy dolny
0.4f - 0.02f, 0.4f + 0.02f, 0.0f, // prawy dolny
0.4f - 0.02f, 0.02f, 0.0f, // lewy górny
};
GLfloat g_color_buffer_data[] = {
1.0f, 1.0f, 1.0f, // lewy górny
1.0f, 1.0f, 1.0f, // lewy dolny
1.0f, 1.0f, 1.0f, // prawy dolny
1.0f, 1.0f, 1.0f, // lewy górny
0.0f, 0.0f, 1.0f,
0.0f, 0.0f, 1.0f,
0.0f, 0.0f, 1.0f,
0.0f, 0.0f, 1.0f,
};
GLuint vertexbuffer;
glGenBuffers(1, &vertexbuffer);
glBindBuffer(GL_ARRAY_BUFFER, vertexbuffer);
glBufferData(GL_ARRAY_BUFFER, sizeof(g_vertex_buffer_data), g_vertex_buffer_data, GL_STATIC_DRAW);
glVertexAttribPointer(
vertexPosition_modelspaceID, // The attribute we want to configure
3, // size
GL_FLOAT, // type
GL_FALSE, // normalized?
0, // stride
(void*)0 // array buffer offset
);
GLuint colorbuffer;
glGenBuffers(1, &colorbuffer);
glBindBuffer(GL_ARRAY_BUFFER, colorbuffer);
glBufferData(GL_ARRAY_BUFFER, sizeof(g_color_buffer_data), g_color_buffer_data, GL_STATIC_DRAW);
glVertexAttribPointer(
vertexColorID, // The attribute we want to configure
3, // size
GL_FLOAT, // type
GL_FALSE, // normalized?
0, // stride
(void*)0 // array buffer offset
);
// All of the above information you only need to specify to openGL once, not every time you draw a frame!
You need to change your shader so that it accepts the x and y offset from each of your collections:
#version 150
uniform float collectionX;
uniform float collectionY;
in vec3 vertexPosition_modelspaceID; // This is the vertex attribute which the name 'vertexPosition_modelspaceID' corresponds to.
// Remember that your shader will also accept a color and give it to the fragment shader, include that code as well.
void main()
{
gl_Position = vec4(vertexPosition_modelspaceID.x + collectionX, vertexPosition_modelspaceID.y + collectionY, vertexPosition_modelspaceID.z, 1.0);
}
And you need to get the locations of the uniform variables you just added to your shader in your main program before the loop:
// Call these functions after you compile and link your shaders. programID should be your compiled and linked shader program.
GLuint collectionXID = glGetUniformLocation(programID, "collectionX");
GLuint collectionYID = glGetUniformLocation(programID, "collectionY");
Your draw function will be very simple now:
void draw()
{
glDrawArrays(GL_QUADS, 0, 8);
}
Finally, your main loop will look something like this:
do
{
glClear(GL_COLOR_BUFFER_BIT);
glUseProgram(programID);
glEnableVertexAttribArray(vertexPosition_modelspaceID);
glEnableVertexAttribArray(vertexColorID);
glUniform1f(collectionXID, _collection[0].x);
glUniform1f(collectionYID, _collection[0].y);
_collection[0].draw();
glUniform1f(collectionXID, _collection[1].x);
glUniform1f(collectionYID, _collection[1].y);
_collection[1].draw();
glfwSwapBuffers(window);
glDisableVertexAttribArray(vertexPosition_modelspaceID);
glDisableVertexAttribArray(vertexColorID);
glfwPollEvents();
} while(glfwGetKey(window, GLFW_KEY_ESCAPE) != GLFW_PRESS && glfwWindowShouldClose(window) == 0)
Note that you are now specifying the location at which to draw the vertices to your shader program by passing your individual collection's x and y position with the glUniform1f function. It is more common to move your vertices around with a transformation matrix, but that is a rather complicated topic itself.
Assuming the collections have different x and y positions, they will now draw in different locations.