PDA

View Full Version : UniformBuffers in OpnGL 3.0+



MrKaktus
09-20-2009, 07:51 AM
Hi I have question, how I connect uniform buffer with array of structs in vertex shader in modern OGL 3.0+ way?
I think glUniformBuffer is deprecated isn't it?

I understand that I should create and fill this buffer in such way:


GLuint vbuffer;
glGenBuffers(1, &vbuffer);
glBindBuffer(GL_UNIFORM_BUFFER_EXT, vbuffer);
glBufferData(GL_UNIFORM_BUFFER_EXT, vsize, tmp,GL_STATIC_READ);


But from where I can get max uniform buffer size in bytes?
And how to connect it with declaration in shader source?

marshats
09-20-2009, 04:40 PM
Take this reply in context. I would like to learn this also -- I am just reading <a href="http://www.opengl.org/registry/specs/ARB/uniform_buffer_object.txt" rel="nofollow" target="_blank">ARB_uniform_buffer_object
</a> where there is a section named "Sample Code"... I suggest reading that too.

Assume in your shader code


layout(std140) uniform colors0
{
float DiffuseCool;
float DiffuseWarm;
vec3 SurfaceColor;
vec3 WarmColor;
vec3 CoolColor;
};


It looks like the relevant code to "connect it[uniform_buffer_object] with declaration in shader source" to be



//Update the uniforms using ARB_uniform_buffer_object
glGenBuffers(1, &amp;buffer_id);

//There's only one uniform block here, the 'colors0' uniform block.
//It contains the color info for the gooch shader.
uniformBlockIndex = glGetUniformBlockIndex(prog_id, "colors0");

//We need to get the uniform block's size in order to back it with the
//appropriate buffer
glGetActiveUniformBlockiv(prog_id, uniformBlockIndex,
GL_UNIFORM_BLOCK_DATA_SIZE,
&amp;uniformBlockSize);
glError();

//Create UBO.
glBindBuffer(GL_UNIFORM_BUFFER, buffer_id);
glBufferData(GL_UNIFORM_BUFFER, uniformBlockSize,
NULL, GL_DYNAMIC_DRAW);

//Now we attach the buffer to UBO binding point 0...
glBindBufferBase(GL_UNIFORM_BUFFER, 0, buffer_id);
//And associate the uniform block to this binding point.
glUniformBlockBinding(prog_id, uniformBlockIndex, 0);
glError();

...

// then when you go to render

glBindBuffer(GL_UNIFORM_BUFFER, buffer_id);
//We can use BufferData to upload our data to the shader,
//since we know it's in the std140 layout
glBufferData(GL_UNIFORM_BUFFER, 80, colors, GL_DYNAMIC_DRAW);
(!!!insert my comment, 80 =sizeof(colors) !!!)
//With a non-standard layout, we'd use BufferSubData for each uniform.
glBufferSubData(GL_UNIFORM_BUFFER_EXT, offset, singleSize, &amp;colors[8]);


Maybe that will give us some hints :)

MrKaktus
09-21-2009, 12:22 AM
So I understand that I could use std140 layout for UBO which gives me simplicity to update it at once but with cost of VRAM (every component is expanded to float4 I assume?).

Alternative is my own UBO layout with good packed structures (no holes) but then I need to call glBufferSubData() for every struct in UBO? So for eg. in for loop 10 times if I have data for 10 instances in it?

I think in second approach there also can be some performance loose because of fact gathering non float4 aligned data.
So in above egsample the best approach would be to use std140 with modified struct:



layout(std140) uniform colors0
{
struct warm
{
float Diffuse;
vec3 Color;
} Warm;

struct cool
{
float Diffuse;
vec3 Color;
} Cool;

vec3 SurfaceColor;
};


I think it would be now packed as three float4 so we loose
only 4 bytes for offset and sizeof(colors) should be
3x4x4 = 48.

If something is wrong please correct me :).

Rob Barris
09-21-2009, 08:54 AM
I believe that's correct per rule 3:

http://www.opengl.org/registry/specs/ARB/uniform_buffer_object.txt

(1) If the member is a scalar consuming <N> basic machine units, the
base alignment is <N>.

(2) If the member is a two- or four-component vector with components
consuming <N> basic machine units, the base alignment is 2<N> or
4<N>, respectively.

(3) If the member is a three-component vector with components consuming
<N> basic machine units, the base alignment is 4<N>.

(4) If the member is an array of scalars or vectors, the base alignment
and array stride are set to match the base alignment of a single
array element, according to rules (1), (2), and (3), and rounded up
to the base alignment of a vec4. The array may have padding at the
end; the base offset of the member following the array is rounded up
to the next multiple of the base alignment.

(5) If the member is a column-major matrix with <C> columns and <R>
rows, the matrix is stored identically to an array of <C> column
vectors with <R> components each, according to rule (4).

(6) If the member is an array of <S> column-major matrices with <C>
columns and <R> rows, the matrix is stored identically to a row of
<S>*<C> column vectors with <R> components each, according to rule
(4).

(7) If the member is a row-major matrix with <C> columns and <R> rows,
the matrix is stored identically to an array of <R> row vectors
with <C> components each, according to rule (4).

(8) If the member is an array of <S> row-major matrices with <C> columns
and <R> rows, the matrix is stored identically to a row of <S>*<R>
row vectors with <C> components each, according to rule (4).

