图形学基础 | 实现OBJ文件的载入
1. tiny_obj_loader.h 的使用
include这个头文件需要先定义一个宏
#define TINYOBJLOADER_IMPLEMENTATION
#include "tiny_obj_loader.h"
1
2
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;
1
2
3
4
5
6
7
attrib_t 主要存放的就是 OBJ文件中所有的顶点数据信息. 比如:
vertices : 顶点位置信息
normals : 法线信息
texcoords : 纹理坐标信息
colors : 颜色信息
2.2 shape_t
typedef struct {
std::string name;
mesh_t mesh;
path_t path;
} shape_t;
1
2
3
4
5
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;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
索引信息重要放在 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(http://www.my516.com) * 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;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
实现结果
meshlab 观看效果.
导入光栅化渲染器的线框模型
---------------------
图形学基础 | 实现OBJ文件的载入的更多相关文章
- [计算机图形学] OpenGL读取obj文件并显示其3D效果
读取三维网格模型(Wavefront OBJ文件) 无法向立方体:cube.obj 有法向兔子模型:bunny.obj 有法向有纹理八字模型:Eight.obj OBJ文件的格式可参考:http: ...
- obj文件的连接问题以及tlib的基本用法
1.基础研究 用tcc将程序编译为.obj文件. 这里也可以使用tcc -linclude run.c来将run.c文件编译成run.obj文件. 再用tcc对下面的程序进行编译链接,发现提示错误: ...
- OBJ文件
OBJ文件是Alias|Wavefront公司为它的一套基于工作站的3D建模和动画软件"Advanced Visualizer"开发的一种标准3D模型文件格式,很适合用于3D软件模 ...
- 软件光栅器实现(四、OBJ文件加载)
本节介绍软件光栅器的OBJ和MTL文件加载,转载请注明出处. 在管线的应用程序阶段,我们需要设置光栅器所渲染的模型数据.这些模型数据包括模型顶点的坐标.纹理.法线和材质等等,可以由我们手动编写,也可以 ...
- 目前以lib后缀的库有两种,一种为静态链接库(Static Libary,以下简称“静态库”),另一种为动态连接库(DLL,以下简称“动态库”)的导入库(Import Libary,以下简称“导入库”)。静态库是一个或者多个obj文件的打包
前以lib后缀的库有两种,一种为静态链接库(Static Libary,以下简称“静态库”),另一种为动态连接库(DLL,以下简称“动态库”)的导入库(Import Libary,以下简称“导入库”) ...
- CSharpGL(9)解析OBJ文件并用CSharpGL渲染
CSharpGL(9)解析OBJ文件并用CSharpGL渲染 2016-08-13 由于CSharpGL一直在更新,现在这个教程已经不适用最新的代码了.CSharpGL源码中包含10多个独立的Demo ...
- python基础知识---操作文件
一.打开文件 open()函数 open函数返回一个文件对象. 用法:open('文件名','模式') 打开文件的模式有: r,只读模式(默认). w,只写模式.[不可读:不存在则创建:存在则删除内 ...
- Java基础之读文件——使用通道读取混合数据2(ReadPrimesMixedData2)
控制台程序,本例读取Java基础之写文件部分(PrimesToFile2)写入的Primes.txt. 方法二:设置一个任意容量的.大小合适的字节缓冲区并且使用来自文件的字节进行填充.然后整理出缓冲区 ...
- Java基础之读文件——使用通道读取混合数据1(ReadPrimesMixedData)
控制台程序,本例读取Java基础之写文件部分(PrimesToFile2)写入的Primes.txt. 方法一:可以在第一个读操作中读取字符串的长度,然后再将字符串和二进制素数值读入到文本中.这种方式 ...
随机推荐
- holiday题解
题目描述如下: 经过几个月辛勤的工作,FJ 决定让奶牛放假.假期可以在 1…N 天内任意选择一段(需要连续),每一天都有一个享受指数 W.但是奶牛的要求非常苛刻,假期不能短于 P 天,否则奶牛不能得到 ...
- WIN7开机自动登录设置
WIN7开机自动登录设置 1 执行rundll32 netplwiz.dll,UsersRunDll 开始菜单中找到运行并单击运行:如下图1所示 ...
- opencord视频截图
参考:https://www.youtube.com/watch?v=Teu9jK6GF6s
- CMDB资产采集笔记
一.资产采集四种方式 1. Agent方式 API:Django接收数据并入库 程序:放置在每台服务器 应用场景:针对服务器较多的公司 步骤一: #执行本地命令的库 import subprocess ...
- Codeforces Round #324 (Div. 2)C. Marina and Vasya
A的万般无奈...后来跑了大牛的这份代码发现, 题意是求一个序列与给定的两个序列有t个不同. 只要保证...对应位置就行了.. 所以处理起来非常方便.............. 可是没有感觉是对应位置 ...
- php,c# hamsha1
#!/usr/bin/php <?php print strtoupper(hash_hmac("sha256", "message", "ke ...
- 屏蔽QQ黄钻官方团队送礼物的方法
按照在网上查到的方法: 登录手机QQ \(\longrightarrow\) 好友动态 \(\longrightarrow\) 个人主页 \(\longrightarrow\) 右上角三道杠 \(\l ...
- 自定义层or网络
目录 Outline keras.Sequential Layer/Model MyDense MyModel Outline keras.Sequential keras.layers.Layer ...
- LuoguP1370 Charlie的云笔记序列 【dp】By cellur925
题目传送门 题目大意:给你一个序列,求出它所有区间的本质不同的子序列个数.(空序列也算作本质不同),数据范围$1e5$. 我们肯定是不能一个个枚举区间的...而且这个复杂度下,也就大概$O(n)$或$ ...
- jquery html() 和text()的用法
html()类似JS中的 innerHTML,首先看一段代码: <!DOCTYPE html> <html lang="en"> <head> ...