Understanding implementation of glu.PickMatrix() - opengl

I am working on an OpenGL project which requires object selection feature. I use OpenTK framework to do this; however OpenTK doesn't support glu.PickMatrix() method to define the picking region. I ended up googling its implementation and here is what i got:
void GluPickMatrix(double x, double y, double deltax, double deltay, int[] viewport)
{
if (deltax <= 0 || deltay <= 0)
{
return;
}
GL.Translate((viewport[2] - 2 * (x - viewport[0])) / deltax, (viewport[3] - 2 * (y - viewport[1])) / deltay, 0);
GL.Scale(viewport[2] / deltax, viewport[3] / deltay, 1.0);
}
I totally fail to understand this piece of code. Moreover, this doesn't work with my following code sample:
//selectbuffer
private int[] _selectBuffer = new int[512];
private void Init()
{
float[] triangleVertices = new float[] { 0.0f, 1.0f, 0.0f, -1.0f, -1.0f, 0.0f, 1.0f, -1.0f, 0.0f };
float[] _triangleColors = new float[] { 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f };
GL.GenBuffers(2, _vBO);
GL.BindBuffer(BufferTarget.ArrayBuffer, _vBO[0]);
GL.BufferData(BufferTarget.ArrayBuffer, new IntPtr(sizeof(float) * _triangleVertices.Length), _triangleVertices, BufferUsageHint.StaticDraw);
GL.VertexPointer(3, VertexPointerType.Float, 0, 0);
GL.BindBuffer(BufferTarget.ArrayBuffer, _vBO[1]);
GL.BufferData(BufferTarget.ArrayBuffer, new IntPtr(sizeof(float) * _triangleColors.Length), _triangleColors, BufferUsageHint.StaticDraw);
GL.ColorPointer(3, ColorPointerType.Float, 0, 0);
GL.EnableClientState(ArrayCap.VertexArray);
GL.EnableClientState(ArrayCap.ColorArray);
//Selectbuffer set up
GL.SelectBuffer(512, _selectBuffer);
}
private void glControlWindow_Paint(object sender, PaintEventArgs e)
{
GL.Clear(ClearBufferMask.ColorBufferBit);
GL.Clear(ClearBufferMask.DepthBufferBit);
float[] eyes = { 0.0f, 0.0f, -10.0f };
float[] target = { 0.0f, 0.0f, 0.0f };
Matrix4 projection = Matrix4.CreatePerspectiveFieldOfView(0.785398163f, 4.0f / 3.0f, 0.1f, 100f); //45 degree = 0.785398163 rads
Matrix4 view = Matrix4.LookAt(eyes[0], eyes[1], eyes[2], target[0], target[1], target[2], 0, 1, 0);
Matrix4 model = Matrix4.Identity;
Matrix4 MV = view * model;
//First Clear Buffers
GL.Clear(ClearBufferMask.ColorBufferBit);
GL.Clear(ClearBufferMask.DepthBufferBit);
GL.MatrixMode(MatrixMode.Projection);
GL.LoadIdentity();
GL.LoadMatrix(ref projection);
GL.MatrixMode(MatrixMode.Modelview);
GL.LoadIdentity();
GL.LoadMatrix(ref MV);
GL.Viewport(0, 0, glControlWindow.Width, glControlWindow.Height);
GL.Enable(EnableCap.DepthTest); //Enable correct Z Drawings
GL.DepthFunc(DepthFunction.Less); //Enable correct Z Drawings
GL.MatrixMode(MatrixMode.Modelview);
GL.PushMatrix();
GL.Translate(3.0f, 0.0f, 0.0f);
DrawTriangle();
GL.PopMatrix();
GL.PushMatrix();
GL.Translate(-3.0f, 0.0f, 0.0f);
DrawTriangle();
GL.PopMatrix();
//Finally...
GraphicsContext.CurrentContext.VSync = true; //Caps frame rate as to not over run GPU
glControlWindow.SwapBuffers(); //Takes from the 'GL' and puts into control
}
private void DrawTriangle()
{
GL.BindBuffer(BufferTarget.ArrayBuffer, _vBO[0]);
GL.VertexPointer(3, VertexPointerType.Float, 0, 0);
GL.EnableClientState(ArrayCap.VertexArray);
GL.DrawArrays(BeginMode.Triangles, 0, 3);
GL.DisableClientState(ArrayCap.VertexArray);
}
//mouse click event implementation
private void glControlWindow_MouseClick(object sender, System.Windows.Forms.MouseEventArgs e)
{
//Enter Select mode. Pretend drawing.
GL.RenderMode(RenderingMode.Select);
int[] viewport = new int[4];
GL.GetInteger(GetPName.Viewport, viewport);
GL.PushMatrix();
GL.MatrixMode(MatrixMode.Projection);
GL.LoadIdentity();
GluPickMatrix(e.X, e.Y, 5, 5, viewport);
Matrix4 projection = Matrix4.CreatePerspectiveFieldOfView(0.785398163f, 4.0f / 3.0f, 0.1f, 100f); // this projection matrix is the same as one in glControlWindow_Paint method.
GL.LoadMatrix(ref projection);
GL.MatrixMode(MatrixMode.Modelview);
int i = 0;
int hits;
GL.InitNames();
GL.PushMatrix();
GL.Translate(3.0f, 0.0f, 0.0f);
GL.PushName(i);
DrawTriangle();
GL.PopName();
GL.PopMatrix();
i++;
GL.PushMatrix();
GL.Translate(-3.0f, 0.0f, 0.0f);
GL.PushName(i);
DrawTriangle();
GL.PopName();
GL.PopMatrix();
hits = GL.RenderMode(RenderingMode.Render);
.....hits processing code goes here...
GL.PopMatrix();
glControlWindow.Invalidate();
}
I expect to get only one hit everytime i click inside a triangle, but i always get 2 no matter where i click. I suspect there is something wrong with the implementation of the GluPickMatrix, I haven't figured out yet.

