Bly7 / OBJ-Loader

A C++ OBJ Model Loader that will parse .obj & .mtl Files into Indices, Vertices, Materials, and Mesh Structures.
MIT License
533 stars 149 forks source link

OpenGL rendering sample #26

Open virtualenv opened 4 years ago

virtualenv commented 4 years ago

Is there any OpenGL GLFW rendering sample that I could learn from ?

Thank you.

Kriper1111 commented 4 years ago

Even though the issue was opened about a year ago, here's my implementation.

First, we need to load the mesh itself. And for that we need a loader. So it's objl::Loader loader; for the loader and loader.LoadFile(filepath); to load the mesh, where filepath is.. well.. path to the .obj file. Great, we should've got the mesh in. Now we need to access it, objl::Mesh mesh = loader.LoadedMeshes.back();. Note: some people are using loader.LoadedMeshes[0] to get the mesh. Which is correct, if you make a new loader every time you want to load a mesh. In case if you have one constant loader, LoadedMeshes.back() would give you the mesh you need.

Now, when we have the mesh, we have to get its vertex data. I personally used another array(vector) for that. Let's call it vertexData: vector<float> vertexData;. And now we need to fill it with vertex positions, just like we did in OpenGL tutorials, but this time we do that in a loop:

for(int vertex = 0; vertex < mesh.Vertices.size(); vertex++){
    vertexData.push_back(mesh.Vertices[vertex].Position.X);
    vertexData.push_back(mesh.Vertices[vertex].Position.Y);
    vertexData.push_back(mesh.Vertices[vertex].Position.Z);
}

And now we do the usual, make a Vertex Array Object:

GLuint VAO;
glGenVertexArrays(1, &VAO);

make a Vertex Buffer Object:

GLuint VBO;
glGenBuffers(1, &VBO);

bind them and load the data:

glBindVertexArray(VAO);
glBindBuffer(GL_VERTEX_BUFFER, VBO);
glBufferData(GL_VERTEX_BUFFER, vertexData.size() * sizeof(float), vertexData.data(), GL_STATIC_DRAW);

We used `vertexData.size() sizeof(float)to get the byte length of the vertex data array. If you want to type less, you can replacesizeof(float)with4`. Don't forget to pass this data to the shaders in a form of attribute:

glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void *)0);
glEnableVertexAttribArray(0);

You can learn more about glVertexAttribPointer function on learnopengl website. Wonderful, you can render loaded mesh by binding corresponding VAO and calling glDrawArrays(GL_TRIANGLES, 0, vertCount);, where vertCount is the amount of vertices. You can get them using mesh.Vertices.size();.


Please note, that explanation/example above works only for triangulated meshes and doesn't incorporate UVs.

To incorporate UVs, in first loop also push_back texture coordinates:

   ...
   vertexData.push_back(mesh.Vertices[vertex].TextureCoordinate.X);
   vertexData.push_back(mesh.Vertices[vertex].TextureCoordinate.Y);

Don't forget to update vertex attributes:

glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void *)0);
glEnableVertexAttribArray(0);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void *)(3 * sizeof(float)));
glEnableVertexAttribArray(1);

Now you can access your UVs from vertex shaders using layout (location = 1) in vec2 aUV;, and pass them to fragment shaders. This process is neatly described in other OpenGL tutorials. For example learnopengl.

If your mesh is not triangulated (has quads in it, or other non-triangle faces), mesh.Vertices.size() and mesh.Indices.size() will be different, usually Indices > Vertices. You can check for that, and in that case use another buffer, Element Buffer Object. Note that in this case you would need to draw your VAO using glDrawElements(GL_TRIANGLES, vertCount, GL_UNSIGNED_INT, 0);, where vertCount is equal to mesh.Indices.size(); To actually fill EBO, make it

GLuint EBO;
glGenBuffers(1, &EBO);

and fill it

glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, mesh.Indices.size() * sizeof(unsigned int), mesh.Indices.data(), GL_STATIC_DRAW);

Please note that you can't always use indices. If the mesh is triangulated, mesh.Vertices will be in the right order already, and indices would be incorrectly applied.

Hopefully this will help someone some day. And it's readable. Best regards, Kriper @ 2:45am