(9) If the member is a structure, the base alignment of the structure is
<N>, where <N> is the largest base alignment value of any of its
members, and rounded up to the base alignment of a vec4. The
individual members of this sub-structure are then assigned offsets
by applying this set of rules recursively, where the base offset of
the first member of the sub-structure is equal to the aligned offset
of the structure. The structure may have padding at the end; the
base offset of the member following the sub-structure is rounded up
to the next multiple of the base alignment of the structure.

(10) If the member is an array of <S> structures, the <S> elements of
the array are laid out in order, according to rule (9).


PS - that said, it might well turn out that using the "packed" form could come out smaller on common implementations, then you just need to query the offsets at runtime. Setting up your code to not require things to line up the same way as a C struct can be helpful.

MrKaktus
10-05-2009, 10:02 AM
Can I do something like this (part in my framework treat as pseudocode):

In constructor I prepare buffers/program connection once:



m_gpu = enRenderingContext::getPointer();

// Create VBO with tile description
uint8 row[] = {2, FLOAT3, FLOAT2 };
m_tileID = m_gpu->render.input.create((void*)tile,3,row);

// Create VS, FS and combined program
m_objectsID[0] = loadShader(EN_VERTEX, string("resources/T2_vs.txt"));
m_objectsID[1] = loadShader(EN_FRAGMENT, string("resources/Fbasic.txt"));
m_progID = m_gpu->render.program.create(2,m_objectsID);
m_gpu->render.program.compile(m_progID);

// Connect VBO to program input
m_ogl_progID = m_gpu->render.program.handle(m_progID);
glBindAttribLocation(m_ogl_progID, 0, "mPosition");
glBindAttribLocation(m_ogl_progID, 1, "mTexCoord0");

// Get handles to program params
m_ogl_mvpID = glGetUniformLocation(m_ogl_progID, "matMVP");

// Create UBO for instancing data
glGenBuffers(1, &amp;m_uboID);
glBufferData(GL_UNIFORM_BUFFER, (T2_MAX_SECTORS * sizeof(sector_desc)), NULL, GL_DYNAMIC_DRAW);

// Connect UBO to UB in program (through binding table)
m_ogl_ubID = glGetUniformBlockIndex(m_ogl_progID, string("Sectors").c_str() );
if (m_ogl_ubID != GL_INVALID_INDEX)
{
sint32 ub_size = 0;
glGetActiveUniformBlockiv(m_ogl_progID, m_ogl_ubID, GL_UNIFORM_BLOCK_DATA_SIZE, &amp;ub_size);

if (ub_size == (T2_MAX_SECTORS * sizeof(sector_desc)))
{
glBindBufferBase(GL_UNIFORM_BUFFER, 0, m_uboID);
glUniformBlockBinding(m_ogl_progID, m_ogl_ubID, 0); // 0 czy m_uboID ??
}
}


Then in rendering I just do something like that:



// Fill Tiles Data Buffer (UBO)
glBindBuffer(GL_UNIFORM_BUFFER, m_uboID);
glBufferData(GL_UNIFORM_BUFFER, sectors * sizeof(sector_desc), &amp;m_sectors[0],GL_DYNAMIC_DRAW); // STATIC READ ?? are this correct ??

// Set the rest of params
float4x4 mvpMatrix = mul(camera->projectionMatrix(),mul(camera->matrix(),this->matrix()));
glUniformMatrix4fv(m_ogl_mvpID, 1, false, (const GLfloat *)&amp;mvpMatrix.m); // set the planet projection matrix

m_gpu->render.program.use(m_progID);
m_gpu->render.input.draw(m_tileID,EN_TRIANGLES,sectors);


So basically I once connect everything together and then I
just update UBO with new data. But this isn't working so I'm trying to figure out where I could have an error.

Do you have some suggestions?

EDIT:
It turn's out that it works and error was not in the UBO's.

Second question:
Does updating UBO every frame hurts performance a lot?

marshats
10-08-2009, 11:39 PM
>Does updating UBO every frame hurts performance a lot?

I was wondering the same thing. I did some very specific benchmarks with my particular code (ie risky to extrapolate to a general case) but found that using the uniform block decreased the time to draw my scene significantly (about 50%). So the UBO actually helped my performance, even though I sent the entire block of data each frame (glBindBuffer + glBindBufferData just like you do in your renderframe).