Related

Why changing model matrix change size of wrong object in OpenGL?

After scaling model2 matrix. model2 matrix is matrix of grid
Before
I want to increase size of grid not cube. Why is it happened? Why scale set y of cube to 0? Where is the mistake? All my code below.
Look at draw_canvas.cpp paintGL function, there is scaling matrix with name 'model2'. This line model2.scale(10.0f,0.0f,10.0f). Why this scaling changes my cube?
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.translate(QVector3D(0.0f, 0.0f, 0.0f));
model2.scale(10.0f, 0.0f, 10.0f);
draw_canwas3D.h
#pragma once
#include <QOpenGLWidget>
#include <QOpenGLVertexArrayObject>
#include <QOpenGLBuffer>
#include <QOpenGLShaderProgram>
#include <QOpenGLShader>
#include <list>
class Canvas3D : public QOpenGLWidget
{
public:
Canvas3D(QWidget *parent = nullptr) : QOpenGLWidget(parent) { };
~Canvas3D();
private:
QOpenGLVertexArrayObject m_vao;
QOpenGLBuffer m_vbo;
QOpenGLShaderProgram* m_program;
QOpenGLShaderProgram* gridShaderProgram;
QOpenGLShaderProgram* shLightprogram;
QVector3D cameraPos {0.0f, 0.0f, 3.0f};
QVector3D cameraFront{0.0f, 0.0f, -1.0f};
QVector3D cameraUp{0.0f, 1.0f, 0.0f};
float yaw = -90.0f;
float pitch = 0.0f;
float lastX = 400, lastY = 300;
float Zoom = 45.0;
unsigned int gridVAO;
unsigned int gridEBO;
GLuint lenght = 0;
//std::list<Object*>
protected:
void initializeGL() override;
void resizeGL(int w, int h) override;
void paintGL() override;
void keyPressEvent(QKeyEvent *ev) override;
bool eventFilter(QObject *obj, QEvent *event);
};
draw_canvas3D.cpp
#include "draw_canvas3D.h"
#include "src/common/geometry/shapes3D/cube.h"
#include <iostream>
#include <QOpenGLContext>
#include <QOpenGLFunctions>
#include <QOpenGLExtraFunctions>
#include <QMatrix4x4>
#include <QVector3D>
#include <QtMath>
#include <QKeyEvent>
void Canvas3D::initializeGL()
{
connect(this, SIGNAL(frameSwapped()), this, SLOT(update()));
auto functions = this->context()->functions();
functions->glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
this->setGeometry(100, 100, 800, 600);
functions->glViewport(this->geometry().x(),
this->geometry().y(),
this->geometry().width(),
this->geometry().height());
m_program = new QOpenGLShaderProgram(this);
QOpenGLShader vxShader(QOpenGLShader::Vertex);
vxShader.compileSourceFile("/main/stage/home/andreyp/fork_invar/invar/shaders/shader.vs");
QOpenGLShader frShader(QOpenGLShader::Fragment);
frShader.compileSourceFile("/main/stage/home/andreyp/fork_invar/invar/shaders/shader.fs");
m_program->addShader(&vxShader);
m_program->addShader(&frShader);
m_program->link();
functions->glEnable(GL_DEPTH_TEST);
this->installEventFilter(this);
functions->glEnable(GL_POLYGON_SMOOTH);
functions->glHint(GL_LINE_SMOOTH_HINT, GL_NICEST);
functions->glHint(GL_POLYGON_SMOOTH_HINT, GL_NICEST);
/*GRID*/ //Create Class
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;
/*std::vector<QVector3D> vecLines;
std::vector<unsigned int> vecLinesIdx;*/
/*for(unsigned int nHorizontalLines = 0; nHorizontalLines < 10; ++nHorizontalLines)
{
vecLines.push_back(QVector3D(0.0f, 0.0, -(float)nHorizontalLines));
vecLines.push_back(QVector3D(9.0f, 0.0, -(float)nHorizontalLines));
}
for(unsigned int nVerticalLines = 0; nVerticalLines < 10; ++nVerticalLines)
{
vecLines.push_back(QVector3D((float)nVerticalLines, 0.0f, 0.0f));
vecLines.push_back(QVector3D((float)nVerticalLines, 0.0f, -9.0f));
}
for(unsigned int j = 0; j < 39; j+=2)
{
vecLinesIdx.push_back(j);
vecLinesIdx.push_back(j+1);
}*/
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();
/*GRID END*/
}
void Canvas3D::resizeGL(int w, int h)
{
auto functions = this->context()->functions();
functions->glViewport(0,
0,
this->geometry().width(),
this->geometry().height());
}
void Canvas3D::paintGL()
{
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");
model.translate(cubePositions[i]);
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, 0.0f, 10.0f); ------ THIS SCALING
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);
/*GRID END*/
}
void Canvas3D::keyPressEvent(QKeyEvent *ev)
{
const float cameraSpeed = 0.25f;
if(ev->key() == Qt::Key_W)
cameraPos += cameraSpeed * cameraFront;
else if(ev->key() == Qt::Key_S)
cameraPos -= cameraSpeed * cameraFront;
else if(ev->key() == Qt::Key_A)
cameraPos -= QVector3D(QVector3D::crossProduct(cameraFront, cameraUp)) * cameraSpeed;
else if(ev->key() == Qt::Key_D)
cameraPos += QVector3D(QVector3D::crossProduct(cameraFront, cameraUp)) * cameraSpeed;
else if(ev->key() == Qt::Key_Q)
{
/*Rotate camera*/
}
else if(ev->key() == Qt::Key_E)
{
/*Rotate camera*/
}
}
bool Canvas3D::eventFilter(QObject *obj, QEvent *event)
{
if (event->type() == QEvent::MouseButtonPress)
{
QMouseEvent *mouseEvent = static_cast<QMouseEvent*>(event);
lastX = mouseEvent->pos().x();
lastY = mouseEvent->pos().y();
}
if (event->type() == QEvent::MouseMove)
{
QMouseEvent *mouseEvent = static_cast<QMouseEvent*>(event);
float xoffset = mouseEvent->pos().x() - lastX;
float yoffset = lastY - mouseEvent->pos().y();
lastX = mouseEvent->pos().x();
lastY = mouseEvent->pos().y();
float sensitivity = 0.1f;
xoffset *= sensitivity;
yoffset *= sensitivity;
yaw += xoffset;
pitch += yoffset;
if(pitch > 89.0f)
pitch = 89.0f;
if(pitch < -89.0f)
pitch = -89.0f;
QVector3D direction;
direction.setX(qCos(qDegreesToRadians(yaw)) * qCos(qDegreesToRadians(pitch)));
direction.setY(qSin(qDegreesToRadians(pitch)));
direction.setZ(qSin(qDegreesToRadians(yaw)) * qCos(qDegreesToRadians(pitch)));
cameraFront = direction.normalized();
}
else if (event->type() == QEvent::Wheel)
{
QWheelEvent *wheelEvent = static_cast<QWheelEvent*>(event);
QPoint numDegrees = wheelEvent->angleDelta();
if (numDegrees.y() < 0 && Zoom < 45.0f)
Zoom += 1.0f;
if (numDegrees.y() > 0 && Zoom > 1.0f)
Zoom -= 1.0f;
}
return false;
}
Canvas3D::~Canvas3D()
{
//delete f;
}
cube.cpp
#include "src/common/geometry/shapes3D/cube.h"
#include <QOpenGLExtraFunctions>
#include <QOpenGLWidget>
#include <QOpenGLContext>
#include <QtMath>
#include <iostream>
namespace invar::geometry3D
{
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,//Point A 0
-0.5f,0.5f,0.5f, 0.0f, 0.0f, 1.0f,//Point B 1
0.5f,0.5f,-0.5f, 0.0f, 1.0f, 0.0f,//Point C 2
0.5f,0.5f,0.5f, 0.0f, 1.0f, 1.0f,//Point D 3
-0.5f,-0.5f,-0.5f, 1.0f, 0.0f, 0.0f,//Point E 4
-0.5f,-0.5f,0.5f, 1.0f, 0.0f, 1.0f,//Point F 5
0.5f,-0.5f,-0.5f, 1.0f, 1.0f, 0.0f,//Point G 6
0.5f,-0.5f,0.5f, 1.0f, 1.0f, 1.0f//Point H 7
};
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();
}
}
cube.h
#pragma once
#include "shape3D.h"
#include <QOpenGLContext>
#include <QVector3D>
#include <QOpenGLShaderProgram>
namespace invar::geometry3D
{
class Cube
{
public:
Cube(QVector3D pos, float diagonal, QOpenGLShaderProgram* program);
void Draw();
private:
unsigned int VAO;
unsigned int EBO;
QVector3D pos;
float diagonal;
QOpenGLShaderProgram* m_program;
void setupShape();
};
}
Shaders is simple
shaderGrid.fs
#version 130
out vec4 FragColor;
void main()
{
FragColor = vec4(0, 1.0, 1.0, 1.0);
}
shaderGrid.vs
#version 130
in vec3 aPos;
uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;
void main()
{
gl_Position = projection * view * model * vec4(aPos, 1.0);
}
shader.fs //--- CUBE FRAGMENT SHADER
#version 130
out vec4 FragColor;
in vec3 ourColor;
void main()
{
FragColor = vec4(ourColor, 1.0);
}
shader.vs //--- CUBE VERTEX SHADER
#version 130
in vec3 aPos;
in vec3 aColor;
out vec3 ourColor;
uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;
void main()
{
gl_Position = projection * view * model * vec4(aPos, 1.0);
ourColor = aColor;
}
glUniform* changes a uniform of the currently installed shader program. Therefore, you must install (bind) the program before setting the uniforms:
gridShaderProgram->bind(); // <--- INSERT
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, 0.0f, 10.0f); ------ THIS SCALING
model2.translate(QVector3D(0.0f, 0.0f, 0.0f));
functions->glUniformMatrix4fv(modelID2, 1, GL_FALSE, model2.constData());
// gridShaderProgram->bind(); <--- DELETE

