Related
My scene: (the video is blurry because I had to convert this to a GIF)
There are two other objects that should be rendered here!
I am writing a program with GLFW/OpenGL. Essentially what I am trying to do is to be able to render a bunch of independent objects, who all can move freely around. To do this, I create a shader, a VAO, a VBO, and a EBO for each model that I want to render. static_models is a vector of class Model, and class Model is just a way to organize my vertices, indices, colors, and normals.
First is creating the vector of Models: (I know this class works as it should, because I use the exact same class for different shaders and buffer objects and things render well)
std::vector<Model> static_models; // scale // color
Model plane("models/plane.ply", { 1.0f, 1.0f, 1.0f }, { 1.0f, 1.0f, 1.0f });
Model tetrahedron("models/tetrahedron.ply", { 1.0f, 1.0f, 1.0f }, { 0.2f, 1.0f, 1.0f });
static_models.emplace_back(plane);
static_models.emplace_back(tetrahedron);
The code for generating the shader objects, VAOS, VBOS, and EBOS:
for (int i = 0; i < static_models.size(); i++)
{
Shader tempShader("plane.vert", "plane.frag");
// create a shader program for each model (in case we need to rotate them or transform them in some way they will be independent)
static_model_shaders.emplace_back(tempShader);
VAOS_static.emplace_back();
VAOS_static.back().Bind();
VBO tempVBO(&static_models.at(i).vertices.front(), static_models.at(i).vertices.size() * sizeof(GLfloat));
EBO tempEBO(&static_models.at(i).indices.front(), static_models.at(i).indices.size() * sizeof(GLuint));
VAOS_static.back().LinkAttrib(tempVBO, 0, 3, GL_FLOAT, 11 * sizeof(float), (void*)0);
VAOS_static.back().LinkAttrib(tempVBO, 1, 3, GL_FLOAT, 11 * sizeof(float), (void*)(3 * sizeof(float)));
VAOS_static.back().LinkAttrib(tempVBO, 2, 2, GL_FLOAT, 11 * sizeof(float), (void*)(6 * sizeof(float)));
VAOS_static.back().LinkAttrib(tempVBO, 3, 3, GL_FLOAT, 11 * sizeof(float), (void*)(8 * sizeof(float)));
VAOS_static.back().Unbind();
tempVBO.Unbind();
tempEBO.Unbind();
}
Then the code to create the positions and mat4 matrixes for each model:
// static model vectors for position and matrix
std::vector<glm::vec3> staticModelPositions;
std::vector<glm::mat4> staticModels;
// initialize all static_model object positions
for (int i = 0; i < static_models.size(); i++)
{
staticModelPositions.emplace_back();
staticModelPositions.back() = glm::vec3(0.0f, 1.0f, 0.0f);
staticModels.emplace_back();
staticModels.back() = glm::translate(staticModels.back(), staticModelPositions.back());
}
Then I set some initial values for the uniforms:
std::vector<Texture> textures;
//static objects
for (int i = 0; i < static_models.size(); i++)
{
//activate first before setting uniforms
static_model_shaders.at(i).Activate();
// static model load model, then load lightColor, then load lightPos for each static_model
glUniformMatrix4fv(glGetUniformLocation(static_model_shaders.at(i).ID, "model"), 1, GL_FALSE, glm::value_ptr(staticModels.at(i)));
glUniform4f(glGetUniformLocation(static_model_shaders.at(i).ID, "lightColor"), lightColor.x, lightColor.y, lightColor.z, 1.0f);
glUniform3f(glGetUniformLocation(static_model_shaders.at(i).ID, "lightPos"), lightPos.x, lightPos.y, lightPos.z);
//create texture objects
textures.emplace_back(Texture("brick.png", GL_TEXTURE_2D, GL_TEXTURE0, GL_RGBA, GL_UNSIGNED_BYTE));
textures.back().texUnit(static_model_shaders.at(i), "tex0", 0);
}
Then drawing the models in the game loop: (game loop not shown this is a big program)
//draw all static models (each with a different shader and matrix)
for (int i = 0; i < static_model_shaders.size(); i++)
{
//activate shader for current model
// Tells OpenGL which Shader Program we want to use
static_model_shaders.at(i).Activate();
// Exports the camera Position to the Fragment Shader for specular lighting
glUniform3f(glGetUniformLocation(static_model_shaders.at(i).ID, "camPos"), camera.Position.x, camera.Position.y, camera.Position.z);
glUniformMatrix4fv(glGetUniformLocation(static_model_shaders.at(i).ID, "model"), 1, GL_FALSE, glm::value_ptr(staticModels.at(i)));
glUniform4f(glGetUniformLocation(static_model_shaders.at(i).ID, "lightColor"), lightColor.x, lightColor.y, lightColor.z, 1.0f);
// Export the camMatrix to the Vertex Shader of the pyramid
camera.Matrix(static_model_shaders.at(i), "camMatrix");
// Binds texture so that is appears in rendering
textures.at(i).Bind();
VAOS_static.at(i).Bind();
glDrawElements(GL_TRIANGLES, static_models.at(i).indices.size(), GL_UNSIGNED_INT, 0);
VAOS_static.at(i).Unbind();
}
My vertex shader:
#version 330 core
// Positions/Coordinates
layout (location = 0) in vec3 aPos;
// Colors
layout (location = 1) in vec3 aColor;
// Texture Coordinates
layout (location = 2) in vec2 aTex;
// Normals (not necessarily normalized)
layout (location = 3) in vec3 aNormal;
// Outputs the color for the Fragment Shader
out vec3 color;
// Outputs the texture coordinates to the Fragment Shader
out vec2 texCoord;
// Outputs the normal for the Fragment Shader
out vec3 Normal;
// Outputs the current position for the Fragment Shader
out vec3 crntPos;
// Imports the camera matrix from the main function
uniform mat4 camMatrix;
// Imports the model matrix from the main function
uniform mat4 model;
void main()
{
// calculates current position
crntPos = vec3(model * vec4(aPos, 1.0f));
// Outputs the positions/coordinates of all vertices
gl_Position = camMatrix * vec4(crntPos, 1.0);
// Assigns the colors from the Vertex Data to "color"
color = aColor;
// Assigns the texture coordinates from the Vertex Data to "texCoord"
texCoord = aTex;
// Assigns the normal from the Vertex Data to "Normal"
Normal = aNormal;
}
And fragment shader:
#version 330 core
// Outputs colors in RGBA
out vec4 FragColor;
// Imports the color from the Vertex Shader
in vec3 color;
// Imports the texture coordinates from the Vertex Shader
in vec2 texCoord;
// Imports the normal from the Vertex Shader
in vec3 Normal;
// Imports the current position from the Vertex Shader
in vec3 crntPos;
// Gets the Texture Unit from the main function
uniform sampler2D tex0;
// Gets the color of the light from the main function
uniform vec4 lightColor;
// Gets the position of the light from the main function
uniform vec3 lightPos;
// Gets the position of the camera from the main function
uniform vec3 camPos;
void main()
{
// ambient lighting
float ambient = 0.40f;
// diffuse lighting
vec3 normal = normalize(Normal);
vec3 lightDirection = normalize(lightPos - crntPos);
float diffuse = max(dot(normal, lightDirection), 0.0f);
// specular lighting
float specularLight = 0.50f;
vec3 viewDirection = normalize(camPos - crntPos);
vec3 reflectionDirection = reflect(-lightDirection, normal);
float specAmount = pow(max(dot(viewDirection, reflectionDirection), 0.0f), 8);
float specular = specAmount * specularLight;
// outputs final color
FragColor = texture(tex0, texCoord) * lightColor * (diffuse + ambient + specular);
}
I have other objects in the scene, and they render and update well. There are no errors in the code and everything runs fine, the objects in static_models are just not being rendered. Anyone have any ideas as to why it wouldn't be showing anything?
I fixed this after a very long time spent. The issue was this block of code:
// static model vectors for position and matrix
std::vector<glm::vec3> staticModelPositions;
std::vector<glm::mat4> staticModels;
// initialize all static_model object positions
for (int i = 0; i < static_models.size(); i++)
{
staticModelPositions.emplace_back();
staticModelPositions.back() = glm::vec3(0.0f, 1.0f, 0.0f);
staticModels.emplace_back();
staticModels.back() = glm::translate(staticModels.back(), staticModelPositions.back());
}
There is a line missing here. After doing staticModels.emplace_back(); we must create the identity matrix for the model. This code allows the program to function as intended:
// static model vectors for position and matrix
std::vector<glm::vec3> staticModelPositions;
std::vector<glm::mat4> staticModels;
// initialize all static_model object positions
for (int i = 0; i < static_models.size(); i++)
{
staticModelPositions.emplace_back();
staticModelPositions.back() = glm::vec3(0.0f, 1.0f, 0.0f);
staticModels.emplace_back();
staticModels.at(i) = glm::mat4(1.0f);
staticModels.back() = glm::translate(staticModels.back(), staticModelPositions.back());
}
I'm trying to pass color attributes to each point of a 3d model imported by assimp via the vertex shader.
Attempted solutions:
Pass in via layout (location = 0) in vec3 color;
My laptop has Intel HD 4000 graphics which do not support #extension ARB_explicit_attrib_location : require. I am running GLSL 140 which requires the #extension for layout to work.
Have a separate vector of color values whose indices match those of the points and pass in as another attribute.
Did not work. **Edit (what didn't work): ** the shader never received the color passed by main.cpp.
My code
part of main.cpp
GLuint program_id = LoadShaders("pointcloud.vert", "pointcloud.frag");
ObjLoader *obj_loader = new ObjLoader();
int result = 0;
if (argc > 1) result = obj_loader->loadAsset(argv[1]);
else{ result = obj_loader->loadAsset("obj_samples/Heart.stl"); rotateY = 0.75f; rotateX = 0.5f;}
if (result){glfwTerminate(); exit(EXIT_FAILURE);}
glEnable(GL_DEPTH_TEST);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
GLuint matrix_id = glGetUniformLocation(program_id, "MVP");//get the location for our "MVP" uniform variable
GLfloat *g_vertex_buffer_data = (GLfloat*)malloc(obj_loader->getNumVertices()*sizeof(GLfloat));//use a large buffer to store the entire scene
obj_loader->loadVertices(g_vertex_buffer_data);//load the scene into the vertex buffer
GLint attribute_vertex = glGetAttribLocation(program_id, "vertex_pos");//get the location for the attribute variables
GLuint vertex_array_id;//generate the Vertex Array Object (Depedency: GLEW)
GLuint vertex_buffer[2];//initialize the vertex buffer memory (similar to malloc)
GLuint color_buffer;
glGenVertexArrays(1, &vertex_array_id); //create the VAO
glBindVertexArray(vertex_array_id); // bind the VAO
glGenBuffers(1, vertex_buffer); //create vertex buffer
glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer[0]); //bind the buffer
glBufferData(GL_ARRAY_BUFFER, obj_loader->getNumVertices()*sizeof(GLfloat), g_vertex_buffer_data, GL_STATIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(vec3d), 0);
glEnableVertexAttribArray(attribute_vertex);//enable
glUseProgram(program_id);//use our shader
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glPointSize(3.0f);
LoadShaders():
GLuint LoadShaders(const char * vertex_file_path,const char * fragment_file_path){//load the shader programs and return the id
GLuint vertex_shader_id = glCreateShader(GL_VERTEX_SHADER);//create the vertex and fragment shaders
GLuint fragment_shader_id = glCreateShader(GL_FRAGMENT_SHADER);
std::string vertex_shader_code = readSourceFile(vertex_file_path);//read the shader and fragment programs into string
if(vertex_shader_code == "")return 0; //failed because empty string
printf("Compiling Vertex shader : %s\n", vertex_file_path);
CompileShader(vertex_shader_code, vertex_shader_id);
std::string fragment_shader_code = readSourceFile(fragment_file_path);//load the fragment program (optional)
if(fragment_shader_code == "")return 0; //failed: empty string
printf("Compiling Fragment shader : %s\n", fragment_file_path);//compile the fragment shader
CompileShader(fragment_shader_code, fragment_shader_id);
GLint result = GL_FALSE;
int infolog_length;
printf("Linking program\n");//link the program
GLuint program_id = glCreateProgram();
glAttachShader(program_id, vertex_shader_id);
glAttachShader(program_id, fragment_shader_id);
glLinkProgram(program_id);
glGetProgramiv(program_id, GL_LINK_STATUS, &result);//check the program and ensure that the program is linked properly
glGetProgramiv(program_id, GL_INFO_LOG_LENGTH, &infolog_length);
if ( infolog_length > 0 ){
std::vector<char> program_error_msg(infolog_length+1);
glGetProgramInfoLog(program_id, infolog_length, NULL, &program_error_msg[0]);
printf("%s\n", &program_error_msg[0]);
}else{
printf("Linked Successfully\n");
}
glDeleteShader(vertex_shader_id);
glDeleteShader(fragment_shader_id);
return program_id;
}
pointcloud.vert:
#version 140
uniform mat4 MVP;
out vec4 colorV;
in vec3 vertex_pos;
void main(){
gl_Position = MVP * vec4(vertex_pos, 1.0f);// Output position of the vertex, in clip space : MVP * position
colorV = vec4(1.0f, 0.0f, 0.0f, 1.0f);
}
I have a std::vector of rgb values I'd like to pass to the shader - any recommendations to achieve this seemingly simple behavior?
Judging from your comment I assume that I misunderstood the problem.
If you want to pass vertex-colors from assimp into the shader, you need to define another vertex attribute. To do this, you need to create another buffer after the vertex buffer:
GLfloat* colorData = (GLfloat*)malloc(sizeof(GLfloat)*obj_loader->getNumVertices()*3);
obj_loader->loadColors(colorData);//or however you named it
Gluint colorBuffer;
glGenBuffers(1, &colorBuffer);
glBindBuffer(GL_ARRAY_BUFFER, colorBuffer);
glBufferData(GL_ARRAY_BUFFER, sizeof(Glfloat)*obj_loader->getNumVertices()*3, colorData, GL_STATIC_DRAW);
glBindAttribLocation(program_id, 0, "vertex_pos");
glBindAttribLocation(program_id, 1, "colorData");
glEnableVertexAttribArray(0);
glEnableVertexAttribArray(1);
//... continue with your code
Vertex Shader:
#version 140
in vec3 vertex_pos;
in vec3 colorData;
out vec4 colorV;
uniform mat4 MVP;
void main()
{
//...
}
I'm creating a set of classes to read in 3d objects from COLLADA files. I started with some basic code to read in the positions and normals and plot them with opengl. I added code to scale the vertices successfully and added all the code I need to read in the color or texture connected with each graphics element in the COLLAD file. But now I need to add the code to draw the vertices with color. I have created the buffer object array to house the color array for each of the vertices array and buffer objects.
This is the code I have to build the arrays from data I obtain from the COLLADA file:
Keep in mind I am still creating this it's not perfect.
// Set vertex coordinate data
glBindBuffer(GL_ARRAY_BUFFER, vbosPosition[i]);
glBufferData(GL_ARRAY_BUFFER, col->vectorGeometry[i].map["POSITION"].size,
scaledData, GL_STATIC_DRAW);
free(scaledData);
loc = glGetAttribLocation(program, "in_coords");//get a GLuint for the attribute and put it into GLuint loc.
glVertexAttribPointer(loc, col->vectorGeometry[i].map["POSITION"].stride, col->vectorGeometry[i].map["POSITION"].type, GL_FALSE, 0, 0);//glVertexAttribPointer — loc specifies the index of the generic vertex attribute to be modified.
glEnableVertexAttribArray(0);
#ifdef Testing_Mesh3D
PrintGLVertex(vbosPosition[i], col->vectorGeometry[i].map["POSITION"].size / 4);
#endif // Set normal vector data
glBindBuffer(GL_ARRAY_BUFFER, vbosNormal[i]);
glBufferData(GL_ARRAY_BUFFER, col->vectorGeometry[i].map["NORMAL"].size, col->vectorGeometry[i].map["NORMAL"].data, GL_STATIC_DRAW);
loc = glGetAttribLocation(program, "in_normals");
glVertexAttribPointer(loc, col->vectorGeometry[i].map["NORMAL"].stride, col->vectorGeometry[i].map["NORMAL"].type, GL_FALSE, 0, 0);
glEnableVertexAttribArray(1);
glBindBuffer(GL_ARRAY_BUFFER, vbosColor[i]);
Material* material = col->mapGeometryUrlToMaterial2Effect[col->vectorGeometry[i].id];
if (material->effect1.size() > 0)
{
Effect effect1 = material->effect1[0];
if (effect1.type == enumEffectTypes::color)
{
Color color = effect1.color;
glBufferData(GL_ARRAY_BUFFER, color.length, color.values, GL_STATIC_DRAW);
loc = glGetAttribLocation(program, "in_colors");
glVertexAttribPointer(loc, color.length, color.type, GL_FALSE, 0, 0);
}
else
{
}
}
}
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);
}
// Initialize uniform data
void Mesh3D::InitializeUniforms(GLuint program) {
GLuint program_index, ubo_index;
struct LightParameters params;
// Specify the rotation matrix
glm::vec4 diff_color = glm::vec4(0.3f, 0.3f, 1.0f, 1.0f);
GLint location = glGetUniformLocation(program, "diffuse_color");
glUniform4fv(location, 1, &(diff_color[0]));
// Initialize UBO data
params.diffuse_intensity = glm::vec4(0.5f, 0.5f, 0.5f, 1.0f);
params.ambient_intensity = glm::vec4(0.3f, 0.3f, 0.3f, 1.0f);
params.light_direction = glm::vec4(-1.0f, -1.0f, 0.25f, 1.0f);
// Set the uniform buffer object
glUseProgram(program);
glGenBuffers(1, &ubo);
glBindBuffer(GL_UNIFORM_BUFFER, ubo);
glBufferData(GL_UNIFORM_BUFFER, 3 * sizeof(glm::vec4), ¶ms, GL_STREAM_DRAW);
glBindBuffer(GL_UNIFORM_BUFFER, 0);
glUseProgram(program);
// Match the UBO to the uniform block
glUseProgram(program);
ubo_index = 0;
program_index = glGetUniformBlockIndex(program, "LightParameters");
glUniformBlockBinding(program, program_index, ubo_index);
glBindBufferRange(GL_UNIFORM_BUFFER, ubo_index, ubo, 0, 3 * sizeof(glm::vec4));
glUseProgram(program);
This is a hearder file containing the two string literals I housing the strings used to build the vertex and fragment shader. Again I am new to this and not sure how I need to modify the shader to include colored vertices, I have started by adding an input vec4 for the four float colour ( includes alpha). Any help?
#pragma once
#ifndef Included_shaders
#define Included_shaders
#include<stdio.h>
#include<iostream>
static std::string shaderVert = "#version 330\n"
"in vec3 in_coords;\n"
"in vec3 in_normals;\n"
"in vec4 in_colors; \n"//added by me
"out vec3 vertex_normal;\n"
"void main(void) {\n"
"vertex_normal = in_normals;\n"
"gl_Position = vec4(in_coords, 1.0);\n"
"}\n";
static std::string shaderFrag = "#version 330\n"
"in vec3 vertex_normal;\n"
"out vec4 output_color;\n"
"layout(std140) uniform LightParameters{\n"
"vec4 diffuse_intensity;\n"
"vec4 ambient_intensity;\n"
"vec4 light_direction;\n"
"};\n"
"uniform vec4 diffuse_color;\n"
"void main() {\n"
"/* Compute cosine of angle of incidence */\n"
"float cos_incidence = dot(vertex_normal, light_direction.xyz);\n"
"cos_incidence = clamp(cos_incidence, 0, 1);\n"
"/* Compute Blinn term */\n"
"vec3 view_direction = vec3(0, 0, 1);\n"
"vec3 half_angle = normalize(light_direction.xyz + view_direction);\n"
"float blinn_term = dot(vertex_normal, half_angle);\n"
"blinn_term = clamp(blinn_term, 0, 1);\n"
"blinn_term = pow(blinn_term, 1.0);\n"
"/* Set specular color and compute final color */\n"
"vec4 specular_color = vec4(0.25, 0.25, 0.25, 1.0);\n"
"output_color = ambient_intensity * diffuse_color +\n"
"diffuse_intensity * diffuse_color * cos_incidence +\n"
"diffuse_intensity * specular_color * blinn_term;\n"
"}\n";
#endif
Finally this is the funciton I am modifying to draw the colored elements
void Mesh3D::DrawToParent()
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// Draw elements of each mesh in the vector
for (int i = 0; i<nVectorGeometry; i++)
{
glBindVertexArray(vaos[i]);
glDrawElements(col->vectorGeometry[i].primitive/*This is 4 for GL_Triangles*/, col->vectorGeometry[i].index_count,
GL_UNSIGNED_SHORT, col->vectorGeometry[i].indices);
}
glBindVertexArray(0);
glutSwapBuffers();
}
am getting a little confused about the glVertexAttribPointer and glGetAttribLocation though I think I get the basic idea. Am I using this right.
Am I setting up the buffer object for colors correctly. Am I correct I have a color for each vertex in this buffer, right now I have only placed the single color that applies to all associated buffers in this array and probably need to change that?
How exactly do I go about drawing the colored vertices when I call glDrawElements?
Don't just refer me to the resources for opengl a lot of the wordy explanations make little sense to me.
Make your vertex shader output color and make the fragment shader take it as input. The color will be interpolated between vertices.
Yes, it seems you have understood glAttribPointer correctly.
DrawElements takes the indices of the vertices you want to draw. The last argument should not contain the indices. Instead, it should probably be null. The indices should be specified with glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ...).
If the color buffer is correctly bound you don't need to do anything special id drawElements for the colors. The shaders will get all the enabled attribarrays.
I can help you better if you run the code and you tell me what problems you get. It would also help if the code was easier to read. If you split it into functions under ten lines in length you may spot some errors yourself and possibly remove some duplication.
My use case is a bunch a textured quads that I want to draw. I'm trying to use the same indexed array of a quad to draw it a bunch of times and use the gl_InstanceID and gl_VertexID in GLSL to retrieve texture and position info from a Texture Buffer.
The way I understand a Texture Buffer is that I create it and my actual buffer, link them, and then whatever I put in the actual buffer magically appears in my texture buffer?
So I have my vertex data and index data:
struct Vertex
{
GLfloat position[4];
GLfloat uv[2];
};
Vertex m_vertices[4] =
{
{{-1,1,0,1},{0,1}},
{{1,1,0,1},{1,1}},
{{-1,-1,0,1},{0,0}},
{{1,-1,0,1},{1,0}}
};
GLuint m_indices[6] = {0,2,1,1,2,3};
Then I create my VAO, VBO and IBO for the quads:
glGenBuffers(1,&m_vertexBuffer);
glBindBuffer(GL_ARRAY_BUFFER,m_vertexBuffer);
glBufferData(GL_ARRAY_BUFFER,sizeof(Vertex)*4,&m_vertices,GL_STATIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER,0);
glGenVertexArrays(1,&m_vao);
glBindVertexArray(m_vao);
glBindBuffer(GL_ARRAY_BUFFER,m_vertexBuffer);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0,4,GL_FLOAT, GL_FALSE, sizeof(struct Vertex),(const GLvoid*)offsetof(struct Vertex, position));
glEnableVertexAttribArray(1);
glVertexAttribPointer(0,2,GL_FLOAT, GL_FALSE, sizeof(struct Vertex),(const GLvoid*)offsetof(struct Vertex, uv));
glBindVertexArray(0);
glBindBuffer(GL_ARRAY_BUFFER,0);
glBindVertexArray(m_vao);
glGenBuffers(1, &m_ibo);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_ibo);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(GLuint)*6,&m_indices,GL_STATIC_DRAW);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,0);
glBindVertexArray(0);
I'm pretty sure that I've done the above correctly. My quads have 4 vertices, with six indexes to draw triangles.
Next I create my buffer and texture for the the Texture Buffer:
glGenBuffers(1,&m_xywhuvBuffer);
glBindBuffer(GL_TEXTURE_BUFFER, m_xywhuvBuffer);
glBufferData(GL_TEXTURE_BUFFER, sizeof(GLfloat)*8*100, nullptr, GL_DYNAMIC_DRAW); // 8 floats
glGenTextures(1,&m_xywhuvTexture);
glBindTexture(GL_TEXTURE_BUFFER, m_xywhuvTexture);
glTexBuffer(GL_TEXTURE_BUFFER, GL_RG32F, m_xywhuvBuffer); // they're in pairs of 2, in r,g of each texel.
glBindBuffer(GL_TEXTURE_BUFFER,0);
So, the idea is that every four texels belongs to one quad, or gl_InstanceID.
When I'm drawing my quads, they execute the below:
glActiveTexture(GL_TEXTURE0);
glBindBuffer(GL_TEXTURE_BUFFER, m_xywhuvBuffer);
std::vector<GLfloat> xywhuz =
{
-1.0f + position.x / screenDimensions.x * 2.0f,
1.0f - position.y / screenDimensions.y * 2.0f,
dimensions.x / screenDimensions.x,
dimensions.y / screenDimensions.y,
m_region.x,
m_region.y,
m_region.w,
m_region.h
};
glBufferSubData(GL_TEXTURE_BUFFER, sizeof(GLfloat)*8*m_rectsDrawnThisFrame, sizeof(GLfloat)*8, xywhuz.data());
m_rectsDrawnThisFrame++;
So I increase m_rectsDrawThisFrame for each quad. You'll notice that the data I'm passing is 8 GLfloats, so each of the 4 texels that belong to each gl_InstanceID is the x,y position, the width and height, and then the same details for the real texture that I'm going to texture my quads with.
Finally once all of my rects have updated their section of the GL_TEXTURE_BUFFER I run this:
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D,texture); // this is my actual texture that the quads take a section from to texture themselves.
glUniform1i(m_program->GetUniformLocation("tex"),1);
glUniform4f(m_program->GetUniformLocation("color"),1,0,1,1);
glBindVertexArray(m_vao);
glDrawElementsInstanced(GL_TRIANGLES,4,GL_UNSIGNED_INT,0,m_rectsDrawnThisFrame);
m_rectsDrawnThisFrame = 0;
I reset the draw count. I also noticed that I had to activate the texture in the second slot. Does the Texture Buffer Object use up one?
Finally my Vert shader
#version 410
layout (location = 0) in vec4 in_Position;
layout (location = 1) in vec2 in_UV;
out vec2 ex_texcoord;
uniform samplerBuffer buf;
void main(void)
{
vec2 position = texelFetch(buf,gl_InstanceID*4).xy;
vec2 dimensions = texelFetch(buf,gl_InstanceID*4+1).xy;
vec2 uvXY = texelFetch(buf,gl_InstanceID*4+2).xy;
vec2 uvWH = texelFetch(buf,gl_InstanceID*4+3).xy;
if(gl_VertexID == 0)
{
gl_Position = vec4(position.xy,0,1);
ex_texcoord = uvXY;
}
else if(gl_VertexID == 1)
{
gl_Position = vec4(position.x + dimensions.x, position.y,0,1);
ex_texcoord = vec2(uvXY.x + uvWH.x, uvXY.y);
}
else if(gl_VertexID == 2)
{
gl_Position = vec4(position.x, position.y + dimensions.y, 0,1);
ex_texcoord = vec2(uvXY.x, uvXY.y + uvWH.y);
}
else if(gl_VertexID == 3)
{
gl_Position = vec4(position.x + dimensions.x, position.y + dimensions.y, 0,1);
ex_texcoord = vec2(uvXY.x + uvWH.x, uvXY.y + uvWH.y );
}
}
And my Frag shader
#version 410
in vec2 ex_texcoord;
uniform sampler2D tex;
uniform vec4 color = vec4(1,1,1,1);
layout (location = 0) out vec4 FragColor;
void main()
{
FragColor = texture(tex,ex_texcoord) * color;
}
Now the problem, after I'm getting no errors reported in GLIntercept, is that I'm getting nothing drawn on the screen.
Any help?
There is one subtle issue in your code that would certainly stop it from working. At the end of the VAO/VBO setup code, you have this:
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,0);
glBindVertexArray(0);
The GL_ELEMENT_ARRAY_BUFFER binding is part of the VAO state. If you unbind it while the VAO is bound, this VAO will not have an element array buffer binding. Which means that you don't have indices when you draw later.
You should simply remove this call:
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,0);
Also, since you have 6 indices, the second argument to the draw call should be 6:
glDrawElementsInstanced(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0, m_rectsDrawnThisFrame);
Apart from that, it all looks reasonable to me. But there's quite a lot of code, so I can't guarantee that I would have spotted all problems.
I also noticed that I had to activate the texture in the second slot. Does the Texture Buffer Object use up one?
Yes. The buffer texture needs to be bound, and the value of the sampler variable set to the corresponding texture unit. Since you bind the buffer texture during setup, never unbind it, and the default value of the sampler variable is 0, you're probably fine there. But I think it would be cleaner to set it up more explicitly. Where you prepare for drawing:
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_BUFFER, m_xywhuvTexture);
glUniform1i(m_program->GetUniformLocation("buf"), 0);
There are a couple of questions like this, but I still haven't really understood. I was coding with OpenGL over 10 years ago and noticed how difficult it is to get into modern OpenGL. The OpenGL.org page is a horrible mess when it comes to examples, you never know what version it is, any version seems to be mixed up in various code examples.
Alright, I have an old code I want to update to OpenGL >3 at least. So first thing I did was to move on from glVertex3fv to finally make it with glVertexAttribPointer (over a step with glVertexPointer until I read this is deprecated now as well). This works out fine, but when trying to place textures I got stuck quickly and I assume it is because of wrong positioning and I wanted to get rid of c++ code :
glMatrixMode( GL_PROJECTION );
glLoadIdentity();
glFrustum( -RProjZ, +RProjZ, -Aspect*RProjZ, +Aspect*RProjZ, 1.0, 32768.0 );
and to draw it
// bind vertex buffer
glBindBuffer(GL_ARRAY_BUFFER, VertBuffer);
glBufferData(GL_ARRAY_BUFFER, sizeof(float) * size, verts, GL_STATIC_DRAW);
// enable arrays
glEnableVertexAttribArray(0);
// set pointers
glVertexAttribPointer(0,3,GL_FLOAT, GL_FALSE, sizeof(float) * floatsPerVertex, 0);
// render ComplexSurface
glDrawArrays(GL_TRIANGLE_FAN, 0, size);
glDisableVertexAttribArray(0);
with in the vertexshader
gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
And everything is working magically. Now don't get me wrong, I'm a big fan of magic, but...
Then I found a couple of matrix conversions which can be used to get a matrix to replace glFrustum, but whenever I try to replace it, it fails badly (although I think I understood the maths behind glFrustum and the conversion into the matrix).
What tried is something like
buildPerspProjMat(g_ProjView,FovAngle,Aspect,1.0,32768.0 );
glUseProgram(g_program);
glUniformMatrix4fv(g_programFrustum, 1, GL_FALSE, g_ProjView );
glUseProgram(0);
and using the position in the shader from the buffer above with the projection matix, but this doesn't work out at all.
So what I plain don't get now is where to replace this and with what in the shader. I don't know at which point the glMatrixMode takes place and "when" to replace it with some uniform matrix (passing the args as uniform ain't the problem here).
I can't count how many tutorials I read already, but I always get confused over all the mixed versions. I am always happy about some code examples, but please OpenGL 3 or higher.
The next would be a replacement for glTexCoord2f for texturing, but that's a different story :)
I find that when thinking about modern OpenGL it is best to forget that glMatrixMode ever existed.
With that in mind, let's go over what you need for the most basic draw operation: a replacement for gl_ModelViewProjectionMatrix. As it's name implies this is a combination of 3 different matrices: the model matrix, the view matrix, and the projection matrix.
So what you'll need in your shader to accomodate this is 3 uniform variables of type mat4. Which you'll use like so:
uniform mat4 projMat;
uniform mat4 viewMat;
uniform mat4 modelMat;
layout (location = 0) in vec3 position;
void main()
{
gl_Position = projMat * viewMat * modelMat * vec4(position, 1.0);
}
This bit of shader code performs the same function as the one you had above. What changed is the built-in gl_ModelViewProjectionMatrix was replaced by 3 uniform variables (which could be combined as one if you make sure to multiply them yourself on the C++ side before passing it in). And the builtin gl_Vertex was replaced by an input variable.
On the C++ side you will need to do 2 things. First you'll need to get the location for each of these uniforms:
GLuint modelMatIdx = glGetUniformLocation(shaderProgId, "modelMat");
GLuint viewMatIdx = glGetUniformLocation(shaderProgId, "viewMat");
GLuint projMatIdx = glGetUniformLocation(shaderProgId, "projMat");
And with this in hand you can now pass in the values for each uniform right before drawing using glUniformMatrix4fv.
One particular library which makes this particularly easy is glm. For example to get the same projection matrix as in your example you would do:
glm::mat4 projMat = glm::frustum(-RProjZ, +RProjZ, -Aspect*RProjZ, +Aspect*RProjZ, 1.0, 32768.0);
and you would pass it in like so:
glUniformMatrix4fv(projMatIdx, 1, GL_FALSE, glm::value_ptr(projMat));
Now that you know how, I'd like to address the issue of "when". You said you weren't clear about the matrix mode stuff and that brings me back to my earlier assertion of "forget about it". The matrix mode was there so that you could tell opengl which built in should be affected by calls to OpenGL matrix operations such as glTranslate, glFrustum and so on, but all of this is gone now. You are now in charge of managing the (possibly many) matrices involved. All you have to do is pass them in before you draw (as I've shown above) and you'll be fine. Just make sure the program is bound before you attempt to modify its uniforms.
Here's a working example (if you're suprised by gl::... instead of gl... it's because I'm using an opengl header generated by glLoadGen which puts all of the opengl API functions in the gl namespace).
GLuint simpleProgramID;
// load the shader and make the program
GLuint modelMatIdx = gl::GetUniformLocation(simpleProgramID, "modelMat");
GLuint viewMatIdx = gl::GetUniformLocation(simpleProgramID, "viewMat");
GLuint projMatIdx = gl::GetUniformLocation(simpleProgramID, "projMat");
GLuint vaoID;
gl::GenVertexArrays(1, &vaoID);
gl::BindVertexArray(vaoID);
GLuint vertBufferID, indexBufferID;
gl::GenBuffers(1, &vertBufferID);
gl::GenBuffers(1, &indexBufferID);
struct Vec2 { float x, y; };
struct Vec3 { float x, y, z; };
struct Vert { Vec3 pos; Vec2 tex; };
std::array<Vert, 8> cubeVerts = {{
{ { 0.5f, 0.5f, 0.5f }, { 1.0f, 0.0f } }, { { 0.5f, 0.5f, -0.5f }, { 1.0f, 1.0f } },
{ { 0.5f, -0.5f, -0.5f }, { 0.0f, 1.0f } }, { { 0.5f, -0.5f, 0.5f }, { 0.0f, 0.0f } },
{ { -0.5f, 0.5f, 0.5f }, { 0.0f, 0.0f } }, { { -0.5f, 0.5f, -0.5f }, { 0.0f, 1.0f } },
{ { -0.5f, -0.5f, -0.5f }, { 1.0f, 1.0f } }, { { -0.5f, -0.5f, 0.5f }, { 1.0f, 0.0f } }
}};
std::array<unsigned int, 36> cubeIdxs = {{
0, 2, 1, 0, 3, 2, // Right
4, 5, 6, 4, 6, 7, // Left
0, 7, 3, 0, 4, 7, // Top
1, 6, 2, 1, 5, 6, // Bottom
0, 5, 1, 0, 4, 5, // Front
3, 7, 6, 3, 6, 2 // Back
}};
// Vertex buffer
gl::BindBuffer(gl::ARRAY_BUFFER, vertBufferID);
gl::BufferData(gl::ARRAY_BUFFER, sizeof(Vert) * cubeVerts.size(), cubeVerts.data(), gl::STATIC_DRAW);
gl::EnableVertexAttribArray(0); // Matches layout (location = 0)
gl::VertexAttribPointer(0, 3, gl::FLOAT, gl::FALSE_, sizeof(Vert), 0);
gl::EnableVertexAttribArray(1); // Matches layout (location = 1)
gl::VertexAttribPointer(1, 2, gl::FLOAT, gl::FALSE_, sizeof(Vert), (GLvoid*)sizeof(Vec3));
// Index buffer
gl::BindBuffer(gl::ELEMENT_ARRAY_BUFFER, indexBufferID);
gl::BufferData(gl::ELEMENT_ARRAY_BUFFER, sizeof(unsigned int) * cubeIdxs.size(), cubeIdxs.data(), gl::STATIC_DRAW);
gl::BindVertexArray(0);
glm::mat4 projMat = glm::perspective(56.25f, 16.0f/9.0f, 0.1f, 100.0f);
glm::mat4 viewMat = glm::lookAt(glm::vec3(5, 5, 5), glm::vec3(0, 0, 0), glm::vec3(0, 0, 1));
glm::mat4 modelMat; // identity
while (!glfwWindowShouldClose(window))
{
gl::Clear(gl::COLOR_BUFFER_BIT | gl::DEPTH_BUFFER_BIT);
gl::UseProgram(simpleProgramID);
gl::UniformMatrix4fv(projMatIdx, 1, gl::FALSE_, glm::value_ptr(projMat));
gl::UniformMatrix4fv(viewMatIdx, 1, gl::FALSE_, glm::value_ptr(viewMat));
gl::UniformMatrix4fv(modelMatIdx, 1, gl::FALSE_, glm::value_ptr(modelMat));
gl::BindVertexArray(vaoID);
gl::DrawElements(gl::TRIANGLES, 36, gl::UNSIGNED_INT, 0);
gl::BindVertexArray(0);
gl::UseProgram(0);
glfwSwapBuffers(window);
glfwPollEvents();
}
Associated Vertex Shader:
//[VERTEX SHADER]
#version 430
uniform mat4 projMat;
uniform mat4 viewMat;
uniform mat4 modelMat;
layout (location = 0) in vec3 in_position; // matches gl::EnableVertexAttribArray(0);
layout (location = 1) in vec2 in_uv; // matches gl::EnableVertexAttribArray(1);
out vec2 uv;
void main()
{
gl_Position = projMat * viewMat * modelMat * vec4(in_position, 1.0);
uv = in_uv;
}
And finally Fragment shader:
//[FRAGMENT SHADER]
#version 430
in vec2 uv;
out vec4 color;
void main()
{
color = vec4(uv, 0.0, 1.0);
}
The resulting image is:
Well, I agree that most OpenGL tutorials confuse bits of deprecated and non-deprecated stuff. To get you in the right direction, let me explain.
gl_ModelViewProjectionMatrix, gl_ModeView, glMatrixMode() and the matrix stack glPushMatrix() glPopMatrix() are deprecated. You need to define your own matrices as a uniform variables then set and pass them to the shader using glUniform*.
gl_Vertex is also deprecated, actually the whole fixed attributes names are deprecated. Alternatively you need to define your own attribute names and bind them to specific locations. Then you can set their values using glVertexAttribPointer by passing the attribute location to it (Full explanation here). For example:
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, vertices); // for vertices
glVertexAttribPointer(1, 4, GL_FLOAT, GL_FALSE, 0, color); // for color
And for the shader code
layout (location = 0) in vec4 vertex;
layout (location = 1) in vec4 color;
uniform mat4 modelview;
uniform mat4 projection;
void main()
{
gl_Position = projection* modelview* vertex;
}
For the attributes locations you can set them in the shader code as I did, or from OpenGL API using glBindAttribLocation.
Managing uniform variables can be somehow tricky if you are used to the old OpenGL globals variables such as gl_ModelView I wrote an article that hopefully can help you manage uniform variables for a big project.