我似乎无法在C ++中使用 glDrawElements 渲染OBJ模型。
这是我的代码:
#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);
}
};
我可以在对顶点和索引进行硬编码时渲染一个简单的三角形,因此它不能成为着色器问题。我已经彻底检查了索引和顶点优化部分,即使与其他示例进行比较,也无法看到它有什么问题。
任何想法,更正,解决方案或其他什么?
编辑:立方体模型呈现但现在我遇到了一个新问题 - 在渲染三角形时,索引似乎被双重交叉加扰:
这是相同的cube.obj模型,但在点亮模式下:
顶点着色器代码:
#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;
}
片段着色器代码:
#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());
}
可能是缓冲区偏移问题吗?谢谢你的帮助!
答案 0 :(得分:0)
您发布了大量代码,因此可能会出现比下面指出的错误更多的错误。此外,即使您正确加载和渲染对象,它仍然可能由于各种原因而无法显示,例如因为坐标位于视图体积之外。
从快速浏览看,我在代码中看到了两个错误,您将几何体加载到缓冲区中,并设置顶点属性:
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);
在第二次调用中,您只需将3个索引加载到索引缓冲区中。您必须根据相应向量的大小计算所有索引的大小,就像您第一次调用时一样:
glBufferData(GL_ELEMENT_ARRAY_BUFFER, i.size() * sizeof(CBuint), &i[0], GL_STATIC_DRAW);
顶点设置中的偏移量也不正确:
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
是一个向量,因此sizeof(v)
是向量对象的大小,这与此无关。您需要顶点对象中字段的偏移量:
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));