I am attaching my code based on GLUT, openGL Mathematics Library (http://glm.g-truc.net/) and using the CPU timing instruction rdtsc. And I have some 2MB vertices/normals files (blender_droid_noidtluom.*) that are too big to post here :). This may be too specific for anyone else to compile and run but it will show what I am using for the very specific benchmark ... Just including the code to be complete.


unsigned long long int rdtsc(void);

// g++ -DFULLSCREEN -DUSE_IDLE -DREGULATE_FPS -I./include/ glut_opengl3_helloworld_uniform_block.cpp lib/libglut.a -lXxf86vm -L./lib/ -lglut -lGLEW -DGL3_PROTOTYPES
// g++ -DUSE_IDLE -DREGULATE_FPS -I./include/ glut_opengl3_helloworld_uniform_block.cpp lib/libglut.a -lXxf86vm -L./lib/ -lglut -lGLEW -DGL3_PROTOTYPES
// -D [ USE_IDLE | REGULATE_FPS | XGLUNIFORMMATRIX4FV ]

#include <GL/glew.h> // great opengl extensions helper
#include <GL/gl3.h>
#include <GL/freeglut.h> // use of opengl 3.0 context requires freeglut!

//OpenGL Mathematics (GLM). A C++ mathematics library for 3D graphics.
#include <glm/glm.h>
#include <glm/glmext.h>
#include <glm/GLM_VIRTREV_address.h> // gives helper function address(X) instead of using &amp;X[0][0]
namespace glm
{
// read glm/gtx/transform.h for function prototypes
using GLM_GTX_transform;
using GLM_GTX_matrix_projection;
using GLM_GTX_transform2; // for lookAt
}
using namespace glm;

#include <cstdio>
#include <cmath>
#include <iostream>
#include <cstring>
#include <cassert>

#define MAKE_GLSL_STRING(A) #A

#ifdef XGLUNIFORMMATRIX4FV
inline void XglUniformMatrix4fv (const GLint &amp;a, const GLsizei &amp;b, const GLboolean &amp;c, const mat4 &amp;d)
{
glUniformMatrix4fv (a, b, c, address(d));
};
#endif

//quick helper for printing mat4 values
std::ostream&amp; operator << (std::ostream&amp; os, glm::mat4&amp; M)
{
os << M[0][0] << "\t" << M[0][1] << "\t" << M[0][2] << "\t" << M[0][3] << std::endl;
os << M[1][0] << "\t" << M[1][1] << "\t" << M[1][2] << "\t" << M[1][3] << std::endl;
os << M[2][0] << "\t" << M[2][1] << "\t" << M[2][2] << "\t" << M[2][3] << std::endl;
os << M[3][0] << "\t" << M[3][1] << "\t" << M[3][2] << "\t" << M[3][3] << std::endl;
return os;
};

//quick helper for printing flat matrix values
void show_gl4x4 (GLfloat *M)
{
std::cout << M[0] << "\t" << M[1] << "\t" << M[2] << "\t" << M[3] << std::endl;
std::cout << M[4] << "\t" << M[5] << "\t" << M[6] << "\t" << M[7] << std::endl;
std::cout << M[8] << "\t" << M[9] << "\t" << M[10] << "\t" << M[11] << std::endl;
std::cout << M[12] << "\t" << M[13] << "\t" << M[14] << "\t" << M[15] << std::endl;
};

GLfloat uniformBlock_colors0[] =
{ //layout(std140) uniform colors0
0.45, 1,1,1, //float DiffuseCool
0.45, 1,1,1, //float DiffuseWarm
0.75,0.75,0.75, 1, //vec3 SurfaceColor
0.0,0.0,1.0, 1, //vec3 WarmColor
0.0,1.0,0.0, 1, //vec3 CoolColor
};
GLuint sizeof_uniformBlock_colors0 = sizeof(uniformBlock_colors0);

//convenience map into uniformBlock_colors0
GLfloat &amp;DiffuseCool = (GLfloat&amp;)uniformBlock_colors0[0];
GLfloat &amp;DiffuseWarm = (GLfloat&amp;)uniformBlock_colors0[4];
vec3 &amp;SurfaceColor = (vec3&amp;)uniformBlock_colors0[8];
vec3 &amp;WarmColor = (vec3&amp;)uniformBlock_colors0[12];
vec3 &amp;CoolColor = (vec3&amp;)uniformBlock_colors0[16];

GLfloat uniformBlock_matrix1[] =
{ //layout(std140) uniform matrix1
1.0,0.0,0.0,0.0, //mat4 glm_ProjectionMatrix
0.0,1.0,0.0,0.0,
0.0,0.0,1.0,0.0,
0.0,0.0,0.0,1.0,
1.0,0.0,0.0,0.0, //mat4 glm_ModelViewMatrix
0.0,1.0,0.0,0.0,
0.0,0.0,1.0,0.0,
0.0,0.0,0.0,1.0,
1.0,0.0,0.0,0.0, //mat4 glm_NormalMatrix
0.0,1.0,0.0,0.0,
0.0,0.0,1.0,0.0,
0.0,0.0,0.0,1.0,
};
GLuint sizeof_uniformBlock_matrix1 = sizeof(uniformBlock_matrix1);

//convenience map into uniformBlock_matrix1
mat4 &amp;glm_ProjectionMatrix = (mat4&amp;)uniformBlock_matrix1[0];
mat4 &amp;glm_ModelViewMatrix = (mat4&amp;)uniformBlock_matrix1[16];
mat4 &amp;glm_NormalMatrix = (mat4&amp;)uniformBlock_matrix1[32];

class CustomGraphicsPipeline
{
GLuint shader_id;
GLuint uniformBlock_matrix1_id; // uniform block matrix1
GLuint uniformBlock_colors0_id; // uniform block colors0
#ifdef XGLUNIFORMMATRIX4FV
mat4 glm_ProjectionMatrix; //cpu-side
GLint glm_ProjectionMatrix_id; //cpu-side hook to shader uniform
mat4 glm_ModelViewMatrix; //cpu-side
GLint glm_ModelViewMatrix_id; //cpu-side hook to shader uniform
mat4 glm_NormalMatrix; //cpu-side
GLint glm_NormalMatrix_id; //cpu-side hook to shader uniform
#endif
GLuint vao_id[2]; // vertex array object hook id
GLuint vao_elementcount[2]; // number of attribute elements in vao ie number of vertices
public:
CustomGraphicsPipeline() :
shader_id(NULL),
#ifdef XGLUNIFORMMATRIX4FV
glm_ProjectionMatrix( mat4(1.0f) ), // identity matrix
glm_ProjectionMatrix_id(NULL),
glm_ModelViewMatrix( mat4(1.0f) ), // identity matrix
glm_ModelViewMatrix_id(NULL),
glm_NormalMatrix( mat4(1.0f) ), // identity matrix
glm_NormalMatrix_id(NULL),
#else
uniformBlock_matrix1_id(NULL),
#endif
uniformBlock_colors0_id(NULL)
{
vao_id[0]=NULL;
vao_id[1]=NULL;
vao_elementcount[0]=0;
vao_elementcount[1]=0;
}
~CustomGraphicsPipeline() {}

bool Init() // All Setup For OpenGL Goes Here
{
GLint ret = true; // optimistic return value upon completion of execution

#ifdef DEBUG
std::cout << "GL_VERSION: " << glGetString(GL_VERSION) << std::endl;
std::cout << "GL_EXTENSIONS: " << glGetString(GL_EXTENSIONS) << std::endl;
std::cout << "GL_RENDERER: " << glGetString(GL_RENDERER) << std::endl;
std::cout << "GL_VENDOR: " << glGetString(GL_VENDOR) << std::endl;
std::cout << "GLU_VERSION: " << gluGetString(GLU_VERSION) << std::endl;
std::cout << "GLU_EXTENSIONS: " << gluGetString(GLU_EXTENSIONS) << std::endl;
#ifdef GLUT_XLIB_IMPLEMENTATION
std::cout << "GLUT_API_VERSION: " << GLUT_API_VERSION << std::endl;
std::cout << "GLUT_XLIB_IMPLEMENTATION: " << GLUT_XLIB_IMPLEMENTATION << std::endl;
#endif
#endif

glEnable(GL_DEPTH_TEST);
glClearColor(0.2f, 0.2f, 0.2f, 0.5f);

//Datas destioned for video memory, can be local (and lost after bound to GPU!).
GLfloat vertices0[] = { // dgl_Vertex
1.0, -1.0, 0.0, 1.0, // xyzw
-1.0, -1.0, 0.0, 1.0,
0.0, 1.0, 0.0, 1.0
};
size_t Nbytes_vertices0=sizeof(vertices0);

GLfloat normals0[] = { // dgl_Normal
0.0, 0.0, 1.0, 1.0, // xyzw
0.0, 0.0, 1.0, 1.0,
0.0, 0.0, 1.0, 1.0
};
size_t Nbytes_normals0=sizeof(normals0);

GLfloat colors0[] = { // dgl_Color
0.0, 0.0, 1.0, 1.0, //rgba
0.0, 1.0, 0.0, 1.0,
1.0, 0.0, 0.0, 1.0
};
size_t Nbytes_colors0=sizeof(colors0);

GLfloat vertices1[] = { // dgl_Vertex
#include "blender_droid_noidtluom.vertices"
};
size_t Nbytes_vertices1=sizeof(vertices1);

GLfloat normals1[] = { // dgl_Normal
#include "blender_droid_noidtluom.normals"
};
size_t Nbytes_normals1=sizeof(normals1);


GLfloat colors1[] = { // dgl_Color
#include "blender_droid_noidtluom.vertices"
};
size_t Nbytes_colors1=sizeof(colors1);

const GLchar *g_vertexShader[] =
{
//GLSL "#" directives can't be inside MAKE_GLSL_STRING, put before MAKE_GLSL_STRING!
"#version 150\n",
MAKE_GLSL_STRING(
// Vertex shader for Gooch shading
// Author: Randi Rost
// Copyright (c) 2002-2006 3Dlabs Inc. Ltd.
// See 3Dlabs-License.txt for license information
//uniform mat4 glm_ProjectionMatrix; // replaces deprecated gl_ProjectionMatrix see http://www.lighthouse3d.com/opengl/glsl/index.php?minimal
//uniform mat4 glm_ModelViewMatrix; // replaces deprecated gl_ModelViewMatrix
//[tsm]http://www.lighthouse3d.com/opengl/glsl/index.php?normalmatrix
//[tsm]gl_NormalMatrix is transpose(inverse(gl_ModelViewMatrix))
//uniform mat4 glm_NormalMatrix; // replaces deprecated gl_ModelViewMatrix

#ifndef XGLUNIFORMMATRIX4FV
layout(std140) uniform matrix1
{
#endif
uniform mat4 glm_ProjectionMatrix; // replaces deprecated gl_ProjectionMatrix see http://www.lighthouse3d.com/opengl/glsl/index.php?minimal
uniform mat4 glm_ModelViewMatrix; // replaces deprecated gl_ModelViewMatrix
//[tsm]http://www.lighthouse3d.com/opengl/glsl/index.php?normalmatrix
//[tsm]gl_NormalMatrix is transpose(inverse(gl_ModelViewMatrix))
uniform mat4 glm_NormalMatrix;
#ifndef XGLUNIFORMMATRIX4FV
};
#endif

in vec4 dgl_Vertex; // replaces deprecated gl_Vertex
in vec4 dgl_Normal; // replaces deprecated gl_Normal
in vec4 dgl_Color; // replaces deprecated gl_Color

out float NdotL;
out vec3 ReflectVec;
out vec3 ViewVec;
invariant out vec4 Color; // to fragment shader

vec3 LightPosition = vec3(5.0, 5.0, 5.0);

void main(void)
{
vec3 ecPos = vec3 (glm_ModelViewMatrix * dgl_Vertex);
vec3 tnorm = normalize(glm_NormalMatrix * dgl_Normal).xyz; //[tsm] kludge added .xyz at end
vec3 lightVec = normalize(LightPosition - ecPos);
ReflectVec = normalize(reflect(-lightVec, tnorm));
ViewVec = normalize(-ecPos);
NdotL = (dot(lightVec, tnorm) + 1.0) * 0.5;
Color = dgl_Color;
gl_Position = glm_ProjectionMatrix*glm_ModelViewMatrix*dgl_Verte x; // replaces deprecated ftransform()
}
)
};

const GLchar *g_fragmentShader[] =
{
//GLSL "#" directives can't be inside MAKE_GLSL_STRING, put before MAKE_GLSL_STRING!
"#version 150\n",
"#extension GL_ARB_uniform_buffer_object : enable\n",
MAKE_GLSL_STRING(
// Fragment shader for Gooch shading, adapted for ARB_uniform_buffer_object

layout(std140) uniform colors0
{
float DiffuseCool;
float DiffuseWarm;
vec3 SurfaceColor;
vec3 WarmColor;
vec3 CoolColor;
};

invariant in vec4 Color; // from vertex shader
in float NdotL;
in vec3 ReflectVec;
in vec3 ViewVec;

out vec4 dgl_FragColor; // replaces deprecated gl_FragColor

void main (void)
{
vec3 kcool = min(CoolColor + DiffuseCool * SurfaceColor, 1.0);
vec3 kwarm = min(WarmColor + DiffuseWarm * SurfaceColor, 1.0);
vec3 kfinal = mix(kcool, kwarm, NdotL);

vec3 nreflect = normalize(ReflectVec);
vec3 nview = normalize(ViewVec);

float spec = max(dot(nreflect, nview), 0.0);
spec = pow(spec, 32.0);

dgl_FragColor = vec4 (min(kfinal + spec, 1.0), 1.0); // gl_FragColor is deprecated
//dgl_FragColor = Color; // gl_FragColor is deprecated
}
)
};

// compile Vertex shader
GLuint m_vxShaderId = glCreateShader(GL_VERTEX_SHADER);
GLsizei nlines_vx = sizeof(g_vertexShader)/sizeof(const GLchar*);
glShaderSource(m_vxShaderId, nlines_vx, (const GLchar**)g_vertexShader, NULL);
glCompileShader(m_vxShaderId);
CheckShader(m_vxShaderId, GL_COMPILE_STATUS, &amp;ret, "unable to compile the vertex shader!");

// compile Fragment shader
GLuint m_fgShaderId = glCreateShader(GL_FRAGMENT_SHADER);
GLsizei nlines_fg = sizeof(g_fragmentShader)/sizeof(const GLchar*);
glShaderSource(m_fgShaderId, nlines_fg, (const GLchar**)g_fragmentShader, NULL);
glCompileShader(m_fgShaderId);
CheckShader(m_fgShaderId, GL_COMPILE_STATUS, &amp;ret, "unable to compile the fragment shader!");

// link shaders
shader_id = glCreateProgram();
glAttachShader(shader_id, m_vxShaderId);
glAttachShader(shader_id, m_fgShaderId);
glLinkProgram(shader_id);
CheckShader(shader_id, GL_LINK_STATUS, &amp;ret, "unable to link the program!");

//hooks from CPU to GPU
#ifdef XGLUNIFORMMATRIX4FV
//define Uniform hooks
glm_ProjectionMatrix_id = glGetUniformLocation(shader_id, "glm_ProjectionMatrix");
glm_ModelViewMatrix_id = glGetUniformLocation(shader_id, "glm_ModelViewMatrix");
glm_NormalMatrix_id = glGetUniformLocation(shader_id, "glm_NormalMatrix");
#endif

//better to send block of uniforms rather than a single uniform at a time?
//"layout(std140) uniform colors0"
defineUniformBlockObject(0,"colors0",uniformBlock_colors0_id);

#ifndef XGLUNIFORMMATRIX4FV
//better to send block of uniforms rather than a single uniform at a time?
//"layout(std140) uniform matrix1"
defineUniformBlockObject(1,"matrix1",uniformBlock_matrix1_id);
#endif

//guard that all attributes have same number of elements
assert(Nbytes_vertices0/4/sizeof(GLfloat)==Nbytes_colors0/4/sizeof(GLfloat));
vao_elementcount[0]=Nbytes_vertices0/4/sizeof(GLfloat); // number of elements for first VAO
assert(Nbytes_vertices1/4/sizeof(GLfloat)==Nbytes_colors1/4/sizeof(GLfloat));
vao_elementcount[1]=Nbytes_vertices1/4/sizeof(GLfloat); // number of elements for second VAO

//create and define vertex array objects
glGenVertexArrays(2, &amp;vao_id[0]); // vao_id[#] will be referenced in Draw()
defineVertexArrayObject(vao_id[0],Nbytes_vertices0,4,GL_FLOAT,vertices0,normals0,co lors0); //VertexAttribArray: vertices0, colors0
defineVertexArrayObject(vao_id[1],Nbytes_vertices1,4,GL_FLOAT,vertices1,normals1,co lors1); //VertexAttribArray: vertices1, colors1

// finally, use the shader for rendering
glUseProgram(shader_id); // select the shaders program

return ret; // Initialization Went OK?
}

void defineVertexArrayObject(GLuint vaoId, size_t Nbytes, GLint size, GLenum type, GLfloat *vertices, GLfloat *normals, GLfloat *colors)
{
//enable vertex array object to be defined
glBindVertexArray(vaoId);

//generate VBO foreach 'in'; dgl_Vertex, dgl_Normal, and dgl_Color
GLuint m_vboId[3];
glGenBuffers(3, &amp;m_vboId[0]);

//"in vec4 dgl_Vertex;",
glBindBuffer(GL_ARRAY_BUFFER, m_vboId[0] ); // enable the 1st VBO
glBufferData(GL_ARRAY_BUFFER, Nbytes, vertices, GL_STATIC_DRAW); // fill the VBO with vertices data
const GLuint index_mPosition = glGetAttribLocation(shader_id,"dgl_Vertex"); // get ID for "dgl_Vertex"
glVertexAttribPointer(index_mPosition, size, type, GL_FALSE, 0, 0); // VBO point to the "dgl_Vertex" attribute
glEnableVertexAttribArray(index_mPosition); // enable VBO vertex attribute ("dgl_Vertex")

//"in vec4 dgl_Normal;",
glBindBuffer(GL_ARRAY_BUFFER, m_vboId[1] ); // enable the 2nd VBO
glBufferData(GL_ARRAY_BUFFER, Nbytes, normals, GL_STATIC_DRAW); // fill the VBO with vertices data
const GLuint index_mNormal = glGetAttribLocation(shader_id,"dgl_Normal"); // get ID for "dgl_Normal"
glVertexAttribPointer(index_mNormal, size, type, GL_FALSE, 0, 0); // VBO point to the "dgl_Normal" attribute
glEnableVertexAttribArray(index_mNormal); // enable VBO vertex attribute ("dgl_Normal")

//"in vec4 dgl_Color;",
glBindBuffer(GL_ARRAY_BUFFER, m_vboId[2]); // enable the 3rd VBO
glBufferData(GL_ARRAY_BUFFER, Nbytes , colors, GL_STATIC_DRAW); // fill the 2nd VBO with colors data
const GLuint index_mcolor = glGetAttribLocation(shader_id,"dgl_Color"); // get ID for "dgl_Color"
glVertexAttribPointer(index_mcolor, size, type, GL_FALSE, 0, 0); // VBO point to the "dgl_Color" attribute
glEnableVertexAttribArray(index_mcolor); // enable VBO vertex attribute ("dgl_Color")
}

void defineUniformBlockObject(GLuint binding_point, const char *GLSL_block_string, GLuint &amp;uniformBlock_id)
{
// externally used values //////////////////////////////////////////
//Update the uniforms using ARB_uniform_buffer_object
glGenBuffers(1, &amp;uniformBlock_id);
////////////////////////////////////////////////////////////////////

//"layout(std140) uniform GLSL_block_string"
GLuint uniformBlockIndex = glGetUniformBlockIndex(shader_id, GLSL_block_string);

//And associate the uniform block to binding point
glUniformBlockBinding(shader_id, uniformBlockIndex, binding_point);

//Now we attach the buffer to UBO binding_point...
glBindBufferBase(GL_UNIFORM_BUFFER, binding_point, uniformBlock_id);

//We need to get the uniform block's size in order to back it with the
//appropriate buffer
GLsizei uniformBlockSize;
glGetActiveUniformBlockiv(shader_id, uniformBlockIndex,
GL_UNIFORM_BLOCK_DATA_SIZE,
&amp;uniformBlockSize);
//uniformBlockSize is NOT same as sizeof_uniformBlock_colors0

//Create UBO.
glBindBuffer(GL_UNIFORM_BUFFER, uniformBlock_id);
glBufferData(GL_UNIFORM_BUFFER, uniformBlockSize, NULL, GL_DYNAMIC_DRAW);
}

void CheckShader(GLuint id, GLuint type, GLint *ret, const char *onfail)
{
//Check if something is wrong with the shader
switch(type) {
case(GL_COMPILE_STATUS):
glGetShaderiv(id, type, ret);
if(*ret == false){
int infologLength = 0;
glGetShaderiv(id, GL_INFO_LOG_LENGTH, &amp;infologLength);
GLchar buffer[infologLength];
GLsizei charsWritten = 0;
std::cout << onfail << std::endl;
glGetShaderInfoLog(id, infologLength, &amp;charsWritten, buffer);
std::cout << buffer << std::endl;
}
break;
case(GL_LINK_STATUS):
glGetProgramiv(id, type, ret);
if(*ret == false){
int infologLength = 0;
glGetProgramiv(id, GL_INFO_LOG_LENGTH, &amp;infologLength);
GLchar buffer[infologLength];
GLsizei charsWritten = 0;
std::cout << onfail << std::endl;
glGetProgramInfoLog(id, infologLength, &amp;charsWritten, buffer);
std::cout << buffer << std::endl;
}
default:
break;
};
}

void Draw() // Here's Where We Do All The Drawing
{
unsigned long long before = rdtsc();

glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );

// just before drawing update the normal matrix
glm_NormalMatrix = transpose(inverse(glm_ModelViewMatrix));

//set uniform block colors0
glBindBuffer(GL_UNIFORM_BUFFER, uniformBlock_colors0_id);
//We can use BufferData to upload our data to the shader,
//since we know it's in the std140 layout
glBufferData(GL_UNIFORM_BUFFER, sizeof_uniformBlock_colors0, uniformBlock_colors0, GL_DYNAMIC_DRAW);
//With a non-standard layout, we'd use BufferSubData for each uniform.
// glBufferSubData(GL_UNIFORM_BUFFER, offset, singleSize, &amp;uniformBlock_colors0[8]);

#ifndef XGLUNIFORMMATRIX4FV
//set uniform block matrix1
glBindBuffer(GL_UNIFORM_BUFFER, uniformBlock_matrix1_id);
//We can use BufferData to upload our data to the shader,
//since we know it's in the std140 layout
glBufferData(GL_UNIFORM_BUFFER, sizeof_uniformBlock_matrix1, uniformBlock_matrix1, GL_DYNAMIC_DRAW);
//With a non-standard layout, we'd use BufferSubData for each uniform.
// glBufferSubData(GL_UNIFORM_BUFFER, offset, singleSize, &amp;uniformBlock_colors0[8]);
#else
//glm_ModelViewMatrix *= translate( vec3(0.0, 0.0, 0.0) );
//glm_ModelViewMatrix *= rotate(angle, vec3(0.0, 1.0, 0.0) );
//glm_ModelViewMatrix *= scale( vec3(0.5, 0.5, 0.5) );
//XglUniformMatrix4fv(glm_ProjectionMatrix_id, 1, false, glm_ProjectionMatrix );
XglUniformMatrix4fv(glm_ModelViewMatrix_id, 1, false, glm_ModelViewMatrix ); // set the rotation/translation/scale matrix
XglUniformMatrix4fv(glm_NormalMatrix_id, 1, false, transpose(inverse(glm_ModelViewMatrix)) ); // set the rotation/translation/scale matrix
#endif
glBindVertexArray(vao_id[0]); // select the vertex array object:vao_id[0] by definiton using vertices0,normals0,colors0
glDrawArrays(GL_TRIANGLES, 0, vao_elementcount[0]); // draw the array (at the speed of light)

#ifdef XGLUNIFORMMATRIX4FV
//glm_ModelViewMatrix *= translate( vec3(0.0, 0.0, 0.0) );
//glm_ModelViewMatrix *= rotate(angle, vec3(0.0, 1.0, 0.0) );
//glm_ModelViewMatrix *= scale( vec3(0.5, 0.5, 0.5) );
//XglUniformMatrix4fv(glm_ProjectionMatrix_id, 1, false, glm_ProjectionMatrix );
XglUniformMatrix4fv(glm_ModelViewMatrix_id, 1, false, glm_ModelViewMatrix ); // set the rotation/translation/scale matrix
XglUniformMatrix4fv(glm_NormalMatrix_id, 1, false, transpose(inverse(glm_ModelViewMatrix)) ); // set the rotation/translation/scale matrix
#endif
glBindVertexArray(vao_id[1]); // select the vertex array object:vao_id[1] by definition using vertices1,normals1,colors1
glDrawArrays(GL_TRIANGLES , 0, vao_elementcount[1]); // draw the array (at the speed of light)
unsigned long long after = rdtsc();
std::cout << after-before << std::endl;
}

void CameraRotate(GLfloat angle)
{
vec4 eye_pos(0.0, 0.0, 10.0, 1.0); // start camera at z=10
eye_pos = rotate(angle, vec3(0.0, 1.0, 0.0))*eye_pos; // rotate camera around y-axis
glm_ModelViewMatrix = mat4(1.0); //glLoadIdentity
glm_ModelViewMatrix *= lookAt( vec3(eye_pos),
vec3(0.0, 0.0, 0.0),
vec3(0.0, 1.0, 0.0)
);
}

void reshape(GLfloat w, GLfloat h)
{
glViewport(0,0,w,h); // Reset The Current Viewport

glm_ProjectionMatrix = mat4(1.0); //glLoadIdentity
//glm_ProjectionMatrix *= ortho3D(0.f, 2.f, 0.f, 1.f, 0.f, 1.f); // identical to glOrtho(0.f, 2.f, 0.f, 1.f, 0.f, 1.f);
//glm_ProjectionMatrix *= frustum(1.f, 2.f, 1.f, 2.f, 1.0e-45f, 2.f); // identical glFrustum(1.f, 2.f, 1.f, 2.f, 1.f, 2.f) but problem with 0.f, 2.f, 0.f, 1.f, 0.f, 1.f) -- solution near and far must both be > 0 (error if equal to zero!);
glm_ProjectionMatrix *= perspective(20.0f,w/h,1.0f,21.0f);

#ifdef XGLUNIFORMMATRIX4FV
XglUniformMatrix4fv(glm_ProjectionMatrix_id, 1, false, glm_ProjectionMatrix );
#endif
}
};

