When I use immediate mode, it draws correctly but when i pass the same vertices to the GPU, or even point them, it doesn't work.
there is a position buffer holding vertices :
std::vector<glm::vec3> posbuf;
and indices for it.
immediate mode :
for (unsigned int i =0; i < indices.size(); i++) {
glBegin(GL_TRIANGLES);
glVertex3f(posbuf[indices[i].index[0]].x, posbuf[indices[i].index[0]].y, posbuf[indices[i].index[0]].z);
glVertex3f(posbuf[indices[i].index[1]].x, posbuf[indices[i].index[1]].y, posbuf[indices[i].index[1]].z);
glVertex3f(posbuf[indices[i].index[2]].x, posbuf[indices[i].index[2]].y, posbuf[indices[i].index[2]].z);
glEnd();
}
and this is the vertex attribute code :
glEnableVertexAttribArray(GL_ATTRIB_POS);
glVertexAttribPointer(GL_ATTRIB_POS, 3, GL_FLOAT, GL_FALSE, 0,&posbuf[0]);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indVBO);
glDrawElements(GL_TRIANGLES, indices.size(), GL_UNSIGNED_INT, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
glDisableVertexAttribArray(GL_ATTRIB_POS);
and a shader for it :
#version 120
attribute vec3 position;
void main()
{
vec4 finalv=vec4(position, 1.0);
gl_Position = gl_ModelViewProjectionMatrix * vec4(finalv.xyz,1.0);
}
[frag]
#version 120
void main()
{
gl_FragColor = vec4(1.0,0.0,0.0,1.0);
}
immediate result :
shader result:
I don't know what's wrong, i also tried to pass posbuf using glm::value_ptr, they all give the same result. I am on fedora 18, supporting glsl up to #version 140, opengl 3.3.
EDIT :
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indVBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(Triangle) * indices.size(), &indices[0], GL_STATIC_DRAW);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
struct Triangle {
int index[3];
};
It seems your indices vector contains complete triangles consisting of 3 indices each. But then in glDrawElements you use indices.size() as the number of elements to draw. But glDrawElements doesn't take the number of primitives, but the number of indices, so you're missing 2/3 of your triangles. It should rather be
glDrawElements(GL_TRIANGLES, indices.size() * 3, GL_UNSIGNED_INT, 0);
(And indices should be renamed to triangles to avoid further confusion. By the way, index should probably be unsigned int[3] as promised to OpenGL. On any reasonable system there won't be a representational difference between ints and unsigned ints, but it is a bit inconsistent to use int while telling OpenGL it's an unsigned int.)
Related
I loaded the material and texture information from the Obj file based on this code (https://github.com/Bly7/OBJ-Loader). Now I want to load Sponza and render it. However, there are 10 or more textures, and even if all are passed to the shader, it is necessary to correspond to the appropriate vertices. Looking at the result of loading from the Obj file, textures are assigned to each part.
Example.
Mesh 0 : Pillar
position0(x, y, z), position1(x, y, z), uv0(x, y) ...
diffuse texture : tex0.png
Mesh 1 : Wall
position0(x, y, z), position1(x, y, z), uv0(x, y) ...
diffuse texture : tex1.png
.
.
.
Textures are kept as an array, and each has a corresponding mesh index. In my opinion, when passing vertex information to the shader, it works well if you divide it by the number of meshes and pass the texture at the same time. However, I'm not sure if this idea is correct, and I've tried several methods but it doesn't work.
This is the current simple code.
main.cpp :
do {
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texture[i]); // texture[] : Array of textures loaded from obj file.
glUniform1i(glGetUniformLocation(shaderID, "myTex"), 0);
glEnableVertexAttribArray(0);
glBindBuffer(GL_ARRAY_BUFFER, vertex_position);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, (void*)0);
glEnableVertexAttribArray(1);
glBindBuffer(GL_ARRAY_BUFFER, texCoord);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, (void*)0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, element);
glDrawElements(GL_TRIANGLES, element_indices.size(), GL_UNSIGNED_INT, (void*)0);
} while(glfwWindowShouldClose(window) == 0);
Fragment shader :
layout(location = 0) out vec4 out_color;
// from vertex shader
in vec2 texCoord;
uniform sampler2D myTex;
void main() {
out_color = texture(myTex, texCoord);
}
I want to correspond to the mesh index loaded with the "i" in the above code. Please let me know if my idea is wrong or if there is another way.
As your model has only one texture per mesh, I can suggest this simple code to use:
do {
glActiveTexture(GL_TEXTURE0);
glUniform1i(glGetUniformLocation(shaderID, "myTex"), 0);
for (unsigned int i = 0; i < mesh_count; i++) {
glcall(glBindTexture(type, texture[i]));
// Bind vertex and index (or element) buffer and setup vertex attribute pointers
// Draw mesh
}
} while (window_open);
The code much self-explaning. It first activates texture slot 0, then for every mesh it binds texture, vertex buffer, index or element buffer and does any preparation need to draw the mesh. Then it issues draw call.
Note that this is very basic example. Most models would look weird with this code. I would recommend to this tutorial from LearnOpenGL which explain this more broadly but in an easy way.
I'm porting an older program using glBegin()/glEnd() (top picture) to glDrawArraysInstanced() (bottom picture). I expected some performance improvements, but I got the opposite. Now this is the first time I've tried using glDrawArraysInstanced() so I think I must have screwed up somewhere.
The two are basically identical and the only difference is how they draw the circles.
What have I done wrong? And if not, what makes it slower?
// This runs once at startup
std::vector<glm::mat4> transforms;
glGenBuffers(NUM_BUFFERS, VBO);
glBindBuffer(GL_ARRAY_BUFFER, VBO[TRANSFORM]);
for (int i = 0; i < 4; ++i) {
glEnableVertexAttribArray(1 + i);
glVertexAttribPointer(1 + i, 4, GL_FLOAT, GL_FALSE, sizeof(glm::mat4),
(GLvoid *)(i * sizeof(glm::vec4)));
glVertexAttribDivisor(1 + i, 1);
} // ---------
// This runs every frame
if (num_circles > transforms.size()) transforms.resize(num_circles);
int i = 0;
for (const auto &circle : circle_vec) {
transforms[i++] = circle.transform.getModel();
}
glBindBuffer(GL_ARRAY_BUFFER, VBO[TRANSFORM]);
glBufferData(GL_ARRAY_BUFFER, sizeof(glm::mat4) * num_circles, &transforms[0], GL_DYNAMIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(VAO);
glDrawArraysInstanced(GL_LINE_LOOP, 0, CIRCLE_NUM_VERTICES, num_circles);
glBindVertexArray(0);
// ---------
// And this is the vertex shader
#version 410
in vec3 position;
in mat4 transform;
void main()
{
gl_Position = transform * vec4(position, 1.0);
}
What I saw at my first glimpse is that you are creating a new vector on every frame. Consider caching it.
// This runs every frame
std::vector<glm::mat4> transforms;
I'm trying to draw some basic triangles using opengl, but it's not rendering on screen. These are the relevant functions:
glewInit();
glClearColor(0.0, 0.0, 0.0, 1.0);
glFrontFace(GL_CW);
glCullFace(GL_BACK);
glEnable(GL_CULL_FACE);
glEnable(GL_DEPTH_TEST);
Vertex vertices[] = {Vertex(Vector3f(0.0, 1.0, 0.0)),
Vertex(Vector3f(-1.0, -1.0, 0.0)),
Vertex(Vector3f(1.0, -1.0, 0.0))};
mesh.addVertices(vertices, 3);
Pastebin links to Vertex.hpp and Vector3f.hpp:
Vertex.hpp
Vector3f.hpp
/*
* Mesh.cpp:
*/
Mesh::Mesh()
{
glGenBuffers(1, &m_vbo); // unsigned int Mesh::m_vbo
}
void Mesh::addVertices(Vertex vertices[4], int indexSize)
{
m_size = indexSize * 3;
glBindBuffer(GL_ARRAY_BUFFER, m_vbo);
glBufferData(GL_ARRAY_BUFFER, m_size, vertices, GL_STATIC_DRAW);
}
void Mesh::draw()
{
glEnableVertexAttribArray(0);
glBindBuffer(GL_ARRAY_BUFFER, m_vbo);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 4 * sizeof(Vertex), 0);
glDrawArrays(GL_TRIANGLES, 0, m_size);
glDisableVertexAttribArray(0);
}
It's just black if I call glClear otherwise just the random noise of a default window. I can make it draw a triangle by using the most primitive method:
glBegin(GL_TRIANGLES);
glColor3f(0.4, 0.0, 0.0);
glVertex2d(0.0, 0.5);
glVertex2d(-0.5, -0.5);
glVertex2d(0.5, -0.5);
glEnd();
That works and displays what it should do correctly, so I guess that at least says my application is not 100% busted. The tutorial I'm following is in Java, and I'm translating it to C++ SFML as I go along, so I guess it's possible that something got lost in translation so to speak, unless I'm just missing something really basic (more likely.)
How do we fix this so it uses the Vertex list to draw the triangle like it's supposed to?
So many mistakes. There are truly a lot of examples, in any language, so why?
const float pi = 3.141592653589793; is member field of Vector3f. Do you realise this is non-static member and it is included in each and every Vector3f you use, so your vectors actually have four elements - x, y, z, and pi? Did you informed GL about it, so it could skip this garbage data? I don't think so.
You using glVertexAttribPointer, but don't have active shader. There is no guarantee that position is in slot 0. Either use glVertexPointer, or use shader with position attribute bound to 0.
void Mesh::addVertices(Vertex vertices[4], int indexSize) what [4] supposed to mean here? While it is not an error, it is at least misguiding.
glBufferData(GL_ARRAY_BUFFER, m_size, vertices, GL_STATIC_DRAW); m_size is 3*3 in your example, while documentation says it should be array size in bytes - which is sizeof(Vertex) * indexSize.
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 4 * sizeof(Vertex), 0); why stride parameter is 4*sizeof(Vertex)? Either set it to 0 or write correct stride - which is sizeof(Vertex).
glDrawArrays(GL_TRIANGLES, 0, m_size); m_size is already [incorrectly] set as "vertex buffer size", while DrawArrays expects number of vertices to draw - which is m_size / sizeof(Vertex) (given m_size is calculated correctly).
I have written a basic program that loads a model and renders it to the screen. I'm using GLSL to transform the model appropriately, but the normals always seem to be incorrect after rotating them with every combination of model matrix, view matrix, inverse, transpose, etc that I could think of. The model matrix is just a rotation around the y-axis using glm:
angle += deltaTime;
modelMat = glm::rotate(glm::mat4(), angle, glm::vec3(0.f, 1.f, 0.f));
My current vertex shader code (I've modified the normal line many many times):
#version 150 core
uniform mat4 projMat;
uniform mat4 viewMat;
uniform mat4 modelMat;
in vec3 inPosition;
in vec3 inNormal;
out vec3 passColor;
void main()
{
gl_Position = projMat * viewMat * modelMat * vec4(inPosition, 1.0);
vec3 normal = normalize(mat3(inverse(modelMat)) * inNormal);
passColor = normal;
}
And my fragment shader:
#version 150 core
in vec3 passColor;
out vec4 outColor;
void main()
{
outColor = vec4(passColor, 1.0);
}
I know for sure that the uniform variables are being passed to the shader properly, as the model itself gets transformed properly, and the initial normals are correct if I do calculations such as directional lighting.
I've created a GIF of the rotating model, sorry about the low quality:
http://i.imgur.com/LgLKHCb.gif?1
What confuses me the most is how the normals appear to rotate on multiple axis, which I don't think should happen when multiplied by a simple rotation matrix on one axis.
Edit:
I've added some more of the client code below.
This is where the buffers get bound for the model, in the Mesh class (vao is GLuint, defined in the class):
GLuint vbo[3];
glGenVertexArrays(1, &vao);
glBindVertexArray(vao);
glGenBuffers(normals? (uvcoords? 3 : 2) : (uvcoords? 2 : 1), vbo);
glBindBuffer(GL_ARRAY_BUFFER, vbo[0]);
glBufferData(GL_ARRAY_BUFFER, vcount * 3 * sizeof(GLfloat), vertices, GL_STATIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);
glEnableVertexAttribArray(0);
if(normals)
{
glBindBuffer(GL_ARRAY_BUFFER, vbo[1]);
glBufferData(GL_ARRAY_BUFFER, vcount * 3 * sizeof(GLfloat), normals, GL_STATIC_DRAW);
glVertexAttribPointer(1, 3, GL_FLOAT, GL_TRUE, 0, 0);
glEnableVertexAttribArray(1);
}
if(uvcoords)
{
glBindBuffer(GL_ARRAY_BUFFER, vbo[2]);
glBufferData(GL_ARRAY_BUFFER, vcount * 2 * sizeof(GLfloat), uvcoords, GL_STATIC_DRAW);
glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 0, 0);
glEnableVertexAttribArray(2);
}
glBindVertexArray(0);
glGenBuffers(1, &ib);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ib);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, icount * sizeof(GLushort), indices, GL_STATIC_DRAW);
This is where the shaders are compiled after being loaded into memory with a simple readf(), in the Material class:
u32 vertexShader = glCreateShader(GL_VERTEX_SHADER);
u32 fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(vertexShader, 1, (const GLchar**)&vsContent, 0);
glCompileShader(vertexShader);
if(!validateShader(vertexShader)) return false;
glShaderSource(fragmentShader, 1, (const GLchar**)&fsContent, 0);
glCompileShader(fragmentShader);
if(!validateShader(fragmentShader)) return false;
programHandle = glCreateProgram();
glAttachShader(programHandle, vertexShader);
glAttachShader(programHandle, fragmentShader);
glBindAttribLocation(programHandle, 0, "inPosition");
glBindAttribLocation(programHandle, 1, "inNormal");
//glBindAttribLocation(programHandle, 2, "inUVCoords");
glLinkProgram(programHandle);
if(!validateProgram()) return false;
And the validateShader(GLuint) and validateProgram() functions:
bool Material::validateShader(GLuint shaderHandle)
{
char buffer[2048];
memset(buffer, 0, 2048);
GLsizei len = 0;
glGetShaderInfoLog(shaderHandle, 2048, &len, buffer);
if(len > 0)
{
Logger::log("ve::Material::validateShader: Failed to compile shader - %s", buffer);
return false;
}
return true;
}
bool Material::validateProgram()
{
char buffer[2048];
memset(buffer, 0, 2048);
GLsizei len = 0;
glGetProgramInfoLog(programHandle, 2048, &len, buffer);
if(len > 0)
{
Logger::log("ve::Material::validateProgram: Failed to link program - %s", buffer);
return false;
}
glValidateProgram(programHandle);
GLint status;
glGetProgramiv(programHandle, GL_VALIDATE_STATUS, &status);
if(status == GL_FALSE)
{
Logger::log("ve::Material::validateProgram: Failed to validate program");
return false;
}
return true;
}
Each Material instance has a std::map of Meshs, and get rendered as so:
void Material::render()
{
if(loaded)
{
glUseProgram(programHandle);
for(auto it = mmd->uniforms.begin(); it != mmd->uniforms.end(); ++it)
{
GLint loc = glGetUniformLocation(programHandle, (const GLchar*)it->first);
switch(it->second.type)
{
case E_UT_FLOAT3: glUniform3fv(loc, 1, it->second.f32ptr); break;
case E_UT_MAT4: glUniformMatrix4fv(loc, 1, GL_FALSE, it->second.f32ptr); break;
default: break;
}
}
for(Mesh* m : mmd->objects)
{
GLint loc = glGetUniformLocation(programHandle, "modelMat");
glUniformMatrix4fv(loc, 1, GL_FALSE, &m->getTransform()->getTransformMatrix()[0][0]);
m->render();
}
}
}
it->second.f32ptr would be a float pointer to &some_vec3[0] or &some_mat4[0][0].
I manually upload the model's transformation matrix before rendering, however (which is only a rotation matrix, the Transform class (returned by Mesh::getTransform()) will only do a glm::rotation() since I was trying to figure out the problem).
Lastly, the Mesh render code:
if(loaded)
{
glBindVertexArray(vao);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ib);
glDrawElements(GL_TRIANGLES, indexCount, GL_UNSIGNED_SHORT, 0);
}
I think this is all the necessary code, but I can post more if needed.
Your nomal matrix calculation is just wrong. The correct normal matrix would be the transpose of the inverse of the upper-left 3x3 submatrix of the model or modelview matrix (depending on which space you want to do your lighting calculations).
What you do is just inverting the full 4x4 matrix and taking the upper-left 3x3 submatrix of that, which is just totally wrong.
You should calculate transpose(inverse(mat3(modelMat))), but you really shouldn't do this in the shader, but calulate this toghether with the model matrix on the CPU to avoid letting the GPU calculate a quite expensive matrix inversion per vertex.
As long as your transformations consist of only rotations, translations, and uniform scaling, you can simply apply the rotation part of your transformations to the normals.
In general, it's the transposed inverse matrix that needs to be applied to the normals, using only the regular 3x3 linear transformation matrix, without the translation part that extends the matrix to 4x4.
For rotations and uniform scaling, the inverse-transpose is identical to the original matrix. So the matrix operations to invert and transpose matrices are only needed if you apply other types of transformations, like non-uniform scaling, or shear transforms.
Apparently, if the vertex normals of a mesh are incorrect, then strange rotation artifacts will occur. In my case, I had transformed the mesh in my 3D modelling program (Blender) by 90 degrees on the X axis, as Blender uses the z-axis as its vertical axis, whereas my program uses the y-axis as the vertical axis. However, the method I used to transform/rotate the mesh in Blender in my export script did not properly transform the normals, but only the positions of the vertices. Without any prior transformations, the program works as expected. I initially found out that the normals were incorrect by comparing the normalized positions and normals in a symmetrical object (I used a cube with smoothed normals), and saw that the normals were rotated. Thank you to #derhass and #Solkar for guiding me to the answer.
However, if anyone still wants to contribute, I would like to know why the normals don't rotate in one axis when multiplied by a single axis rotation matrix, even if they are incorrect.
I generated model (Suzie) in blender and exported it to .obj file with normals. During loading mode to my app i noticed that numbers of vertices and normals are diffrent (2012 and 1967).
I try to implement simple cell shading. The problem is in passing normals to shader. For storing vertex data i use vectors from glm.
std::vector<unsigned int> face_indices;
std::vector<unsigned int> normal_indices;
std::vector<glm::vec3> geometry;
std::vector<glm::vec3> normals;
Result i've got so far
Buffers Layout
glBindVertexArray(VAO);
glEnableVertexAttribArray(0);
glBindBuffer(GL_ARRAY_BUFFER, VertexVBOID);
glBufferData(GL_ARRAY_BUFFER, geometry.size() * sizeof(glm::vec3), &geometry[0], GL_STATIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);
glEnableVertexAttribArray(1);
glBindBuffer(GL_ARRAY_BUFFER, NormalVBOID);
glBufferData(GL_ARRAY_BUFFER, normals.size() * sizeof(glm::vec3), &normals[0], GL_DYNAMIC_DRAW);
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 0, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, VIndexVBOID);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, face_indices.size() * sizeof(unsigned int), &face_indices[0], GL_DYNAMIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);
Rendering fragment
glBindVertexArray(VAO);
glPolygonMode(GL_FRONT_AND_BACK, GL_QUADS);
glDrawElements(GL_QUADS, face_indices.size(), GL_UNSIGNED_INT, (void*)0);
glBindVertexArray(0);
The reason that had such wierd problem was that some normals were used more than once to preserve disk space so i had to rearrange them in a proper order. So the solution is pretty trival.
geometry.clear();
normals.clear();
geometry.resize(vv.size());
normals.resize(vv.size());
for (unsigned int i = 0; i < face_indices.size(); i++)
{
int vi = face_indices[i];
int ni = normal_indices[i];
glm::vec3 v = vv [vi];
glm::vec3 n = vn [ni];
geometry[vi] = v ;
normals[vi] = n ;
indices.push_back(vi);
}
You should also keep in mind that using the smooth modifier in Blender before export will in some cases help ensure that you have 1 normal per vertex (you may or may not need to also set per-vert normal view instead of face-normal view...can't rem so you'll have to test). This is because by default, blender uses per-face normals. The smooth modifier ("w" hotkey menu)
will switch it to per-vertex norms. Then when you export, you export verts and norms as usual, and the number should match. It doesn't always, but this has worked for me in the past.
This could possibly mean less unnecessary juggling of your data during import.