本网站(662p.com)打包出售,且带程序代码数据,662p.com域名,程序内核采用TP框架开发,需要联系扣扣:2360248666 /wx:lianweikj
精品域名一口价出售:1y1m.com(350元) ,6b7b.com(400元) , 5k5j.com(380元) , yayj.com(1800元), jiongzhun.com(1000元) , niuzen.com(2800元) , zennei.com(5000元)
需要联系扣扣:2360248666 /wx:lianweikj
图形学基础 | 实现OBJ文件的载入
iamitnan · 1697浏览 · 发布于2019-05-29 +关注

在做一个软光栅化渲染器,增加对复杂模型的支持,选择对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 观看效果.

20190407205940418.png


导入光栅化渲染器的线框模型

20190407205717730.png



相关推荐

干货!麻将平胡算法

· 992浏览 · 2019-06-06 11:45:17
Java桌球小游戏

奔跑的男人 · 640浏览 · 2019-06-11 09:37:46
图形用户界面和游戏开发

qq2360248666 · 712浏览 · 2019-06-11 09:57:01
Three.js模型隐藏或显示

吴振华 · 559浏览 · 2019-06-14 10:18:27
Cocos工程命名规则整理(node部分)

吴振华 · 877浏览 · 2019-06-14 10:24:18
教你用Python画一只属于自己的皮卡丘

· 1668浏览 · 2019-06-17 10:35:38
加载中

0评论

评论
分类专栏
小鸟云服务器
扫码进入手机网页