在做一个软光栅化渲染器,增加对复杂模型的支持,选择对OBJ文件进行支持.
OBJ文件载入
这里找了一个开源的OBJ文件解析的库 tinyobjloader.
只有一个头文件 tiny_obj_loader.h
主要的使用方法可以参考Github上的 loader_example.cc Demo.
1. tiny_obj_loader.h 的使用
include这个头文件需要先定义一个宏
#define TINYOBJLOADER_IMPLEMENTATION #include "tiny_obj_loader.h"
2. tiny_obj_loader.h 中数据结构的介绍
2.1 attrib_t
// Vertex attributes typedef struct { std::vector<real_t> vertices; // 'v' std::vector<real_t> normals; // 'vn' std::vector<real_t> texcoords; // 'vt' std::vector<real_t> colors; // extension: vertex colors } attrib_t;
attrib_t 主要存放的就是 OBJ文件中所有的顶点数据信息. 比如:
vertices : 顶点位置信息
normals : 法线信息
texcoords : 纹理坐标信息
colors : 颜色信息
2.2 shape_t
typedef struct { std::string name; mesh_t mesh; path_t path; } shape_t;
shape_t 表示的是比如这个物体的一个部分.
在 OBJ文件中, 如 o xxx 表示一个部分的开始.主要存储的信息有:
name : 这部分的名称 xxx
mesh : 构成这个部分的顶点信息. 这里通过使用索引的方式来记录 . 因为所有的数据信息都放在了 attrib_t 中.
path : pairs of indices for lines 按注释的意思是 线段的索引. 可能有问题. 因为没有用到这个.
在这里重点在于 mesh_t 这个数据结构:
// Index struct to support different indices for vtx/normal/texcoord. // -1 means not used. typedef struct { int vertex_index; int normal_index; int texcoord_index; } index_t; typedef struct { std::vector<index_t> indices; std::vector<unsigned char> num_face_vertices; // The number of vertices per // face. 3 = polygon, 4 = quad, // ... Up to 255. std::vector<int> material_ids; // per-face material ID std::vector<unsigned int> smoothing_group_ids; // per-face smoothing group // ID(0 = off. positive value // = group id) std::vector<tag_t> tags; // SubD tag } mesh_t;
索引信息重要放在 std::vector<index_t> indices; 中.
index_t 中是哪些数据信息的索引下标呢:
vertices : 顶点位置信息
normals : 法线信息
texcoords : 纹理坐标信息
3. 通过 tiny_obj_loader.h 读取并存储数据
主要参考了 loader_example.cc 的 PrintInfo(attrib, shapes, materials) 这个函数
bool Object::make_mesh_and_material_by_obj(const char* filename, const char* basepath,bool triangulate){ std::cout << "Loading " << filename << std::endl; tinyobj::attrib_t attrib; // 所有的数据放在这里 std::vector<tinyobj::shape_t> shapes; // 一个shape,表示一个部分, // 其中主要存的是索引坐标 mesh_t类, // 放在indices中 /* // -1 means not used. typedef struct { int vertex_index; int normal_index; int texcoord_index; } index_t; */ std::vector<tinyobj::material_t> materials; std::string warn; std::string err; bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &warn, &err, filename, basepath, triangulate); // 接下里就是从上面的属性中取值了 if (!warn.empty()) { std::cout << "WARN: " << warn << std::endl; } if (!err.empty()) { std::cerr << "ERR: " << err << std::endl; } if (!ret) { printf("Failed to load/parse .obj.\n"); return false; } // ========================== 将读入的模型数据存入自己定义的数据结构中 ======================== std::cout << "# of vertices : " << (attrib.vertices.size() / 3) << std::endl; std::cout << "# of normals : " << (attrib.normals.size() / 3) << std::endl; std::cout << "# of texcoords : " << (attrib.texcoords.size() / 2) << std::endl; std::cout << "# of shapes : " << shapes.size() << std::endl; std::cout << "# of materials : " << materials.size() << std::endl; ///1. 获取各种材质和纹理 { for (int i = 0; i < materials.size(); i++) { Material* m = new Material(); tinyobj::material_t tm = materials[i]; string name = tm.name; if (name.size()) { m->name = name; } m->ambient.r = tm.ambient[0]; m->ambient.g = tm.ambient[1]; m->ambient.b = tm.ambient[2]; m->diffuse.r = tm.diffuse[0]; m->diffuse.g = tm.diffuse[1]; m->diffuse.b = tm.diffuse[2]; m->specular.r = tm.specular[0]; m->specular.g = tm.specular[1]; m->specular.b = tm.specular[2]; m->transmittance.r = tm.transmittance[0]; m->transmittance.g = tm.transmittance[1]; m->transmittance.b = tm.transmittance[2]; m->emission.r = tm.emission[0]; m->emission.g = tm.emission[1]; m->emission.b = tm.emission[2]; m->shininess = tm.shininess; m->ior = tm.ior; m->dissolve = tm.dissolve; m->illum = tm.illum; m->pad0 = tm.pad0; m->ambient_tex_id = -1; m->diffuse_tex_id = -1; m->specular_tex_id = -1; m->specular_highlight_tex_id = -1; m->bump_tex_id = -1; m->displacement_tex_id = -1; m->alpha_tex_id = -1; m->ambient_texname = ""; m->diffuse_texname = ""; m->specular_texname = ""; m->specular_highlight_texname = ""; m->bump_texname = ""; m->displacement_texname = ""; m->alpha_texname = ""; if (tm.ambient_texname.size()) { } if (tm.diffuse_texname.size()) { } if (tm.specular_texname.size()) { } if (tm.specular_highlight_texname.size()) { } if (tm.bump_texname.size()) { } if (tm.displacement_texname.size()) { } if (tm.alpha_texname.size()) { } this->materials.push_back(m); } } /// 2.顶点数据 { // For each shape 遍历每一个部分 for (size_t i = 0; i < shapes.size(); i++) { // 这部分的名称 printf("shape[%ld].name = %s\n", static_cast<long>(i), shapes[i].name.c_str()); // 网格的点数 printf("Size of shape[%ld].mesh.indices: %lu\n", static_cast<long>(i), static_cast<unsigned long>(shapes[i].mesh.indices.size())); //printf("Size of shape[%ld].path.indices: %lu\n", static_cast<long>(i),static_cast<unsigned long>(shapes[i].path.indices.size())); //assert(shapes[i].mesh.num_face_vertices.size() == shapes[i].mesh.material_ids.size()); //assert(shapes[i].mesh.num_face_vertices.size() == shapes[i].mesh.smoothing_group_ids.size()); printf("shape[%ld].num_faces: %lu\n", static_cast<long>(i), static_cast<unsigned long>(shapes[i].mesh.num_face_vertices.size())); Model* model = new Model(); // 每一部分的模型数据 // 顶点数量 = face的数量x3 model->mesh_num = shapes[i].mesh.num_face_vertices.size() * 3; // 开辟空间 Vertex *mesh_data = new Vertex[model->mesh_num]; size_t index_offset = 0; // For each face for (size_t f = 0; f < shapes[i].mesh.num_face_vertices.size(); f++) { size_t fnum = shapes[i].mesh.num_face_vertices[f]; // 获得所索引下标 tinyobj::index_t idx; int vertex_index[3]; int normal_index[3]; int texcoord_index[3]; for (size_t v = 0; v < fnum; v++) { idx = shapes[i].mesh.indices[index_offset + v]; vertex_index[v] = idx.vertex_index; texcoord_index[v] = idx.texcoord_index; normal_index[v] = idx.normal_index; } for (size_t v = 0; v < fnum; v++) { // v mesh_data[index_offset + v].pos.x = attrib.vertices[(vertex_index[v]) * 3 + 0]; mesh_data[index_offset + v].pos.y = attrib.vertices[(vertex_index[v]) * 3 + 1]; mesh_data[index_offset + v].pos.z = attrib.vertices[(vertex_index[v]) * 3 + 2]; mesh_data[index_offset + v].pos.w = 1.0f; // vt mesh_data[index_offset + v].tc.u = attrib.texcoords[texcoord_index[v] * 2 + 0]; mesh_data[index_offset + v].tc.v = attrib.texcoords[texcoord_index[v] * 2 + 1]; // vn mesh_data[index_offset + v].normal.x = attrib.normals[normal_index[v] * 3 + 0]; mesh_data[index_offset + v].normal.y = attrib.normals[normal_index[v] * 3 + 1]; mesh_data[index_offset + v].normal.z = attrib.normals[normal_index[v] * 3 + 2]; mesh_data[index_offset + v].normal.w = 1.0f; // color mesh_data[index_offset + v].color.r = 1.0f; mesh_data[index_offset + v].color.g = 1.0f; mesh_data[index_offset + v].color.b = 1.0f; mesh_data[index_offset + v].color.a = 1.0f; } // 偏移 index_offset += fnum; } model->mesh = mesh_data; models.push_back(model); } } std::cout << "# Loading Complete #"<< std::endl; //PrintInfo(attrib, shapes, materials); return true; }
实现结果
meshlab 观看效果.
导入光栅化渲染器的线框模型
发表评论 取消回复