I have a working Vertex-Buffer-Object but I need to add the normals.
The normales are stored in the same array as the vertex positons. They are interleaved
Vx Vy Vz Nx Ny Nz
This is my code so far:
GLfloat values[NUM_POINTS*3 + NUM_POINTS*3];
void initScene() {
for(int i = 0; i < (NUM_POINTS) ; i = i+6){
values[i+0] = bunny[i];
values[i+1] = bunny[i+1];
values[i+2] = bunny[i+2];
values[i+3] = normals[i];
values[i+4] = normals[i+1];
values[i+5] = normals[i+2];
}
glGenVertexArrays(1,&bunnyVAO);
glBindVertexArray(bunnyVAO);
glGenBuffers(1, &bunnyVBO);
glBindBuffer(GL_ARRAY_BUFFER, bunnyVBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(bunny), bunny, GL_STATIC_DRAW);
glVertexAttribPointer(0,3, GL_FLOAT, GL_FALSE, 0,0);
glEnableVertexAttribArray(0);
glGenBuffers(1, &bunnyIBO);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, bunnyIBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(triangles), triangles, GL_STATIC_DRAW);
// unbind active buffers //
glBindVertexArray(0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
}
void renderScene() {
if (bunnyVBO != 0) {
// x: bind VAO //
glEnableClientState(GL_VERTEX_ARRAY);
glBindVertexArray(bunnyVAO);
glDrawElements(GL_TRIANGLES, NUM_TRIANGLES, GL_UNSIGNED_INT, NULL);
glDisableClientState(GL_VERTEX_ARRAY);
// unbind active buffers //
glBindVertexArray(0);
}
}
I can see something on the screen but it is not right as the normals are not used correctly...
How can I use the values array correctly connected with my code so far.
You need to call glVertexAttribPointer two times, once for the vertices and once for the normals. This is how you tell OpenGL how your data is layed out inside your vertex buffer.
// Vertices consist of 3 floats, occurring every 24 bytes (6 floats),
// starting at byte 0.
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 24, 0);
// Normals consist of 3 floats, occurring every 24 bytes starting at byte 12.
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 24, 12);
This is assuming that your normal attribute in your shader has an index of 1.
Related
I'm trying to load and render a 3D model i exported from blender to 3ds format.
I'm using Assimp to load the model and OpenGL (GLEW) to render it. for some reason some. in some of the models only parts of the model gets rendered.
for some this can be fixed by selecting all objects in blender and clicking join. But for others this does not solve the problem.
Here is the code Im using to load the models in:
(all of the "Array"s here are std::vector)
void Mesh::recursiveProcess(aiNode* node,const aiScene* scene) {
for(int i=0;i<node->mNumMeshes;i++) {
aiMesh* mesh=scene->mMeshes[node->mMeshes[i]];
processMesh(mesh, scene);
}
for(int i=0;i<node->mNumChildren;i++) {
recursiveProcess(node->mChildren[i], scene);
}
}
void Mesh::processMesh(aiMesh* m,const aiScene* scene) {
for(int i=0;i<m->mNumVertices;i++) {
vertexArray.push_back(m->mVertices[i].x);
vertexArray.push_back(m->mVertices[i].y);
vertexArray.push_back(m->mVertices[i].z);
if(m->mNormals!=NULL) {
normalArray.push_back(m->mNormals[i].x);
normalArray.push_back(m->mNormals[i].y);
normalArray.push_back(m->mNormals[i].z);
}
if(m->mTextureCoords[0]!=NULL) {
uvArray.push_back(m->mTextureCoords[0][i].x);
uvArray.push_back(m->mTextureCoords[0][i].y);
}
if(m->mTangents!=NULL) {
tangentArray.push_back(m->mTangents[i].x);
tangentArray.push_back(m->mTangents[i].y);
tangentArray.push_back(m->mTangents[i].z);
}
}
for(int i=0;i<m->mNumFaces;i++) {
aiFace face=m->mFaces[i];
for(int j=0;j<face.mNumIndices;j++) {
indexArray.push_back(face.mIndices[j]);
}
}
}
void Mesh::Load(string path) {
vertexArray.clear();
indexArray.clear();
normalArray.clear();
uvArray.clear();
tangentArray.clear();
const aiScene* scene=aiImportFile(path.c_str(),
aiProcess_GenSmoothNormals |
aiProcess_CalcTangentSpace |
aiProcess_Triangulate |
aiProcess_FindInvalidData);
const char* err = aiGetErrorString();
if(err!=NULL&&sizeof(err) > 4 && scene==NULL) {
cout << "The file wasn't successfuly opened " << path << " Because " << err;
return;
}
recursiveProcess(scene->mRootNode,scene);
if(uvArray.size()==0) uvArray.push_back(0);
if(normalArray.size()==0) normalArray.push_back(0);
if(tangentArray.size()==0) tangentArray.push_back(0);
}
and here is how I'm rendering them;
first i create the buffers. i do this once
glGenBuffers(1, &vertexbuffer);
glBindBuffer(GL_ARRAY_BUFFER, vertexbuffer);
glBufferData(GL_ARRAY_BUFFER, vertexArray.size() * sizeof(float), vertexArray.data(), GL_STATIC_DRAW);
glGenBuffers(1, &indexbuffer);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexbuffer);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, indexArray.size() * sizeof(unsigned int), indexArray.data(), GL_STATIC_DRAW);
glGenBuffers(1, &uvbuffer);
glBindBuffer(GL_ARRAY_BUFFER, uvbuffer);
glBufferData(GL_ARRAY_BUFFER, uvArray.size() * sizeof(float), uvArray.data(), GL_STATIC_DRAW);
then I execute this in the main render loop
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_LESS);
glEnable(GL_CULL_FACE);
glEnableVertexAttribArray(0);
glBindBuffer(GL_ARRAY_BUFFER, vertexbuffer);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3*sizeof(float), (void*)0);
glEnableVertexAttribArray(1);
glBindBuffer(GL_ARRAY_BUFFER, uvbuffer);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 2*sizeof(float), (void*)0);
camera.Update();
mvp = camera.getProjection() * camera.getView() * Model;
shader.SetUniformLocationMatrix4fv("MVP", &mvp[0][0]);
glBindTexture(GL_TEXTURE_2D, akm_tex);
glUseProgram(shader);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexbuffer);
glDrawElements(
GL_TRIANGLES, // mode
indexArray.size(), // count
GL_UNSIGNED_INT, // type
(void*)0 // element array buffer offset
);
glDisableVertexAttribArray(0);
glDisableVertexAttribArray(1);
glDisableVertexAttribArray(2);
here is how a model looks like in my program:
and here is how it look in blender
You missed to apply the transformations of the nodes to the vertex buffers you are rendering. Each assimp-node stores a local transformation which need to get applied to all its assigned meshes.
You can introduce in your shader a uniform-variable to represent the global transformation for the vertices. During rendering you need to multiply the local transformation with the global transformation and apply it via this uniform matrix.
Or you can just use glPushMAtrix and glPopMatrix and apply the local transformation of your current node as you can see here (recursive_render)
I have a sprite class to handle initializing a sprite and drawing it but i keep getting this error Exception thrown at 0x68ECC760 (nvoglv32.dll) this seems to be mainly because of _vboID and _vertNum I'm not exactly sure what is wrong with them though, here's the source code for it
void Sprite::init(int vertNum, float r, Shape shape) {
_vertNum = vertNum;
if (_vboID = 0) {
glGenBuffers(1, &_vboID);
}
std::vector<Vertex> vertexData(vertNum);
if (shape == Shape::CIRCLE) {
for (int i = 0; i < vertNum; i++) {
vertexData[i].setPosition(r*cos(i), r*sin(i));
}
}
for (int i = 0; i < _vertNum; i++) {
vertexData[i].setColor(1, 1, 1, 1);
}
glBindBuffer(GL_VERTEX_ARRAY, _vboID);
glBufferData(GL_VERTEX_ARRAY, sizeof(vertexData), vertexData.data(), GL_DYNAMIC_DRAW);
glBindBuffer(GL_VERTEX_ARRAY, 0);
}
void Sprite::draw() {
//bind buffer array
glBindBuffer(GL_VERTEX_ARRAY, _vboID);
//use first vertex attrib array
glEnableVertexAttribArray(0);
//pointer to vertices location
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex, position));
//pointer to vertices color
glVertexAttribPointer(1, 4, GL_UNSIGNED_BYTE, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex, color));
//Draw the 6 vertices to the screen
glDrawArrays(GL_POLYGON, 0, _vertNum);
//Disable the vertex attrib array. This is not optional.
glDisableVertexAttribArray(0);
//Unbind the VBO
glBindBuffer(GL_ARRAY_BUFFER, 0);
}
GL_VERTEX_ARRAY is not a valid buffer binding target. What you are looking for is GL_ARRAY_BUFFER.
When you use GL_VERTEX_ARRAY in contexts where a buffer binding target is expected, you should only get a GL_INVALID_ENUM error, and the calls will have no further effect. As a result, you have no GL_ARRAY_BUFFER bound during your glVertexAttribPointer call, which in a legacy or core profile will result the GL in interpreting your pointers as pointers to client memory.
You should really add some error checks, or use a debug callback, so that such simple mistakes can be spotted early.
I have the following pieces of code where I successfully create a vertex buffer object, initialize it with data, and render it using GLSL 4.0. However, when I go to update the data stored in the vertices after animation, OpenGL gives me the error code 0x502 and does not accept my updated vertices information.
Could someone point me in the direction as to why these code does not allow my vertices information to be successfully updated? I should also mention that sometimes, the data is successfully updated with is not always consistent/predictable.
Data Structure used
struct Vertex3{
glm::vec3 vtx; //0
glm::vec3 norm; //3
glm::vec3 tex; //6 Use for texturing or color
};
vector<Vertex3> geometry.vertices3;
Initialization Code
void solidus::Mesh::initVBO(){
geometry.totalVertexCount = geometry.getVertexCount();
// Allocate an OpenGL vertex array object.
glGenVertexArrays(1, &vertexArrayId);
glGenBuffers(2,geometry.vboObjects);
// Bind the vertex array object to store all the buffers and vertex attributes we create here.
glBindVertexArray(vertexArrayId);
glBindBuffer(GL_ARRAY_BUFFER, geometry.vboObjects[VERTEX_DATA]);
//size the size of the total vtx
GLuint byte_size = getTotalSize();
//Reserve the inital space for the vertex data
glBufferData(GL_ARRAY_BUFFER, byte_size, NULL, GL_STREAM_DRAW);
if(geometry.isStructVertex4())
initVBO4( );
else if(geometry.isStructVertex3())
initVBO3( );
else
initVBO2( );
//release
glBindVertexArray(0);
geometry.vertices4.clear();
//geometry.vertices3.clear();
geometry.vertices2.clear();
}
void solidus::Mesh::initVBO3( ){
//getTotalSize() == getVtxCount() * sizeof(Vertex3);
glBufferSubData(GL_ARRAY_BUFFER, 0, getTotalSize(), &geometry.vertices3[0]);
//Note: offsetof -- c++ standard library
//Note: glVertexAttribPointer- first parameter is location of GLSL variable
glEnableVertexAttribArray(0); // Vertex4 position
glVertexAttribPointer( (GLuint)0, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex3), (GLvoid*)offsetof(Vertex3,vtx) );
// Vertex4 normal
glEnableVertexAttribArray(1);
glVertexAttribPointer( (GLuint)1, 3, GL_FLOAT, GL_TRUE, sizeof(Vertex3), (GLvoid*)offsetof(Vertex3,norm) );
// Texture coords
glEnableVertexAttribArray(2);
glVertexAttribPointer( (GLuint)2, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex3),(GLvoid*)offsetof(Vertex3,tex) );
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, geometry.vboObjects[INDEX_DATA]);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(GLuint)*geometry.indices.size(), &geometry.indices[0], GL_STATIC_DRAW);
}
Update the Mesh Vertex information why does this fail
void solidus::Mesh::uploadVertexGLFx(){
glBindBuffer(GL_ARRAY_BUFFER, geometry.vboObjects[VERTEX_DATA]);
string e0="";
if(geometry.isStructVertex2()){
solidus::GLVBO::setVBOSubData(getTotalSize (), &geometry.vertices2[0]);
e0="Vertex2";
}else if(geometry.isStructVertex3()){
//THIS IS THE POINT OF INTEREST: at least suspected!!!!!
// getVtxCount() * sizeof(Vertex3) = getTotalSize
glBufferSubData(GL_ARRAY_BUFFER, 0, getTotalSize (), &geometry.vertices3[0]);
e0="Vertex3";
}else {
solidus::GLVBO::setVBOSubData(getTotalSize (), &geometry.vertices4[0]);
e0="Vertex4";
}
//report error is glGetError is not equal to 0
postMsg("failed to upload vertex for struct " + e0 , "uploadVertexGLFx",30);
glBindBuffer(GL_ARRAY_BUFFER, 0);
}
I modified my updateVertexGLFx function to the code listed below. The main difference with this good is that after I resupplied the vertices information to GL, I informed OpenGL of the pointer offset using gl*AtribPointer. Now the program reliably updates when I call my update function.
void solidus::Mesh::uploadVertexGLFx(){
glBindBuffer(GL_ARRAY_BUFFER, geometry.vboObjects[VERTEX_DATA]);
string e0="";
if(geometry.isStructVertex2()){
solidus::GLVBO::setVBOSubData(getTotalSize (), &geometry.vertices2[0]);
e0="Vertex2";
}else if(geometry.isStructVertex3()){
//glBufferData(GL_ARRAY_BUFFER, getTotalSize (), NULL, GL_STREAM_DRAW);
//THIS IS THE POINT OF INTEREST: at least suspected!!!!!
// getVtxCount() * sizeof(Vertex3) = getTotalSize
cout << "Total Size = " << getTotalSize() <<endl;
cout << "Vtx Count = " << getVtxCount() << endl;
cout << "Sizeof(Vertex3)=" <<sizeof(Vertex3)<<endl;
Vertex3 *f = new Vertex3[getVtxCount()];
for(int i=0; i<getVtxCount();i++){
f[i] = geometry.vertices3[i];
}
glBufferData(GL_ARRAY_BUFFER, getTotalSize(), NULL, GL_STREAM_DRAW);
glBufferSubData(GL_ARRAY_BUFFER, 0, getTotalSize (), f);
//Note: glVertexAttribPointer- first parameter is location of GLSL variable
glEnableVertexAttribArray(0); // Vertex4 position
glVertexAttribPointer( (GLuint)0, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex3), (GLvoid*)offsetof(Vertex3,vtx) );
// Vertex4 normal
glEnableVertexAttribArray(1);
glVertexAttribPointer( (GLuint)1, 3, GL_FLOAT, GL_TRUE, sizeof(Vertex3), (GLvoid*)offsetof(Vertex3,norm) );
// Texture coords
glEnableVertexAttribArray(2);
glVertexAttribPointer( (GLuint)2, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex3),(GLvoid*)offsetof(Vertex3,tex) );
delete f;
f = nullptr;
e0="Vertex3";
}else {
solidus::GLVBO::setVBOSubData(getTotalSize (), &geometry.vertices4[0]);
e0="Vertex4";
}
//report error is glGetError is not equal to 0
postMsg("failed to upload vertex for struct " + e0 , "uploadVertexGLFx",30);
glBindBuffer(GL_ARRAY_BUFFER, 0);
}
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.
This question already has answers here:
What is the proper way to modify OpenGL vertex buffer?
(3 answers)
Closed 2 years ago.
Currently I'm writing a program that simulates water. Here are the steps that I do:
Create water surface - plane.
Create VAO
Create vertex buffer object in which I store normals and vertices.
Bind pointers to this VBO.
Create index buffer object.
Then I render this plane using glDrawElements and then I invoke an update() function which changes positions of vertices of water surface. After that I invoke glBufferSubData function to update vertices positions.
When I do that - nothing happens as if the buffer isn't changed.
Here's the code snippet:
glGenBuffers(1, &vertexBuffer);
glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer);
glBufferData(GL_ARRAY_BUFFER, sizeof(Oscillator) * nOscillators, oscillators, GL_DYNAMIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(Oscillator), 0);
glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, sizeof(Oscillator), (const GLvoid*)12);
glEnableVertexAttribArray(0); // Vertex position
glEnableVertexAttribArray(2); // normals position
glGenBuffers(1, &indicesBuffer);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indicesBuffer);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(unsigned int) * nIndices, indices, GL_DYNAMIC_DRAW);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indicesBuffer);
glBindVertexArray(0);
Then render:
glBindVertexArray(vaoHandle);
glDrawElements(GL_TRIANGLES, nIndices, GL_UNSIGNED_INT, 0);
update(time);
And update function:
//some calculations
glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(Oscillator) * nOscillators, oscillators);
Oscillator - it's a structure that has: 8 floats respectively - x, y, z (vertex position), nx, ny, nz (normals), upSpeed, newY
oscillators - this is an array of Oscillator structures.
What I do wrong?
Before updating the data you have to bind the correct buffer. E.g:
glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer);
Since you are updating the full buffer at once I would suggest to use glMapBuffer to update it
void* data = glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY);
//[...] update the buffer with new values
bool done = glUnmapBuffer(GL_ARRAY_BUFFER);
And remember to wait (or force) a glFlush() before modifying the data you are goin to copy to the gl buffer.