[英]Wavefront OBJ Model Not Rendering - OpenGL C++
I can't seem to render an OBJ model using glDrawElements in C++.我似乎无法在 C++ 中使用glDrawElements渲染 OBJ 模型。
Here is my code:这是我的代码:
#include <iostream>
#include <stdio.h>
#include <vector>
#include <fstream>
#include "Object.h"
#define BUFFER_OFFSET(i) ((char *)NULL + (i))
class Model : public object
{
private:
struct Vertex
{
CBfloat v1, v2, v3;
CBfloat vn1, vn2, vn3;
CBfloat vt1, vt2;
Vertex(vec3 _position, vec3 _normal, vec2 _texCoord)
{
v1 = _position[0];
v2 = _position[1];
v3 = _position[2];
vn1 = _normal[0];
vn2 = _normal[1];
vn3 = _normal[2];
vt1 = _texCoord[0];
vt2 = _texCoord[1];
}
};
struct Face
{
CBuint v[3];
CBuint vt[3];
CBuint vn[3];
CBuint mat_id;
Face(CBuint v1, CBuint vt1, CBuint vn1, CBuint v2, CBuint vt2, CBuint vn2, CBuint v3, CBuint vt3, CBuint vn3) : mat_id(-1)
{
v[0] = v1;
vt[0] = vt1;
vn[0] = vn1;
v[1] = v2;
vt[1] = vt2;
vn[1] = vn2;
v[2] = v3;
vt[2] = vt3;
vn[2] = vn3;
}
};
struct Mesh
{
CBuint vbo;
CBuint ebo;
Mesh() {}
Mesh(std::vector <Vertex>& v, std::vector <CBuint>& i)
{
// Generate
glGenBuffers(1, &vbo);
glGenBuffers(1, &ebo);
// Bind
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);
// Buffer
glBufferData(GL_ARRAY_BUFFER, v.size() * sizeof(Vertex), &v[0], GL_STATIC_DRAW);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(CBuint) * 3, &i[0], GL_STATIC_DRAW);
// Set attribs
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), BUFFER_OFFSET(0));
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), BUFFER_OFFSET(sizeof(v) * 3));
glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), BUFFER_OFFSET(sizeof(v) * 5));
// Enable attribs
glEnableVertexAttribArray(0);
glEnableVertexAttribArray(1);
glEnableVertexAttribArray(2);
// Clean up
glBindVertexArray(0);
}
~Mesh()
{
glDeleteBuffers(1, &vbo);
glDeleteBuffers(1, &ebo);
}
};
// Local variables
CBuint _vao;
bool _materialised;
std::string _obj_url;
std::string _mtl_url;
std::vector <std::string> _material_element;
std::vector <Vertex> _vertices;
std::vector <CBuint> _indices;
std::vector <Mesh*> _meshes;
std::vector <Material*> _materials;
public:
Model(Shader* shader) : _materialised(false) {}
Model(Shader* shader, const char* obj_file) : _materialised(false)
{
// Load OBJ file
if (loadOBJ(obj_file) != 1)
std::cout << "Error: Failed to load OBJ!" << std::endl;
// Load MTL file
if (_materialised)
{
if (loadMTL(shader, _mtl_url.c_str()) != 1)
std::cout << "Error: Failed to load MTL file!" << std::endl;
}
// Apply material if none were applied
if (!_materialised)
{
_materials.push_back(new Material(shader, "default_m",false, false, 1.0f, vec3(1.0f, 1.0f, 1.0f), vec3(0.25f, 0.25f, 0.25f), 1.0f, 8.0f, 1.0f));
_materialised = true;
}
// Initialise matrix
initialiseMatrix(shader);
}
~Model()
{
if (!_indices.empty())
clear();
glDeleteVertexArrays(1, &_vao);
}
inline CBvoid clear()
{
_vertices.clear();
_indices.clear();
_materials.clear();
_meshes.clear();
}
inline CBint loadOBJ(const char* file)
{
this->_obj_url = file;
// Check for valid file
std::ifstream _in(file);
if (!_in)
{
std::cout << "Error: OBJ file is invalid!" << std::endl;
return -1;
}
else if (!_in.is_open())
{
std::cout << "Error: Cannot open OBJ file!" << std::endl;
return -1;
}
// Get data
char data[256];
std::vector <std::string> line;
while (_in.getline(data, 256))
line.push_back(data);
// Assign vertex data
std::vector <vec3> _v;
std::vector <vec3> _vn;
std::vector <vec2> _vt;
std::vector <Face> _f;
for (unsigned int i = 0; i < line.size(); i++)
{
switch ((line[i])[0])
{
case '#':
continue;
case '\0':
continue;
case 'm':
char mtl_url[128];
sscanf_s((&line[i])[0].c_str(), "mtllib %s", mtl_url, sizeof(mtl_url));
_mtl_url = mtl_url;
_materialised = true;
break;
case 'v':
float x, y, z;
if ((line[i])[1] == 'n')
{
sscanf_s((&line[i])[0].c_str(), "vn %f %f %f", &x, &y, &z);
_vn.push_back(vec3(x, y, z));
}
if ((line[i])[1] == 't')
{
sscanf_s((&line[i])[0].c_str(), "vt %f %f", &x, &y);
_vt.push_back(vec2(x, y));
}
else if ((line[i])[1] == ' ')
{
sscanf_s((&line[i])[0].c_str(), "v %f %f %f", &x, &y, &z);
_v.push_back(vec3(x, y, z));
}
break;
case 'u':
char material_element[128];
sscanf_s((&line[i])[0].c_str(), "usemtl %s", &material_element, sizeof(material_element));
// Assign new material to element
if (_material_element.size() > 1)
_f[_f.size() - 1].mat_id = _material_element.size();
_material_element.push_back(material_element);
break;
case 'f':
CBuint v_i[3];
CBuint vn_i[3];
CBuint vt_i[3];
sscanf_s((&line[i])[0].c_str(), "f %d/%d/%d %d/%d/%d %d/%d/%d", &v_i[0], &vt_i[0], &vn_i[0], &v_i[1], &vt_i[1], &vn_i[1], &v_i[2], &vt_i[2], &vn_i[2]);
// Faces
_f.push_back(Face(v_i[0], vt_i[0], vn_i[0], v_i[1], vt_i[1], vn_i[1], v_i[2], vt_i[2], vn_i[2]));
// Indices
_indices.push_back(v_i[0] - 1);
_indices.push_back(vt_i[0] - 1);
_indices.push_back(vn_i[0] - 1);
_indices.push_back(v_i[1] - 1);
_indices.push_back(vt_i[1] - 1);
_indices.push_back(vn_i[1] - 1);
_indices.push_back(v_i[2] - 1);
_indices.push_back(vt_i[2] - 1);
_indices.push_back(vn_i[2] - 1);
break;
}
}
// Optimise vertices
for (CBuint i = 0; i < _v.size(); i++)
_vertices.push_back(Vertex(_v[i], vec3(0.0f, 0.0f, 0.0f), vec2(1.0f, 1.0f)));
// Optimise buffers
CBuint next_element = 0;
glGenVertexArrays(1, &_vao);
glBindVertexArray(_vao);
if (_materials.size() > 1)
{
for (CBuint i = 0; i < _f.size(); i++)
{
if (_f[i].mat_id == next_element)
{
_meshes.push_back(new Mesh(_vertices, _indices));
next_element++;
}
}
}
else
_meshes.push_back(new Mesh(_vertices, _indices));
glBindVertexArray(0);
// Output
std::cout << "v: " << _v.size() << std::endl;
std::cout << "vn: " << _vn.size() << std::endl;
std::cout << "vt: " << _vt.size() << std::endl;
std::cout << "f: " << _f.size() << std::endl;
std::cout << "vertices: " << _vertices.size() << std::endl;
std::cout << "indices: " << _indices.size() << std::endl;
std::cout << "meshes: " << _meshes.size() << std::endl;
std::cout << "materials: " << _materials.size() << std::endl;
// Close file and return
_in.close();
return 1;
}
inline CBint loadMTL(Shader* shader, const char* file)
{
// ----------------- MTL pipeline -----------------
// ----------------------------------------------------
// Ns - specular exponent multiplied by the texture value
// d - dissolve multiplied by the texture value
// Ka - ambient colour multiplied by the texture value
// Kd - diffuse colour multiplied by the texture value
// Ks - specular colour multiplied by the texture value
// Ke - emissive colour multiplied by the texture value
// map_* - texture map multiplied by the texture value
// bump - normal map multiplied by the texture value
// Check file
std::ifstream in_file(file);
if (!in_file)
{
std::cout << "Error: MTL file is invalid!" << std::endl;
return -1;
}
else if (!in_file.is_open())
{
std::cout << "Error: MTL file failed to open!" << std::endl;
return -1;
}
// Push_back materials from elements in obj
for (unsigned int i = 0; i < _material_element.size(); i++)
_materials.push_back(new Material(shader, _material_element[i], false, false, 1.0f, vec3(1.0f, 1.0f, 1.0f), vec3(0.5f, 0.5f, 0.5f), 0.75f, 8.0f, 1.0f));
// Get data
char data[256];
std::vector <std::string> line;
while (in_file.getline(data, 256))
line.push_back(data);
// Assign data
CBint current_material_element = -1;
for (unsigned int i = 0; i < line.size(); i++)
{
switch ((line[i])[0])
{
case '#':
continue;
case '\0':
continue;
case 'n':
current_material_element++;
break;
case '\t':
if ((line[i])[1] == 'N' && (line[i])[2] == 's')
{
float _Ns = 0.0f;
sscanf_s((&line[i])[0].c_str(), "\tNs %f", &_Ns);
_materials[current_material_element]->_specular_damper = _Ns;
}
else if ((line[i])[1] == 'd')
{
float _d = 0.0f;
sscanf_s((&line[i])[0].c_str(), "\td %f", &_d);
_materials[current_material_element]->_opacity = _d;
}
else if ((line[i])[1] == 'K' && (line[i])[2] == 'd')
{
vec3 _Kd = vec3(0.0f);
sscanf_s((&line[i])[0].c_str(), "\tKd %f %f %f", &_Kd.data[0], &_Kd.data[1], &_Kd.data[2]);
_materials[current_material_element]->_diffuse = _Kd;
}
else if ((line[i])[1] == 'K' && (line[i])[2] == 's')
{
vec3 _Ks = vec3(0.0f);
sscanf_s((&line[i])[0].c_str(), "\tKs %f %f %f", &_Ks.data[0], &_Ks.data[1], &_Ks.data[2]);
_materials[current_material_element]->_specular = _Ks;
}
break;
}
}
return 1;
}
virtual void update(double delta)
{
tickMatrix();
// TEMP!
_rotation = vec4(20.0f, 0.0f, 1.0f, 0.0f);
}
virtual void render()
{
glUniformMatrix4fv(_u_model, 1, GL_FALSE, _model);
_materials[0]->bind();
glBindVertexArray(_vao);
glDrawElements(GL_TRIANGLES, _indices.size(), GL_UNSIGNED_INT, 0);
glBindVertexArray(0);
}
};
I can render a simple triangle when hard-coding the vertices and indices, so it can't be a shader problem.在对顶点和索引进行硬编码时,我可以渲染一个简单的三角形,因此它不可能是着色器问题。 I've thoroughly checked through the index and vertex optimisation part and can't see anything wrong with it, even after comparing with other examples.我已经彻底检查了索引和顶点优化部分,即使与其他示例进行了比较,也看不出有什么问题。
Any ideas, corrections, solutions or whatnot?任何想法,更正,解决方案或诸如此类的东西?
Edit: The cube model renders but now I've come across a new problem - the indices seem to be scrambled with double cross-overs when rendering the triangles:编辑:立方体模型渲染,但现在我遇到了一个新问题 - 渲染三角形时,索引似乎被双重交叉打乱:
And here is the same cube.obj model but in lit mode:这是相同的 cube.obj 模型,但处于点亮模式:
Click to see in lit mode!单击以在点亮模式下查看!
Vertex shader code:顶点着色器代码:
#version 330 core
// Vertex attributes
layout(location = 0) in vec3 position;
layout(location = 1) in vec3 normal;
layout(location = 2) in vec2 texCoord;
// Node parses
out vec2 TexCoord;
out vec3 Normal;
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);
Normal = mat3(transpose(inverse(model))) * normal;
Fragpos = vec3(model * vec4(position, 1.0f));
TexCoord = texCoord;
}
Fragment shader code:片段着色器代码:
#version 330 core
// Node parses
out vec4 color;
in vec2 TexCoord;
in vec3 Normal;
in vec3 Fragpos;
// Camera data
uniform vec3 view_loc;
vec3 view_dir;
// Model data
vec3 normal;
// Phong data
vec3 _ambient;
vec3 _diffuse;
vec3 _specular;
vec3 _emissive;
vec3 _result;
// Material uniforms
uniform struct MATERIAL
{
bool masked;
bool additive;
float opacity;
float specular_damper;
float specular_intensity;
float emissive_intensity;
vec3 diffuse;
vec3 specular;
} _mat;
uniform sampler2D texture[3];
// Global uniforms
uniform struct GLOBAL
{
float ambient_coefficient;
vec3 ambient_colour;
} _global;
// Directional uniforms
uniform struct DIRECTIONAL
{
float intensity;
vec3 position;
vec3 colour;
} _dir;
vec3 d_view_dir;
// Directional light
vec3 directional()
{
vec3 r;
d_view_dir = normalize(-_dir.position);
float src = max(dot(normal, d_view_dir),0.0);
r = (src * _dir.colour) * _dir.intensity;
return r;
}
// Final gather
vec4 finalGather()
{
normal = normalize(Normal);
view_dir = normalize(view_loc - Fragpos);
// Ambient
_ambient = _global.ambient_coefficient * _mat.diffuse * _dir.intensity;
// Diffuse
_diffuse = _mat.diffuse * directional();
// Specular
vec3 reflectDir = reflect(-d_view_dir, normal);
float power = pow(max(dot(view_dir, reflectDir), 0.0), _mat.specular_damper);
_specular = (_mat.specular) * power * _dir.colour * _dir.intensity;
// Emissive
// Final result
_result = (_ambient + _diffuse + _specular);
//_diffuse;
return vec4(_result, _mat.opacity);
}
// Main loop
void main(void)
{
color = vec4(finalGather());
}
Could it be a buffer offset problem?可能是缓冲区偏移问题吗? Thanks for the help guys!感谢您的帮助!
You posted a lot of code, so there could potentially be more errors than the ones pointed out below.您发布了大量代码,因此可能存在比下面指出的错误更多的错误。 Also, even once you load and render your object correctly, it can still not show up for various reasons, eg because the coordinates are outside the view volume.此外,即使您正确加载和渲染您的对象,由于各种原因,它仍然无法显示,例如因为坐标在视图体积之外。
From a quick look, I saw two errors in the code where you load the geometry into buffers, and set up the vertex attributes:快速浏览一下,我在将几何图形加载到缓冲区并设置顶点属性的代码中看到了两个错误:
glBufferData(GL_ARRAY_BUFFER, v.size() * sizeof(Vertex), &v[0], GL_STATIC_DRAW);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(CBuint) * 3, &i[0], GL_STATIC_DRAW);
In the second call, you're only loading 3 indices into the index buffer.在第二次调用中,您只将 3 个索引加载到索引缓冲区中。 You have to calculate the size of all indices, based on the size of the corresponding vector, just like you did for the first call:您必须根据相应向量的大小计算所有索引的大小,就像您在第一次调用时所做的那样:
glBufferData(GL_ELEMENT_ARRAY_BUFFER, i.size() * sizeof(CBuint), &i[0], GL_STATIC_DRAW);
The offsets in the vertex setup are also incorrect:顶点设置中的偏移量也不正确:
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), BUFFER_OFFSET(0));
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), BUFFER_OFFSET(sizeof(v) * 3));
glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), BUFFER_OFFSET(sizeof(v) * 5));
v
is a vector, so sizeof(v)
is the size of the vector object, which is irrelevant here. v
是一个向量,所以sizeof(v)
是向量对象的大小,这里无关紧要。 You need the offset of the fields within the vertex object:您需要顶点对象中字段的偏移量:
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), BUFFER_OFFSET(sizeof(CBfloat) * 3));
glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), BUFFER_OFFSET(sizeof(CBfloat) * 6));
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.