// GLOBAL ///////////////////////////////////////////////////////////
CustomGraphicsPipeline Scene;
/////////////////////////////////////////////////////////////////////
unsigned long long int rdtsc(void)
{
// http://www.cs.wm.edu/~kearns/001lab.d/rdtsc.html
unsigned long long int x;
unsigned a, d;
__asm__ volatile("rdtsc" : "=a" (a), "=d" (d));
return ((unsigned long long)a) | (((unsigned long long)d) << 32);
}

double gettime_ms()
{
const double ticks_per_ms = 3214818125.0/1000.; // hardwired from main.cpp
return rdtsc()/ticks_per_ms;
}

size_t gFramesPerSecond = 1;
const int desiredFPS = 50;
size_t gPhysicsPerSecond = 1;
const int desiredPPS=desiredFPS*2;

class MeasureCallsPerSecond
{
size_t Frames; // frames averaged over 1000mS
double PreviousClock; // [milliSeconds]
double Clock; // [milliSeconds]
double NextClock; // [milliSeconds]
size_t CallsPerSecond;

public:
MeasureCallsPerSecond():
Frames(0),
PreviousClock(gettime_ms()),
Clock(gettime_ms()),
NextClock(gettime_ms()),
CallsPerSecond(1)
{};
~MeasureCallsPerSecond(){};

size_t measure() {
++Frames;
Clock = gettime_ms(); //has limited resolution, so average over 1000mS
if ( Clock < NextClock ) return CallsPerSecond;

CallsPerSecond = Frames*10; // store the averaged number of frames per second

PreviousClock = Clock;
NextClock = Clock+100; // 1000mS=1S in the future
Frames=0;

return CallsPerSecond;
};
};

