I'm attempting to render a triangle using VBOs in OpenGL via C++.
First, I identify my variables:
CBuint _vao;
CBuint _vbo;
CBuint _ebo;
struct Vertex
{
CBfloat position[3];
};
Then, I set the positions for each vertex to form a geometrical triangle:
Vertex v[3];
v[0].position[0] = 0.5f;
v[0].position[1] = 0.5f;
v[0].position[2] = 0.0f;
v[1].position[0] = 0.5f;
v[1].position[1] = -0.5f;
v[1].position[2] = 0.0f;
v[2].position[0] = -0.5f;
v[2].position[1] = -0.5f;
v[2].position[2] = 0.0f;
Simple enough, right?
Then, I declare my indices for the EBO/IBO:
unsigned short i[] =
{
0, 1, 2
};
Now that I have all the attribute data needed for buffering, I bind the VAO as well as the VBOs:
// Generate vertex elements
glGenVertexArrays(1, &_vao);
glGenBuffers(1, &_vbo);
glGenBuffers(1, &_ebo);
// VAO
glBindVertexArray(_vao);
// VBO
glBindBuffer(GL_ARRAY_BUFFER, _vbo);
glBufferData(GL_ARRAY_BUFFER, sizeof(Vertex) * 9, &v, GL_STATIC_DRAW);
// EBO
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _ebo);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(unsigned short) * 3, &i, GL_STATIC_DRAW);
// Location 0 - Positions
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), BUFFER_OFFSET(0));
glBindVertexArray(0);
Next, I render them:
glBindVertexArray(_vao);
glDrawElements(GL_TRIANGLES, 3, GL_UNSIGNED_SHORT, 0);
glBindVertexArray(0);
I then use the vertex shader:
#version 330 core
// Vertex attributes
layout(location = 0) in vec3 position;
// Node parses
out vec3 Fragpos;
// Uniforms
uniform mat4 model;
uniform mat4 projection;
uniform mat4 view;
// Vertex loop
void main()
{
gl_Position = projection * view * model * vec4(position, 1.0f);
Fragpos = vec3(model * vec4(position, 1.0f));
}
To simply calculate the model position as well as the camera view. The fragment shader is again, quite simplistic:
#version 330 core
// Node parses
out vec4 color;
in vec3 Fragpos;
// Camera data
uniform vec3 view_loc;
// Global uniforms
uniform struct GLOBAL
{
float ambient_coefficient;
vec3 ambient_colour;
} _global;
// Main loop
void main(void)
{
color = vec4(1.0, 1.0, 1.0, 1.0);
}
The uniforms work perfectly fine as I've been using this shader code for previous projects. So what could the problem be? The triangle simply does not render. I can't think of anything that's causing this, any ideas?
Edit: Just to narrow things down, I also use these variables to handle the model matrix that are being parsed to and from the vertex shader:
CBuint _u_model;
mat4 _model;
vec3 _position;
vec4 _rotation;
vec3 _scale;
Then inside the constructor, I initialize the variables like so:
_model = mat4::identity();
_position = vec3(0.0f, 0.0f, 0.0f);
_rotation = vec4(0.0f, 1.0f, 0.0f, 0.0f);
_scale = vec3(1.0f, 1.0f, 1.0f);
_u_model = glGetUniformLocation(shader->_program, "model");
And finally, I update the model matrix using this formula:
_model = translate(_position) *
rotate(_rotation.data[0], _rotation.data[1], _rotation.data[2], _rotation.data[3]) *
scale(_scale);
Edit 2: This is the camera class I use for the MVP:
class Camera : public object
{
private:
CBbool _director;
CBfloat _fov;
CBfloat _near;
CBfloat _far;
CBfloat _speed;
Math::vec3 _front;
Math::vec3 _up;
Math::mat4 _projection;
Math::mat4 _view;
CBuint _u_projection;
CBuint _u_view;
public:
Camera(Shader* shader, Math::vec3 pos, float fov, float n, float f, bool dir) : _speed(5.0f)
{
_model = Math::mat4::identity();
_projection = Math::mat4::identity();
_view = Math::mat4::identity();
_position = pos;
_fov = fov;
_near = n;
_far = f;
_director = dir;
_front = vec3(0.0f, 0.0f, -1.0f);
_up = vec3(0.0f, 1.0f, 0.0f);
_u_projection = glGetUniformLocation(shader->_program, "projection");
_u_view = glGetUniformLocation(shader->_program, "view");
_u_model = glGetUniformLocation(shader->_program, "view_loc");
}
~Camera() {}
inline CBbool isDirector() { return _director; }
inline void forward(double delta) { _position.data[2] -= _speed * (float)delta; }
inline void back(double delta) { _position.data[2] += _speed * (float)delta; }
inline void left(double delta) { _position.data[0] -= _speed * (float)delta; }
inline void right(double delta) { _position.data[0] += _speed * (float)delta; }
inline void up(double delta) { _position.data[1] += _speed * (float)delta; }
inline void down(double delta) { _position.data[1] -= _speed * (float)delta; }
virtual void update(double delta)
{
_view = Math::lookat(_position, _position + _front, _up);
_projection = Math::perspective(_fov, 900.0f / 600.0f, _near, _far);
}
virtual void render()
{
glUniformMatrix4fv(_u_view, 1, GL_FALSE, _view);
glUniformMatrix4fv(_u_projection, 1, GL_FALSE, _projection);
glUniform3f(_u_model, _position.data[0], _position.data[1], _position.data[2]);
}
};
As Amadeus mentioned, I simply had to use gl_Position = vec4(position, 1.0f); for it to render. No idea why, but now's the time to find out! Thanks for your time.
Related
I'm trying to show the normals of some vertices of the mesh when I select them but accordingly to the position of the cam these normals are displayed or not and the big problem is that they are displayed when they shouldn't (i.e. when i select vertices of the tibia but i'm looking at the calf so they should be hidden by the leg) and not displayed when they should be displayed.
I could be wrong but it's like some geometry of the model is drawn above the line. Moreover, if before calling the draw calls i disable the depth test it works (even if the problem of being showed when they shouldn't isn't resolved obviously). That's some images to give you an idea of what I mean.
WITH DEPTH TEST OFF:
DEPT TEST ON:
That's the code:
void StatusManager::Render() {
glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
if (!animatedModel)
return;
Update();
// draw wireframe if enabled
if (wireframeEnabled)
DrawWireframe();
if (visualMode == Mode_Texture) {
// draw model
if (lightingMode == Mode_Flat)
DrawModel(modelFlatShader);
else if (lightingMode == Mode_Smooth)
DrawModel(modelSmoothShader);
else
DrawModel(modelNoLightShader);
}
else if (visualMode == Mode_CurrentBoneIDInfluence) {
DrawModel(currentBoneShader);
}
else if (visualMode == Mode_NumBones)
{
DrawModel(numBonesShader);
}
else
{
DrawModel(modelGreyShader);
}
// render selected vertices
DrawSelectedVertices();
if (!info.hitPoint)
return;
//DrawHotPoint();
if (selectionMode == Mode_Vertex)
DrawHoveredPoint();
if (selectionMode == Mode_Edge)
DrawHoveredLine();
if (selectionMode == Mode_Face)
DrawHoveredFace();
}
void StatusManager::DrawWireframe() {
wireframeShader.use();
glLineWidth(1.0f);
// model/view/projection transformations
glm::mat4 modelView = camera.viewMatrix;
wireframeShader.setMat4("modelView", modelView);
wireframeShader.setMat4("projection", projection);
// pass bones matrices to the shader
auto transforms = animator.GetFinalBoneMatrices();
for (int i = 0; i < transforms.size(); ++i)
wireframeShader.setMat4("finalBonesMatrices[" + std::to_string(i) + "]", transforms[i]);
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
animatedModel.value().Draw(wireframeShader);
}
void StatusManager::DrawModel(Shader& modelShader) {
modelShader.use();
// model/view/projection transformations
modelShader.setMat4("modelView", camera.viewMatrix);
modelShader.setMat4("projection", projection);
if (visualMode == Mode_Texture)
modelShader.setVec3("light_pos", lightPos);
else if (visualMode == Mode_CurrentBoneIDInfluence)
modelShader.setInt("currentBoneID", currentBoneID);
// pass bones matrices to the shader
auto transforms = animator.GetFinalBoneMatrices();
for (int i = 0; i < transforms.size(); ++i)
modelShader.setMat4("finalBonesMatrices[" + std::to_string(i) + "]", transforms[i]);
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
glEnable(GL_POLYGON_OFFSET_FILL);
glPolygonOffset(1.0, 1.0);
/*if (pause)
bakedModel.value().Draw(modelShader);
else*/
animatedModel.value().Draw(modelShader);
}
void StatusManager::DrawHoveredFace() {
assert(bakedModel.has_value());
assert(info.hitPoint.has_value());
Mesh& m = bakedModel.value().meshes[info.meshIndex];
Face& f = info.face.value();
//vertex
float hoveredVertices[9] = {
m.vertices[f.indices[0]].Position.x, m.vertices[f.indices[0]].Position.y, m.vertices[f.indices[0]].Position.z,
m.vertices[f.indices[1]].Position.x, m.vertices[f.indices[1]].Position.y, m.vertices[f.indices[1]].Position.z,
m.vertices[f.indices[2]].Position.x, m.vertices[f.indices[2]].Position.y, m.vertices[f.indices[2]].Position.z,
};
hoverShader.use();
glBindVertexArray(HVAO);
glBindBuffer(GL_ARRAY_BUFFER, HVBO);
glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(hoveredVertices), &hoveredVertices);
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
glEnable(GL_POLYGON_OFFSET_FILL);
glPolygonOffset(0.0, 0.0);
// model/view/projection transformations
glm::mat4 modelView = camera.viewMatrix;
hoverShader.setMat4("modelView", modelView);
hoverShader.setMat4("projection", projection);
glDrawArrays(GL_TRIANGLES, 0, 3);
//unbind
glBindVertexArray(0);
}
void StatusManager::DrawHoveredPoint() {
assert(bakedModel.has_value());
assert(info.hitPoint.has_value());
Mesh& m = bakedModel.value().meshes[info.meshIndex];
Face& f = info.face.value();
int index = getClosestVertexIndex(info.hitPoint.value(), m, f);
//vertex
float hoveredVertices[3] = { m.vertices[index].Position.x, m.vertices[index].Position.y, m.vertices[index].Position.z };
hoverShader.use();
glBindVertexArray(HVAO);
glBindBuffer(GL_ARRAY_BUFFER, HVBO);
glBufferSubData(GL_ARRAY_BUFFER, 0, 3 * sizeof(float), &hoveredVertices);
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
glEnable(GL_POLYGON_OFFSET_FILL);
glPolygonOffset(0.0, 0.0);
// model/view/projection transformations
glm::mat4 modelView = camera.viewMatrix;
hoverShader.setMat4("modelView", modelView);
hoverShader.setMat4("projection", projection);
glPointSize(8.0f);
glDrawArrays(GL_POINTS, 0, 1);
//unbind
glBindVertexArray(0);
}
void StatusManager::DrawHotPoint()
{
float hotVertices[3] = { hotPoint.x, hotPoint.y, hotPoint.z };
hoverShader.use();
glBindVertexArray(HVAO);
glBindBuffer(GL_ARRAY_BUFFER, HVBO);
glBufferSubData(GL_ARRAY_BUFFER, 0, 3 * sizeof(float), &hotVertices);
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
glEnable(GL_POLYGON_OFFSET_FILL);
glPolygonOffset(0.0, 0.0);
// model/view/projection transformations
glm::mat4 modelView = camera.viewMatrix;
hoverShader.setMat4("modelView", modelView);
hoverShader.setMat4("projection", projection);
glPointSize(8.0f);
glDrawArrays(GL_POINTS, 0, 1);
//unbind
glBindVertexArray(0);
}
void StatusManager::UpdateSelectedVertices()
{
selectedVertices.clear();
for (Vertex* v : selectedVerticesPointers)
selectedVertices.push_back(*v);
}
void StatusManager::DrawHoveredLine() {
assert(bakedModel.has_value());
assert(info.hitPoint.has_value());
Mesh& m = bakedModel.value().meshes[info.meshIndex];
Face& f = info.face.value();
auto line = getClosestLineIndex(info.hitPoint.value(), m, f);
//vertex
float hoveredVertices[6] = {
m.vertices[line.v1].Position.x, m.vertices[line.v1].Position.y, m.vertices[line.v1].Position.z,
m.vertices[line.v2].Position.x, m.vertices[line.v2].Position.y, m.vertices[line.v2].Position.z
};
hoverShader.use();
glBindVertexArray(HVAO);
glBindBuffer(GL_ARRAY_BUFFER, HVBO);
glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(hoveredVertices), &hoveredVertices);
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
glEnable(GL_POLYGON_OFFSET_FILL);
glPolygonOffset(0.0, 0.0);
// model/view/projection transformations
glm::mat4 modelView = camera.viewMatrix;
hoverShader.setMat4("modelView", modelView);
hoverShader.setMat4("projection", projection);
glLineWidth(3.0f);
glDrawArrays(GL_LINES, 0, 2);
glLineWidth(1.0f);
//unbind
glBindVertexArray(0);
}
normalShader just for completeness
VERTEX SHADER:
#version 330 core
layout(location = 0) in vec3 pos;
layout(location = 1) in vec3 norm;
layout(location = 2) in ivec4 boneIds;
layout(location = 3) in vec4 weights;
layout(location = 4) in int numBones;
uniform mat4 projection;
uniform mat4 modelView;
uniform mat4 finalBonesMatrices[100];
out VS_OUT {
vec4 normal;
} vs_out;
void main()
{
mat4 cumulativeMatrix = mat4(1.0);
if (numBones>0)
cumulativeMatrix = mat4(0.0);
for(int i = 0 ; i < numBones ; i++)
{
cumulativeMatrix += (finalBonesMatrices[boneIds[i]] * weights[i]);
}
gl_Position = projection * modelView * cumulativeMatrix * vec4(pos, 1.0);
vs_out.normal = normalize(modelView * cumulativeMatrix * vec4(norm, 0.0));
}
GEOMETRY SHADER:
#version 330 core
layout (points) in;
layout (line_strip, max_vertices = 2) out;
uniform float normal_length;
in VS_OUT {
vec4 normal;
} gs_in[];
void main() {
gl_Position = gl_in[0].gl_Position;
EmitVertex();
gl_Position = gl_in[0].gl_Position + gs_in[0].normal * normal_length;
EmitVertex();
EndPrimitive();
}
FRAGMENT SHADER:
#version 330 core
out vec4 FragColor;
void main()
{
FragColor = vec4(1, 0, 0, 1);
}
If you need other piece of code tell me and I'll provide it, but I think I posted all the code and images you need.
Ok I don't know how I didn't noticed it, but yes, the problem was that I wasn't applying the projection matrix to the normal vector. Thanks Spektre to point this out.
I am using Assimp to load models to render in OpenGL but am running into an issue where chunks/pieces of a mesh don't render.
Example:
What model is supposed to look like:
What I end up rendering:
As you can see, some of the model is rendering properly, but not all.
I have verified multiple times that the meshes being loaded from assimp are loading the correct vertices and indices into my "Mesh" class. Here is my code for loading a model:
This function will recursively call itself for all child nodes and load each mesh inside the node. Each mesh will then be transformed into my own "Mesh" class by creating a vector of vertices and faces.
void Model::LoadAssimpNode(aiNode* node, const aiScene* scene)
{
// Process assimp meshes
for (unsigned int i = 0; i < node->mNumMeshes; i++)
{
aiMesh* mesh = scene->mMeshes[node->mMeshes[i]];
this->meshes.push_back(this->LoadAssimpMesh(mesh, scene));
}
// Recursivley processes child nodes
for (unsigned int i = 0; i < node->mNumChildren; i++)
{
this->LoadAssimpNode(node->mChildren[i], scene);
}
}
Mesh Model::LoadAssimpMesh(aiMesh* mesh, const aiScene* scene)
{
std::vector<sVertex> vertices;
for (unsigned int i = 0; i < mesh->mNumVertices; i++)
{
sVertex vertex;
vertex.x = mesh->mVertices[i].x;
vertex.y = mesh->mVertices[i].y;
vertex.z = mesh->mVertices[i].z;
vertex.nx = mesh->mNormals[i].x;
vertex.ny = mesh->mNormals[i].y;
vertex.nz = mesh->mNormals[i].z;
if (mesh->mTextureCoords[0])
{
vertex.u0 = mesh->mTextureCoords[0][i].x;
vertex.v0 = mesh->mTextureCoords[0][i].y;
}
vertices.push_back(vertex);
}
std::vector<sTriangle> faces;
for (unsigned int i = 0; i < mesh->mNumFaces; i++)
{
sTriangle face;
aiFace assimpFace = mesh->mFaces[i];
if (assimpFace.mNumIndices != 3)
{
std::cout << "Face is not a triangle!" << std::endl;
}
for (unsigned int j = 0; j < assimpFace.mNumIndices; j++)
{
face.vertIndex[j] = assimpFace.mIndices[j];
}
faces.push_back(face);
}
std::vector<Texture> textures;
if (mesh->mMaterialIndex >= 0)
{
aiMaterial* material = scene->mMaterials[mesh->mMaterialIndex];
// Sampler names should adhere to the following convention:
// Diffuse: texure_diffuseN
// Specular: texture_specularN
// Normal: texture_normalN
// Where N = texture numbers
for (Texture texture : this->LoadAssimpMaterialTextures(material, aiTextureType_DIFFUSE, "texture_diffuse"))
{
this->loadedTextures.insert(std::make_pair(texture.path.C_Str(), texture));
textures.push_back(texture);
}
for (Texture texture : this->LoadAssimpMaterialTextures(material, aiTextureType_SPECULAR, "texture_specular"))
{
this->loadedTextures.insert(std::make_pair(texture.path.C_Str(), texture));
textures.push_back(texture);
}
}
return Mesh(vertices, faces, textures);
}
The sVertex and sTriangle structs are defined as:
struct sVertex
{
float x, y, z;
float nx, ny, nz;
float u0, v0;
};
struct sTriangle
{
unsigned int vertIndex[3];
};
Now that the model is effectively loaded from assimp, we now call the SetupMesh() function which sets up the meshes' respective VAO, VBO and EBO:
void Mesh::SetupMesh()
{
// Generate IDs for our VAO, VBO and EBO
glGenVertexArrays(1, &this->VAO);
glGenBuffers(1, &this->VBO);
glGenBuffers(1, &this->EBO);
glBindVertexArray(this->VAO);
// Now ANY state that is related to vertex or index buffer
// and vertex attribute layout, is stored in the 'state'
// of the VAO...
// Tell open GL where to look for for vertex data
glBindBuffer(GL_ARRAY_BUFFER, this->VBO);
glBufferData(GL_ARRAY_BUFFER, this->vertices.size() * sizeof(sVertex), &this->vertices[0], GL_STATIC_DRAW);
// Tell open GL where our index buffer begins (AKA: where to look for faces)
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, this->EBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, this->faces.size() * sizeof(sTriangle), &this->faces[0], GL_STATIC_DRAW);
// Set the vertex attributes for this shader
// Layout information can be found in the vertex shader, currently:
// 0 = position
// 1 = normals
// 2 = texture coordinates
glEnableVertexAttribArray(0); // position
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(sVertex), (void*) offsetof(sVertex, x));
glEnableVertexAttribArray(1); // normal
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(sVertex), (void*) offsetof(sVertex, nx));
glEnableVertexAttribArray(2); // textureCoordinates
glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, sizeof(sVertex), (void*)offsetof(sVertex, u0));
// Now that all the parts are set up, unbind buffers
glBindVertexArray(0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
glDisableVertexAttribArray(0);
glDisableVertexAttribArray(1);
glDisableVertexAttribArray(2);
}
Once this is all setup, I will now call the Draw method for each mesh to render in my render loop:
void Mesh::Draw(const CompiledShader& shader)
{
glm::mat4 matModel = glm::mat4(1.0f);
glm::mat4 matTranslate = glm::translate(glm::mat4(1.0f), this->positionXYZ); // Translation matrix
glm::mat4 rotateX = glm::rotate(glm::mat4(1.0f), this->orientationXYZ.x, glm::vec3(1.0f, 0.0f, 0.0f)); // X axis rotation
glm::mat4 rotateY = glm::rotate(glm::mat4(1.0f), this->orientationXYZ.y, glm::vec3(0.0f, 1.0f, 0.0f)); // Y axis rotation
glm::mat4 rotateZ = glm::rotate(glm::mat4(1.0f), this->orientationXYZ.z, glm::vec3(0.0f, 0.0f, 1.0f)); // Z axis rotation
glm::mat4 matScale = glm::scale(glm::mat4(1.0f), glm::vec3(this->scale, this->scale, this->scale)); // Scale the mesh
glm::mat4 matInvTransposeModel = glm::inverse(glm::transpose(matModel));
// Apply all the transformations to our matrix
matModel = matModel * matTranslate;
matModel = matModel * rotateZ;
matModel = matModel * rotateY;
matModel = matModel * rotateX;
matModel = matModel * matScale;
glUseProgram(shader.ID);
glUniformMatrix4fv(glGetUniformLocation(shader.ID, "matModel"), 1, GL_FALSE, glm::value_ptr(matModel)); // Tell shader the model matrix (AKA: Position orientation and scale)
glUniformMatrix4fv(glGetUniformLocation(shader.ID, "matModelInverseTranspose"), 1, GL_FALSE, glm::value_ptr(matInvTransposeModel));
// Draw the mesh
glBindVertexArray(this->VAO);
glDrawElements(GL_TRIANGLES, this->faces.size(), GL_UNSIGNED_INT, 0);
glBindVertexArray(0);
}
My shaders are very simple where the color of a pixel is equal to the vertex's normal:
Vertex Shader:
#version 420
layout (location = 0) in vec3 position;
layout (location = 1) in vec3 normal;
layout (location = 2) in vec2 textureCoordinates;
uniform mat4 matModel;
uniform mat4 matView;
uniform mat4 matProjection;
uniform mat4 matModelInverseTranspose; // For normal calculation
out vec4 fVertWorldLocation;
out vec4 fNormal;
out vec2 TextureCoordinates;
void main()
{
mat4 MVP = matProjection * matView * matModel;
gl_Position = MVP * vec4(position, 1.0f);
TextureCoordinates = textureCoordinates;
// The location of the vertex in "world" space (not screen space)
fVertWorldLocation = matModel * vec4(position, 1.0f);
// Calculate the normal based on any rotation we've applied.
// This inverse transpose removes scaling and tranlation (movement)
// from the matrix.
fNormal = matModelInverseTranspose * vec4(normal, 1.0f);
};
Fragment Shader:
#version 420
in vec2 TextureCoordinates;
in vec4 fNormal;
out vec4 Color;
uniform sampler2D texture_diffuse;
void main()
{
//Color = vec4(texture(texture_diffuse, TextureCoordinates));
//Color = vec4(TextureCoordinates, 1.0f, 1.0f);
Color = fNormal;
}
Sorry for the insane length of this post, but I feel that all of it was necessary to get my point across.
If anyone could point out what I am doing wrong here it would be greatly appreciated! I feel like I need an extra pair of eyes here because I have read my code over countless times and can't seem to come up with anything.
Made a stupid mistake, I was under the impression that the "count" argument in the glDrawElements() function wanted the number of faces NOT the number of indices.
The problem was fixed by changing my glDrawElements call from:
glDrawElements(GL_TRIANGLES, this->faces.size(), GL_UNSIGNED_INT, 0);
To this:
glDrawElements(GL_TRIANGLES, this->faces.size() * 3, GL_UNSIGNED_INT, 0);
For everyone unfamiliar, OpenGL instanced drawing is where many objects are drawn with one shader call, so glDrawArrays is only called once for a thousand objects on the screen instead of once for every object.
Now the question is: how do I implement instanced rendering in OpenGL 3 for objects which have constantly changing vertices? Creating an array or specifying a position on the vertex shader dedicated specifically to where the objects are won't work, as I'm dealing with a constantly changing vector of objects which shift coordinates in different velocities every frame.
The header for the object class I'm working with, and the vertex shader I have, are described below for reference.
//CLASS
class Laser {
public:
GLfloat x, y, xVelocity, yVelocity;
GLuint texture;
GLfloat angle;
GLfloat velocity;
GLfloat width, height;
GLfloat drawWidth = 16;
GLfloat drawHeight = 16;
GLfloat damage;
GLint actsToDissapear = -1;
GLint actsExisting = 0;
GLboolean expired = false;
GLboolean isRotated = false;
GLboolean variableColor = false;
glm::vec3 color;
std::string type = "Laser";
Laser(GLfloat damage, GLfloat width, GLfloat height, GLuint texture, GLfloat x, GLfloat y, GLfloat xVelocity, GLfloat yVelocity, GLfloat drawWidth, GLfloat drawHeight, GLfloat actsToDissapear, GLboolean isRotated, GLfloat angle, GLboolean variableColor, glm::vec3 color);
virtual void draw(SpriteRenderer* s);
virtual void move(Rachel* player);
};
//VERTEX SHADER
#version 330 core
layout (location = 0) in vec4 vertex;
uniform mat4 model;
uniform mat4 projection;
out vec2 TexCoords;
void main() {
TexCoords = vec2(vertex.z, vertex.w);
gl_Position = projection * model * vec4(vertex.xy, 0.0, 1.0);
}
The concept you look for is attribute divisor. See glVertexAttribDivisor.
In a few words: you change your model matrix from uniform to an instanced attribute that's read from a buffer. Each frame you update that buffer with the new positions of each instance. One thing to consider when implementing this is to use (vec3 offset, quat4 orientation) representation for the model matrix in order to reduce the number of consumed attributes by half. Also, depending on the exact problem you have at hand, you can update that buffer directly on the GPU with compute shaders.
Heres a code example of what I think you're looking for. I used instanced rendering for my particle system, it supports textures, colors and movement. Works both on android opengl es and windows opengl. This code requires some work to run, but it should be fairly easy to get going.
#include "ParticleSystem.h"
#include "Engine.h"
#include "Transform.h"
#include "Shader.h"
#include "Texture.h"
#include "Mesh.h"
#include "ShaderHandler.h"
ParticleSystem::ParticleSystem()
{
}
ParticleSystem::~ParticleSystem()
{
shader = nullptr;
texture = nullptr;
glDeleteVertexArrays(1, &vertexArrayObject);
}
void ParticleSystem::init(Engine * engine, float size, Texture * texture, float maxVelocity, bool gravity)
{
this->maxVelocity = maxVelocity;
this->gravity = gravity;
this->size = size;
vertex =
{
-size, -size, 0.0f,
-size, size, 0.0f,
size, size, 0.0f,
size, -size, 0.0f
};
indices =
{
1, 0, 2, 3
};
this->shader = engine->getShaderHandler()->loadShader("res/shaders/texturedInstancedShader");
this->texture = texture;
glGenVertexArrays(1, &this->vertexArrayObject);
glBindVertexArray(this->vertexArrayObject);
glGenBuffers(ParticleSystem::NUM_BUFFERS, this->vertexArrayBuffer);
glBindBuffer(GL_ARRAY_BUFFER, this->vertexArrayBuffer[this->VERTEX_VB]);
glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * this->vertex.size(), &this->vertex[0], GL_STATIC_DRAW); //send model to GPU
glBindBuffer(GL_ARRAY_BUFFER, this->vertexArrayBuffer[this->TEXTURE_VB]);
glBufferData(GL_ARRAY_BUFFER, sizeof(glm::vec2) * this->texCoords.size(), &this->texCoords[0], GL_STATIC_DRAW);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, this->vertexArrayBuffer[this->INDEX_VB]);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(unsigned int) * indices.size(), &this->indices[0], GL_STATIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, this->vertexArrayBuffer[this->POSITION_VB]);
glBufferData(GL_ARRAY_BUFFER, sizeof(glm::vec3) * this->positions.size(), NULL, GL_STREAM_DRAW); //NULL (empty) buffer
glBindBuffer(GL_ARRAY_BUFFER, this->vertexArrayBuffer[this->COLOR_VB]);
glBufferData(GL_ARRAY_BUFFER, sizeof(glm::vec4) * this->colors.size(), NULL, GL_STREAM_DRAW); //NULL (empty) buffer
glBindVertexArray(0);
}
void ParticleSystem::createPoint(float pps, float deltaTime, glm::vec3 position, float maxLife, glm::vec4 color, glm::vec3 velocity)
{
Particle particle;
float amountPerSecond = pps * deltaTime;
for (float i = 0; i < amountPerSecond; i++)
{
particle.life = (rand() % static_cast<int>(maxLife * 100)) / 100.f;
particle.velocity =
{
((rand() % 200 / 100.f) - 1.f) * velocity.x,
((rand() % 200 / 100.f) - 1.f) * velocity.y,
((rand() % 200 / 100.f) - 1.f) * velocity.z
};
particles.emplace_back(particle);
positions.emplace_back(position);
colors.emplace_back(color);
}
}
void ParticleSystem::draw(glm::mat4 view)
{
if (particles.size() > 0)
{
Transform transform;
this->shader->bind();
this->shader->loadTransform(transform, view);
this->shader->loadInt(U_TEXTURE0, 0);
this->texture->bind(0);
glBindVertexArray(vertexArrayObject);
glVertexAttribDivisor(0, 0);
glVertexAttribDivisor(1, 1);
glVertexAttribDivisor(2, 1);
glVertexAttribDivisor(3, 0);
glEnableVertexAttribArray(0);
glBindBuffer(GL_ARRAY_BUFFER, this->vertexArrayBuffer[this->VERTEX_VB]);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, (void*)0);
glEnableVertexAttribArray(1);
glBindBuffer(GL_ARRAY_BUFFER, this->vertexArrayBuffer[this->POSITION_VB]);
glBufferData(GL_ARRAY_BUFFER, sizeof(glm::vec3) * positions.size(), &positions[0], GL_STREAM_DRAW);
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 0, (void*)0);
glEnableVertexAttribArray(2);
glBindBuffer(GL_ARRAY_BUFFER, this->vertexArrayBuffer[this->COLOR_VB]);
glBufferData(GL_ARRAY_BUFFER, sizeof(glm::vec4) * colors.size(), &colors[0], GL_STREAM_DRAW);
glVertexAttribPointer(2, 4, GL_FLOAT, GL_FALSE, 0, (void*)0);
glEnableVertexAttribArray(3);
glBindBuffer(GL_ARRAY_BUFFER, this->vertexArrayBuffer[this->TEXTURE_VB]);
glVertexAttribPointer(3, 2, GL_FLOAT, GL_FALSE, 0, (void*)0);
glDrawElementsInstanced(GL_TRIANGLE_STRIP, indices.size(), GL_UNSIGNED_INT, 0, positions.size());
glDisableVertexAttribArray(0);
glDisableVertexAttribArray(1);
glDisableVertexAttribArray(2);
glBindVertexArray(0);
}
}
void ParticleSystem::update(float deltaTime)
{
for (std::size_t i = 0; i < particles.size(); i++)
{
particles[i].life -= (1.f * deltaTime); //decrease life with 1 per second
if (particles[i].life <= 0.f) //dead
{
particles.erase(particles.begin() + i);
colors.erase(colors.begin() + i);
positions.erase(positions.begin() + i);
continue;
}
if (this->gravity == true)
{
if (particles[i].velocity.y > -maxVelocity)
{
particles[i].velocity.y -= maxVelocity * deltaTime; //1 second to reach maximum velocity
}
else
{
particles[i].velocity.y = -maxVelocity;
}
}
positions[i] += (particles[i].velocity * deltaTime);
}
}
Heres the shader:
vertex shader:
#version 330 core
layout(location = 0) in vec3 vertex;
layout(location = 1) in vec3 positions;
layout(location = 2) in vec4 colors;
layout(location = 3) in vec2 texCoords;
out vec2 texCoord;
out vec4 color;
uniform mat4 transform;
void main()
{
color = colors;
texCoord = texCoords;
gl_Position = transform * vec4(vertex + positions, 1.0);
}
fragment shader:
#version 330 core
in vec4 color;
in vec2 texCoord;
out vec4 colors;
uniform sampler2D texture0;
void main()
{
vec4 texel = texture2D(texture0, texCoord);
if (texel.a <= 0.5)
{
discard;
}
colors = color * texel;
}
OpenGL glm calculations don't seem to work in my program. Nothing moves even when i use the glm translate function to translate the z axis with a variable every frame. Am i missing something?
main.cpp
#define GLEW_STATIC
#define NO_SDL_GLEXT
#include "glew.h"
#include <sdl.h>
#undef main
#include "SDL_opengl.h"
#include "timer.h"
#include <time.h>
#include <shader.h>
using namespace std;
#include <glm/gtc/matrix_projection.hpp>
#include <glm/gtc/matrix_transform.hpp>
using namespace glm;
unsigned int vaoID[1]; // Our Vertex Array Object
unsigned int vboID[1]; // Our Vertex Buffer Object
glm::mat4 projectionMatrix; // Store the projection matrix
glm::mat4 viewMatrix; // Store the view matrix
glm::mat4 modelMatrix; // Store the model matrix
Shader *shader; // Our GLSL shader
float ztransform(0);
bool exited(false);
SDL_Event event;
const int FRAMES_PER_SECOND = 60;
void createSquare(void) {
float* vertices = new float[18]; // Vertices for our square
vertices[0] = -0.5; vertices[1] = -0.5; vertices[2] = 0.0; // Bottom left corner
vertices[3] = -0.5; vertices[4] = 0.5; vertices[5] = 0.0; // Top left corner
vertices[6] = 0.5; vertices[7] = 0.5; vertices[8] = 0.0; // Top Right corner
vertices[9] = 0.5; vertices[10] = -0.5; vertices[11] = 0.0; // Bottom right corner
vertices[12] = -0.5; vertices[13] = -0.5; vertices[14] = 0.0; // Bottom left corner
vertices[15] = 0.5; vertices[16] = 0.5; vertices[17] = 0.0; // Top Right corner
glGenVertexArrays(1, &vaoID[0]); // Create our Vertex Array Object
glBindVertexArray(vaoID[0]); // Bind our Vertex Array Object so we can use it
glGenBuffers(1, vboID); // Generate our Vertex Buffer Object
glBindBuffer(GL_ARRAY_BUFFER, vboID[0]); // Bind our Vertex Buffer Object
glBufferData(GL_ARRAY_BUFFER, 18 * sizeof(GLfloat), vertices, GL_STATIC_DRAW); // Set the size and data of our VBO and set it to STATIC_DRAW
glVertexAttribPointer((GLuint)0, 3, GL_FLOAT, GL_FALSE, 0, 0); // Set up our vertex attributes pointer
glEnableVertexAttribArray(0); // Disable our Vertex Array Object
glBindVertexArray(0); // Disable our Vertex Buffer Object
delete [] vertices; // Delete our vertices from memory
}
void startGL()
{
SDL_Init(SDL_INIT_EVERYTHING);
SDL_SetVideoMode(800, 600, 32, SDL_OPENGL);
glewInit();
glClearColor(0.4f, 0.0f, 1.0f, 0.0f);
projectionMatrix = glm::perspective(60.0f, (float)800 / (float)600, 0.1f, 100.f); // Create our perspective projection matrix
shader = new Shader("shader.vert", "shader.frag"); // Create our shader by loading our vertex and fragment shader
createSquare();
}
void drawstuff()
{
glViewport(0, 0, 800, 600); // Set the viewport size to fill the window
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); // Clear required buffers
viewMatrix = glm::translate(glm::mat4(1.0f), glm::vec3(0.0f, 0.0f, ztransform)); // Create our view matrix which will translate us back 5 units
modelMatrix = glm::scale(glm::mat4(1.0f), glm::vec3(0.5f)); // Create our model matrix which will halve the size of our model
shader->bind(); // Bind our shader
int projectionMatrixLocation = glGetUniformLocation(shader->id(), "projectionMatrix"); // Get the location of our projection matrix in the shader
int viewMatrixLocation = glGetUniformLocation(shader->id(), "viewMatrix"); // Get the location of our view matrix in the shader
int modelMatrixLocation = glGetUniformLocation(shader->id(), "modelMatrix"); // Get the location of our model matrix in the shader
glUniformMatrix4fv(projectionMatrixLocation, 1, GL_FALSE, &projectionMatrix[0][0]); // Send our projection matrix to the shader
glUniformMatrix4fv(viewMatrixLocation, 1, GL_FALSE, &viewMatrix[0][0]); // Send our view matrix to the shader
glUniformMatrix4fv(modelMatrixLocation, 1, GL_FALSE, &modelMatrix[0][0]); // Send our model matrix to the shader
glBindVertexArray(vaoID[0]); // Bind our Vertex Array Object
glDrawArrays(GL_TRIANGLES, 0, 6); // Draw our square
glBindVertexArray(0); // Unbind our Vertex Array Object
shader->unbind(); // Unbind our shader
}
int main (int argc, char* args[])
{
Timer fps;
startGL();
while(exited == false)
{
while( SDL_PollEvent(&event) )
{
if( event.type == SDL_QUIT )
exited = true;
}
drawstuff();
ztransform+=.1
SDL_GL_SwapBuffers();
if( fps.get_ticks() < 1000 / FRAMES_PER_SECOND )
SDL_Delay( ( 1000 / FRAMES_PER_SECOND ) - fps.get_ticks() );
}
SDL_Quit();
return 0;
}
shader.frag
#version 150 core
in vec3 pass_Color;
out vec4 out_Color;
void main(void)
{
out_Color = vec4(pass_Color, 1.0);
}
shader.vert
#version 150 core
in vec3 in_Position;
in vec3 in_Color;
out vec3 pass_Color;
void main(void)
{
gl_Position = vec4(in_Position, 1.0);
pass_Color = in_Color;
}
You have to apply your transformation in your vertex shader.
you should define in your vertex shader
uniform mat4 projectionMatrix;
uniform mat4 viewMatrix;
uniform mat4 modelMatrix;
And then apply these transformations to your input position (note: i may have gotten the order wrong)
gl_Position = projectionMatrix * viewMatrix * modelMatrix * vec4(in_position, 1.0);
Generally though, you would multiply the 3 matrices together in your c++ program and pass in a modelViewProjection matrix.
I'm having trouble getting phong shading to look right. I'm pretty sure there's something wrong with either my OpenGL calls, or the way I'm loading my normals, but I guess it could be something else since 3D graphics and Assimp are both still very new to me. When trying to load .obj/.mtl files, the problems I'm seeing are:
The models seem to be lit too intensely (less phong-style and more completely washed out, too bright).
Faces that are lit seem to be lit equally all over (with the exception of a specular highlight showing only when the light source position is moved to be practically right on top of the model)
Because of problems 1 and 2, spheres look very wrong:
picture of sphere
And things with larger faces look (less-noticeably) wrong too:
picture of cube
I could be wrong, but to me this doesn't look like proper phong shading.
Here's the code that I think might be relevant (I can post more if necessary):
file: assimpRenderer.cpp
#include "assimpRenderer.hpp"
namespace def
{
assimpRenderer::assimpRenderer(std::string modelFilename, float modelScale)
{
initSFML();
initOpenGL();
if (assImport(modelFilename)) // if modelFile loaded successfully
{
initScene();
mainLoop(modelScale);
shutdownScene();
}
shutdownOpenGL();
shutdownSFML();
}
assimpRenderer::~assimpRenderer()
{
}
void assimpRenderer::initSFML()
{
windowWidth = 800;
windowHeight = 600;
settings.majorVersion = 3;
settings.minorVersion = 3;
app = NULL;
shader = NULL;
app = new sf::Window(sf::VideoMode(windowWidth,windowHeight,32), "OpenGL 3.x Window", sf::Style::Default, settings);
app->setFramerateLimit(240);
app->setActive();
return;
}
void assimpRenderer::shutdownSFML()
{
delete app;
return;
}
void assimpRenderer::initOpenGL()
{
GLenum err = glewInit();
if (GLEW_OK != err)
{
/* Problem: glewInit failed, something is seriously wrong. */
std::cerr << "Error: " << glewGetErrorString(err) << std::endl;
}
// check the OpenGL context version that's currently in use
int glVersion[2] = {-1, -1};
glGetIntegerv(GL_MAJOR_VERSION, &glVersion[0]); // get the OpenGL Major version
glGetIntegerv(GL_MINOR_VERSION, &glVersion[1]); // get the OpenGL Minor version
std::cout << "Using OpenGL Version: " << glVersion[0] << "." << glVersion[1] << std::endl;
return;
}
void assimpRenderer::shutdownOpenGL()
{
return;
}
void assimpRenderer::initScene()
{
// allocate heap space for VAOs, VBOs, and IBOs
vaoID = new GLuint[scene->mNumMeshes];
vboID = new GLuint[scene->mNumMeshes*2];
iboID = new GLuint[scene->mNumMeshes];
glClearColor(0.4f, 0.6f, 0.9f, 0.0f);
glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_LEQUAL);
glEnable(GL_CULL_FACE);
shader = new Shader("shader.vert", "shader.frag");
projectionMatrix = glm::perspective(60.0f, (float)windowWidth / (float)windowHeight, 0.1f, 100.0f);
rot = 0.0f;
rotSpeed = 50.0f;
faceIndex = 0;
colorArrayA = NULL;
colorArrayD = NULL;
colorArrayS = NULL;
normalArray = NULL;
genVAOs();
return;
}
void assimpRenderer::shutdownScene()
{
delete [] iboID;
delete [] vboID;
delete [] vaoID;
delete shader;
}
void assimpRenderer::renderScene(float modelScale)
{
sf::Time elapsedTime = clock.getElapsedTime();
clock.restart();
if (rot > 360.0f)
rot = 0.0f;
rot += rotSpeed * elapsedTime.asSeconds();
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
viewMatrix = glm::translate(glm::mat4(1.0f), glm::vec3(0.0f, -3.0f, -10.0f)); // move back a bit
modelMatrix = glm::scale(glm::mat4(1.0f), glm::vec3(modelScale)); // scale model
modelMatrix = glm::rotate(modelMatrix, rot, glm::vec3(0, 1, 0));
//modelMatrix = glm::rotate(modelMatrix, 25.0f, glm::vec3(0, 1, 0));
glm::vec3 lightPosition( 0.0f, -100.0f, 0.0f );
float lightPositionArray[3];
lightPositionArray[0] = lightPosition[0];
lightPositionArray[1] = lightPosition[1];
lightPositionArray[2] = lightPosition[2];
shader->bind();
int projectionMatrixLocation = glGetUniformLocation(shader->id(), "projectionMatrix");
int viewMatrixLocation = glGetUniformLocation(shader->id(), "viewMatrix");
int modelMatrixLocation = glGetUniformLocation(shader->id(), "modelMatrix");
int ambientLocation = glGetUniformLocation(shader->id(), "ambientColor");
int diffuseLocation = glGetUniformLocation(shader->id(), "diffuseColor");
int specularLocation = glGetUniformLocation(shader->id(), "specularColor");
int lightPositionLocation = glGetUniformLocation(shader->id(), "lightPosition");
int normalMatrixLocation = glGetUniformLocation(shader->id(), "normalMatrix");
glUniformMatrix4fv(projectionMatrixLocation, 1, GL_FALSE, &projectionMatrix[0][0]);
glUniformMatrix4fv(viewMatrixLocation, 1, GL_FALSE, &viewMatrix[0][0]);
glUniformMatrix4fv(modelMatrixLocation, 1, GL_FALSE, &modelMatrix[0][0]);
glUniform3fv(lightPositionLocation, 1, lightPositionArray);
for (unsigned int i = 0; i < scene->mNumMeshes; i++)
{
colorArrayA = new float[3];
colorArrayD = new float[3];
colorArrayS = new float[3];
material = scene->mMaterials[scene->mNumMaterials-1];
normalArray = new float[scene->mMeshes[i]->mNumVertices * 3];
unsigned int normalIndex = 0;
for (unsigned int j = 0; j < scene->mMeshes[i]->mNumVertices * 3; j+=3, normalIndex++)
{
normalArray[j] = scene->mMeshes[i]->mNormals[normalIndex].x; // x
normalArray[j+1] = scene->mMeshes[i]->mNormals[normalIndex].y; // y
normalArray[j+2] = scene->mMeshes[i]->mNormals[normalIndex].z; // z
}
normalIndex = 0;
glUniformMatrix3fv(normalMatrixLocation, 1, GL_FALSE, normalArray);
aiColor3D ambient(0.0f, 0.0f, 0.0f);
material->Get(AI_MATKEY_COLOR_AMBIENT, ambient);
aiColor3D diffuse(0.0f, 0.0f, 0.0f);
material->Get(AI_MATKEY_COLOR_DIFFUSE, diffuse);
aiColor3D specular(0.0f, 0.0f, 0.0f);
material->Get(AI_MATKEY_COLOR_SPECULAR, specular);
colorArrayA[0] = ambient.r; colorArrayA[1] = ambient.g; colorArrayA[2] = ambient.b;
colorArrayD[0] = diffuse.r; colorArrayD[1] = diffuse.g; colorArrayD[2] = diffuse.b;
colorArrayS[0] = specular.r; colorArrayS[1] = specular.g; colorArrayS[2] = specular.b;
// bind color for each mesh
glUniform3fv(ambientLocation, 1, colorArrayA);
glUniform3fv(diffuseLocation, 1, colorArrayD);
glUniform3fv(specularLocation, 1, colorArrayS);
// render all meshes
glBindVertexArray(vaoID[i]); // bind our VAO
glDrawElements(GL_TRIANGLES, scene->mMeshes[i]->mNumFaces*3, GL_UNSIGNED_INT, 0);
glBindVertexArray(0); // unbind our VAO
delete [] normalArray;
delete [] colorArrayA;
delete [] colorArrayD;
delete [] colorArrayS;
}
shader->unbind();
app->display();
return;
}
void assimpRenderer::handleEvents()
{
sf::Event event;
while (app->pollEvent(event))
{
if (event.type == sf::Event::Closed)
{
app->close();
}
if ((event.type == sf::Event::KeyPressed) && (event.key.code == sf::Keyboard::Escape))
{
app->close();
}
if (event.type == sf::Event::Resized)
{
glViewport(0, 0, event.size.width, event.size.height);
}
}
return;
}
void assimpRenderer::mainLoop(float modelScale)
{
while (app->isOpen())
{
renderScene(modelScale);
handleEvents();
}
}
bool assimpRenderer::assImport(const std::string& pFile)
{
// read the file with some example postprocessing
scene = importer.ReadFile(pFile,
aiProcess_CalcTangentSpace |
aiProcess_Triangulate |
aiProcess_JoinIdenticalVertices |
aiProcess_SortByPType);
// if the import failed, report it
if (!scene)
{
std::cerr << "Error: " << importer.GetErrorString() << std::endl;
return false;
}
return true;
}
void assimpRenderer::genVAOs()
{
int vboIndex = 0;
for (unsigned int i = 0; i < scene->mNumMeshes; i++, vboIndex+=2)
{
mesh = scene->mMeshes[i];
indexArray = new unsigned int[mesh->mNumFaces * sizeof(unsigned int) * 3];
// convert assimp faces format to array
faceIndex = 0;
for (unsigned int t = 0; t < mesh->mNumFaces; ++t)
{
const struct aiFace* face = &mesh->mFaces[t];
std::memcpy(&indexArray[faceIndex], face->mIndices, sizeof(float) * 3);
faceIndex += 3;
}
// generate VAO
glGenVertexArrays(1, &vaoID[i]);
glBindVertexArray(vaoID[i]);
// generate IBO for faces
glGenBuffers(1, &iboID[i]);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, iboID[i]);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(GLuint) * mesh->mNumFaces * 3, indexArray, GL_STATIC_DRAW);
// generate VBO for vertices
if (mesh->HasPositions())
{
glGenBuffers(1, &vboID[vboIndex]);
glBindBuffer(GL_ARRAY_BUFFER, vboID[vboIndex]);
glBufferData(GL_ARRAY_BUFFER, mesh->mNumVertices * sizeof(GLfloat) * 3, mesh->mVertices, GL_STATIC_DRAW);
glEnableVertexAttribArray((GLuint)0);
glVertexAttribPointer((GLuint)0, 3, GL_FLOAT, GL_FALSE, 0, 0);
}
// generate VBO for normals
if (mesh->HasNormals())
{
normalArray = new float[scene->mMeshes[i]->mNumVertices * 3];
unsigned int normalIndex = 0;
for (unsigned int j = 0; j < scene->mMeshes[i]->mNumVertices * 3; j+=3, normalIndex++)
{
normalArray[j] = scene->mMeshes[i]->mNormals[normalIndex].x; // x
normalArray[j+1] = scene->mMeshes[i]->mNormals[normalIndex].y; // y
normalArray[j+2] = scene->mMeshes[i]->mNormals[normalIndex].z; // z
}
normalIndex = 0;
glGenBuffers(1, &vboID[vboIndex+1]);
glBindBuffer(GL_ARRAY_BUFFER, vboID[vboIndex+1]);
glBufferData(GL_ARRAY_BUFFER, mesh->mNumVertices * sizeof(GLfloat) * 3, normalArray, GL_STATIC_DRAW);
glEnableVertexAttribArray((GLuint)1);
glVertexAttribPointer((GLuint)1, 3, GL_FLOAT, GL_FALSE, 0, 0);
delete [] normalArray;
}
// tex coord stuff goes here
// unbind buffers
glBindVertexArray(0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
delete [] indexArray;
}
vboIndex = 0;
return;
}
}
file: shader.vert
#version 150 core
in vec3 in_Position;
in vec3 in_Normal;
uniform mat4 projectionMatrix;
uniform mat4 viewMatrix;
uniform mat4 modelMatrix;
uniform vec3 lightPosition;
uniform mat3 normalMatrix;
smooth out vec3 vVaryingNormal;
smooth out vec3 vVaryingLightDir;
void main()
{
// derive MVP and MV matrices
mat4 modelViewProjectionMatrix = projectionMatrix * viewMatrix * modelMatrix;
mat4 modelViewMatrix = viewMatrix * modelMatrix;
// get surface normal in eye coordinates
vVaryingNormal = normalMatrix * in_Normal;
// get vertex position in eye coordinates
vec4 vPosition4 = modelViewMatrix * vec4(in_Position, 1.0);
vec3 vPosition3 = vPosition4.xyz / vPosition4.w;
// get vector to light source
vVaryingLightDir = normalize(lightPosition - vPosition3);
// Set the position of the current vertex
gl_Position = modelViewProjectionMatrix * vec4(in_Position, 1.0);
}
file: shader.frag
#version 150 core
out vec4 out_Color;
uniform vec3 ambientColor;
uniform vec3 diffuseColor;
uniform vec3 specularColor;
smooth in vec3 vVaryingNormal;
smooth in vec3 vVaryingLightDir;
void main()
{
// dot product gives us diffuse intensity
float diff = max(0.0, dot(normalize(vVaryingNormal), normalize(vVaryingLightDir)));
// multiply intensity by diffuse color, force alpha to 1.0
out_Color = vec4(diff * diffuseColor, 1.0);
// add in ambient light
out_Color += vec4(ambientColor, 1.0);
// specular light
vec3 vReflection = normalize(reflect(-normalize(vVaryingLightDir), normalize(vVaryingNormal)));
float spec = max(0.0, dot(normalize(vVaryingNormal), vReflection));
if (diff != 0)
{
float fSpec = pow(spec, 128.0);
// Set the output color of our current pixel
out_Color.rgb += vec3(fSpec, fSpec, fSpec);
}
}
I know it's a lot to look through, but I'm putting most of the code up so as not to assume where the problem is.
Are you doing the right thing for the normal matrix? This looks quite bizarre to me.
for (unsigned int j = 0; j < scene->mMeshes[i]->mNumVertices * 3; j+=3, normalIndex++)
{
normalArray[j] = scene->mMeshes[i]->mNormals[normalIndex].x; // x
normalArray[j+1] = scene->mMeshes[i]->mNormals[normalIndex].y; // y
normalArray[j+2] = scene->mMeshes[i]->mNormals[normalIndex].z; // z
}
glUniformMatrix3fv(normalMatrixLocation, 1, GL_FALSE, normalArray);
Why does the normalMatrix have anything to do with the vertices of the mesh? It should be identical to your modelMatrix (provided that you're not doing any non-uniform scaling).