OpenGL Rotating an object (set of cubes) around itself rather than the origin (0,0,0)

I'm practicing OpenGL and transformations and have run into a problem. I have a four cubes translated together to form the skeleton of a car. I want to be able to drive and turn the car without the car disassembling itself. Basically, I want the trunk and the front of the car to rotate around the body of the car instead of the origin (0,0,0). I've read that to accomplish this, I must follow the following steps:
Start with an identity matrix
Translate the matrix by -centre of the object
Rotate the matrix by the desired amount
Translate the matrix by centre of the object
Use the resulting matrix to transform the object that you desire to rotate
and have implemented it into my code, but it doesn't work as I expected. Please tell me what I'm doing wrong and the correct way to implement this. I have pasted my code below, and trimmed it so only the important parts are here.
result:
float xMove = 0.0f; //xMove and zMove keeps track of the center of the main body while driving
float zMove = 0.0f;
float carRotate = 0.0f; //degrees of rotation incremented by pressing E
int main()
{
/*some code to initialize the program...*/
while (!glfwWindowShouldClose(window))
{
/*Draw main car body*/
mat4 carMatrix = translate(mat4(1.0f), vec3(0.0f + xMove + x, 0.2f, 0.0f + zMove + z))
* rotate(mat4(1.0f), radians(carRotate), vec3(0.0f, 1.0f, 0.0f))
* scale(mat4(1.0f), vec3(0.5f, 0.4f, 1.0f))
* translate(mat4(1.0f), vec3(0.0f, -0.2f,0.0f));
shaderProgram.setMat4("model", carMatrix);
glDrawArrays(GL_TRIANGLES, 0, 36);
//Bonnet
carMatrix = translate(mat4(1.0f), vec3(0.0f + xMove + x, 0.1f, 0.65f + zMove + z))
* rotate(mat4(1.0f), radians(carRotate), vec3(0.0f, 1.0f, 0.0f))
* scale(mat4(1.0f), vec3(0.3f, 0.2f, 0.3f))
* translate(mat4(1.0f), vec3(xMove, 0.0f, -zMove));
shaderProgram.setMat4("model", carMatrix);
glDrawArrays(GL_TRIANGLES, 0, 36);
//Trunk
carMatrix = translate(mat4(1.0f), vec3(0.0f + xMove + x, 0.1f, -0.65f + zMove + z))
* rotate(mat4(1.0f), radians(carRotate), vec3(0.0f, 1.0f, 0.0f))
* scale(mat4(1.0f), vec3(0.3f, 0.2f, 0.3f))
* translate(mat4(1.0f), vec3(0.0f, 0.0f, 0.0f));
shaderProgram.setMat4("model", carMatrix);
glDrawArrays(GL_TRIANGLES, 0, 36);
//Roof
carMatrix = translate(mat4(1.0f), vec3(0.0f + xMove + x, 0.45f, 0.0f + zMove + z))
* rotate(mat4(1.0f), radians(carRotate), vec3(0.0f, 1.0f, 0.0f))
* scale(mat4(1.0f), vec3(0.3f, 0.1f, 0.6f))
* translate(mat4(1.0f), vec3(0.0f, 0.0f, 0.0f));
shaderProgram.setMat4("model", carMatrix);
glDrawArrays(GL_TRIANGLES, 0, 36);
if (glfwGetKey(window, GLFW_KEY_W) == GLFW_PRESS)
{
zMove++;
}
if (glfwGetKey(window, GLFW_KEY_E) == GLFW_PRESS)
{
carRotate++;
}
}
}
Do not "Translate the matrix by -centre of the object".
Place then entire car in that way, that its center is at (0, 0, 0). Thant means translate each object to its place relative to (0, 0, 0). Then scale and rotate. Finally move the care to the position in the world.
If the relative position of a part of the car to (0, 0, 0) is (relX, relY, relZ), then:
mat4 carMatrix =
translate(mat4(1.0f), vec3(xMove + x, 0.0f, zMove + z) *
rotate(mat4(1.0f), radians(carRotate), vec3(0.0f, 1.0f, 0.0f));
//Bonnet
mat4 partMatrix_1 =
translate(mat4(1.0f), vec3(relX, relY, relZ)) *
scale(mat4(1.0f), vec3(0.5f, 0.4f, 1.0f));
shaderProgram.setMat4("model", carMatrix * partMatrix_1);
glDrawArrays(GL_TRIANGLES, 0, 36);
//Trunk
mat4 partMatrix_2 =
translate(mat4(1.0f), vec3(relX2, relY2, relZ2)) *
scale(mat4(1.0f), vec3(0.3f, 0.2f, 0.3f));
shaderProgram.setMat4("model", carMatrix * partMatrix_2);
glDrawArrays(GL_TRIANGLES, 0, 36);
// [...]
Note, I recommend to scale each part first. After that put it to its place.
relX, relY, relZ are constant and individual coordinates, for each component of the car.

only one value inserted in vector in opengl code

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);
}