class condition_block
{
double desiredCPS;
double PreviousClock;
double Clock;
double deltaT;

public:
condition_block(double _desiredCPS):
desiredCPS(_desiredCPS),
PreviousClock(gettime_ms()),
Clock(gettime_ms()),
deltaT(0.)
{};
~condition_block(){};

bool regulate()
{
bool status = false;
#ifdef REGULATE_FPS
Clock = gettime_ms();
deltaT=Clock-PreviousClock;
if (deltaT < 1000./desiredCPS) {status=true;} else {status=false; PreviousClock=Clock;}
#endif
return status;
};
};


//#define USE_IDLE
#ifdef USE_IDLE
void idle()
{
static condition_block condition_block_pps(desiredPPS);
if (condition_block_pps.regulate()) return;
#else
static void timer(int value)
{
glutTimerFunc(30,timer,++value); // come back in 30msec
#endif

static MeasureCallsPerSecond PPS;
gPhysicsPerSecond = PPS.measure(); //only call once per frame loop to measure FPS
double dt = 1./gPhysicsPerSecond; // delta time since last call

//put your specific Physics code here
//... this code will run at desiredPPS
static GLfloat Angle=0.0;
Angle+= 360./8.*dt; // 360 degrees rotation in 8 seconds
Scene.CameraRotate(Angle);
//end your specific Physics code here

//update the uniform block colors0
DiffuseCool = 0.45*cos(radians(Angle));
DiffuseWarm = 0.45*sin(radians(Angle));
SurfaceColor = vec3(0.75,0.75,0.75);
WarmColor = vec3(0.0,0.0,1.0);
CoolColor = vec3(0.0,1.0,0.0);

static condition_block condition_block_fps(desiredFPS);
if (condition_block_fps.regulate()) return;

glutPostRedisplay();
}

static void display(void)
{
static MeasureCallsPerSecond FPS;
gFramesPerSecond = FPS.measure(); //only call once per frame loop to measure FPS
// printf("FPS %d\n",gFramesPerSecond);
// printf("PPS %d\n",gPhysicsPerSecond);
fflush(stdout);

Scene.Draw();
glutSwapBuffers();
}

static void reshape(int width, int height)// Resize And Initialize The GL Window
{
Scene.reshape(width,height);
}

static void key(unsigned char k, int x, int y)
{
switch (k) {
case 27: // Escape
exit(0);
break;
default:
return;
}
}

int main( int argc, char *argv[] )
{
glutInit( &amp;argc, argv );
glutInitContextVersion( 3, 0 ); // freeglut is so cool! get openGL 3.0 context
//glutInitContextFlags( int flags )
glutInitDisplayMode (GLUT_DOUBLE | GLUT_RGB);
#ifdef FULLSCREEN
glutGameModeString("1920x1200:32");
assert( glutGameModeGet(GLUT_GAME_MODE_POSSIBLE) ); // bail if false
glutEnterGameMode();
#else
glutInitWindowSize (480, 480);
glutCreateWindow ("hello world from openGL 3.0/freeglut/glew/glm");
#endif

glewInit();

assert( Scene.Init() ); // bail if scene initialization fails

//#define USE_IDLE
#ifdef USE_IDLE
glutIdleFunc(idle);
#else
glutTimerFunc(0,timer,0);
#endif
glutDisplayFunc(display);
glutReshapeFunc(reshape);
glutKeyboardFunc(key);

glutMainLoop();

return 0;
}


if you really want to compile, you can download the large object


wget http://24.130.61.216/temporary/blender_droid_noidtluom.vertices

wget http://24.130.61.216/temporary/blender_droid_noidtluom.normals

wget http://24.130.61.216/temporary/blender_droid_noidtluom.credit



Original 3d Blender model (droid.blend) from http://e2-productions.com/repository/modules/PDdownloads/singlefile.php?cid=20&amp;lid=7
and converted by TooL-v0.2.5 (with slight modification)