Instancing millions of objects in OpenGL: improving frames-per-second

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.

OpenGL - Rendering point light with cube shadow map?

I'm having trouble rendering cube shadow map. Negative and positive seem to be reversed and I can't figure out why. Tried to reverse targets and directions, but I get event weirder output.
Code:
// init
glGenTextures(1, &mShadowCubeTex);
glBindTexture(GL_TEXTURE_CUBE_MAP, mShadowCubeTex);
for(int i = 0; i < 6; i++)
glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_DEPTH_COMPONENT24, SHADOW_WIDTH, SHADOW_WIDTH, 0, GL_DEPTH_COMPONENT, GL_FLOAT, NULL);
/* + filters + wrapping */
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_COMPARE_FUNC, GL_LEQUAL);
glGenFramebuffers(1, &mShadowFbo);
glBindFramebuffer(GL_FRAMEBUFFER, mShadowFbo);
glDrawBuffer(GL_NONE);
glReadBuffer(GL_NONE);
glFramebufferTexture(GL_FRAMEBUFFER, GL_DEPTH_COMPONENT, mShadowCubeTex, 0);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
// render
static const struct {
GLenum eTarget;
vec3 vDirection;
vec3 vUp;
} oPointLightConfigs[6] = {
{ GL_TEXTURE_CUBE_MAP_POSITIVE_X, vec3( 1.0f, 0.0, 0.0f), vec3(0.0f, 1.0f, 0.0f) },
{ GL_TEXTURE_CUBE_MAP_NEGATIVE_X, vec3(-1.0f, 0.0, 0.0f), vec3(0.0f, 1.0f, 0.0f) },
{ GL_TEXTURE_CUBE_MAP_POSITIVE_Y, vec3( 0.0f, 1.0, 0.0f), vec3(0.0f, 0.0f,-1.0f) },
{ GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, vec3( 0.0f,-1.0, 0.0f), vec3(0.0f, 0.0f, 1.0f) },
{ GL_TEXTURE_CUBE_MAP_POSITIVE_Z, vec3( 0.0f, 0.0, 1.0f), vec3(0.0f, 1.0f, 0.0f) },
{ GL_TEXTURE_CUBE_MAP_NEGATIVE_Z, vec3( 0.0f, 0.0,-1.0f), vec3(0.0f, 1.0f, 0.0f) }
};
// shadow pass
for(int i = 0; i < 6; i++)
{
glBindFramebuffer(target, mShadowFbo);
glViewport(0, 0, SHADOW_WIDTH, SHADOW_WIDTH);
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, oPointLightConfigs[i].eTarget, mShadowCubeTex, 0);
glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
glDrawBuffer(GL_NONE);
glReadBuffer(GL_NONE);
glCullFace(GL_FRONT);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
for(CModel* pModel : mModels)
{
if(pModel->hasOption(NO_SHADOW_CAST))
{
mNullProgram->use(); // no color output
mat4 M = pModel->getModelMatrix();
mat4 V = lookAt(pPointLight->getPosition(), oPointLightConfigs[i].vDirection, oPointLightConfigs[i].vUp);
mat4 P = mCamera->getProjectionMatrix();
pNullProgram->set("MVP", P * V * M);
pModel->draw();
}
}
}
// color pass
glBindFramebuffer(GL_FRAMEBUFFER, 0);
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
glDrawBuffer(GL_BACK);
glReadBuffer(GL_BACK);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
pLightProgram->use();
glActiveTexture(GL_TEXTURE6);
glBindTexture(GL_TEXTURE_2D, mShadowCubeTex);
pLightProgram->set("V", mCamera->getViewMatrix());
pLightProgram->set("P", mCamera->getProjectionMatrix());
/* set program light uniforms */
for(CModel* pModel : mModels)
{
pLightProgram->set("M", pModel->getModelMatrix());
pModel->draw()
}
// lighting fragment shader
float computeShadowFactor(in samplerCube sShadowMap, in vec3 vL) {
/* vL = vLightPosition - vVertexPosition */
vec3 vLAbs = abs(vL);
float fNear = 0.1;
float fFar = 1000.0;
float fDepth = max(vLAbs.x, max(vLAbs.y, vLAbs.z));
fDepth = ((fFar + fNear) / (fFar - fNear)) - ((2.0 * fFar * fNear) / (fFar - fNear)) / fDepth;
fDepth = (fDepth + 1.0) * 0.5;
float fShadow = texture(sShadowMap, -vL).r; /* -vL = vVertexPosition - vLightPosition */
if(fDepth > fShadow)
return 0.0;
return 1.0;
}
Output:
Update:
I added + oPointLightConfigs[i].vDirection to...
V = lookat(pPointLight->mPosition, pPointLight->mPosition + oPointLightConfigs[i].vDirection, oPointLightConfigs[i].vUp);
Output:
At least now I get one shadow(instead of 6). But it's somehow reversed and the Y axis has no shadow. When the shadow passes from one cubemap face to another it get interrupted. Any hints?
These problems were caused by inverted Y's on cubemaps.
I used:
{ GL_TEXTURE_CUBE_MAP_POSITIVE_Y, math::vec3( 0.0f,-1.0, 0.0f), math::vec3(0.0f, 0.0f, 1.0f) },
{ GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, math::vec3( 0.0f, 1.0, 0.0f), math::vec3(0.0f, 0.0f, 1.0f) },
for setting up the shadow cubemap and:
vL.y *= -1.0;
inside computeShadowFactor() before sampling the shadow cubemap texture.
Note: My +Z axis goes into the monitor - left hand coordinate system.