开始:最近有个计算机图形学的项目,我选了个网络简化与重新网络化。在网上搜了搜没有搜到很好的结果,于是硬着头皮写了个阉割版的loop细分进行重新网络化来应付实验,这里分享一下我的思路,希望对大一大二的同学有点用,当然效果可能不是很好。而且会忽视obj文件中vt,vn这样的信息,毕竟码力不足。。。
首先是loop细分,关于loop细分的定义在网上很容易找到,这里我就简单的说说。
实验原理:重新网络化对比网络简化可是好些的多。我用的是loop细分的方法写的。首先是找到所有的边,然后每个边上构造一个新的点,这个点的位置坐标是用改边的两个点的坐标和改边连接的两个三角形得另外两个顶点进行确定的:
p1p2p3就是xyz,通过这种方法确定点的位置。然后利用新点的位置更新旧点的位置:
第二个for循环前面的是该点之前位置所占的权重,第二个for循环里面是新点所占的权重。之后再利用新点和更新之后的旧点构造新的边和新的面就行了。
方法比较简单,就不贴代码了:就是枚举每个旧的三角形,然后找到其三个边,然后再找到其三个新点,然后构造出4个新的三角形就可以了。
接下来上代码,首先是头文件:
#include <vector>
#include <string>
using namespace std;
class ObjLoader {
public:
ObjLoader(string filename);//构造函数
void Increase();
private:
vector<vector<float>>vSets;//存放顶点(x,y,z)坐标
vector<vector<int>>fSets;//存放面的三个顶点索引
};
接下来是功能cpp文件,主要是实现类的函数:
#include "ObjLoader.h"
#include <fstream>
#include <iostream>
#include<map>
#include<cmath>
#include<algorithm>
using namespace std;
ObjLoader::ObjLoader(string filename)//这个是实现文件的读取
{
string line;
fstream f;
f.open(filename, ios::in);
if (!f.is_open()) {
cout << "Something Went Wrong When Opening Objfiles" << endl;
}
while (!f.eof()) {
getline(f, line);//拿到obj文件中一行,作为一个字符串
vector<string>parameters;
string tailMark = " ";
string ans = "";
line = line.append(tailMark);//停止
for (int i = 0; i < line.length(); i++) {
char ch = line[i];
if (ch =='//')
{
for (int j = i;;j++)
{
if (line[j+1] == ' ')
{
i = j;
break;
}
}
continue;
}
if (ch != ' ') {
ans += ch;
}
else {
parameters.push_back(ans); //取出字符串中的元素,以空格切分
ans = "";
}
}
//cout << parameters.size() << endl;
if (parameters.size() != 4) {
cout << "the size is not correct 第一个" << endl;
}
else {
if (parameters[0] == "v") { //如果是顶点的话
vector<float>Point;
for (int i = 1; i < 4; i++) { //从1开始,将顶点的xyz三个坐标放入顶点vector
float xyz = atof(parameters[i].c_str());
Point.push_back(xyz);
}
vSets.push_back(Point);
}
else if (parameters[0] == "f") { //如果是面的话,存放三个顶点的索引
vector<int>vIndexSets;
for (int i = 1; i < 4; i++) {
string x = parameters[i];
string ans = "";
for (int j = 0; j < x.length(); j++) { //跳过‘/’
char ch = x[j];
if (ch != '/') {
ans += ch;
}
else {
break;
}
}
int index = atof(ans.c_str());
index = index--;//因为顶点索引在obj文件中是从1开始的,而我们存放的顶点vector是从0开始的,因此要减1
vIndexSets.push_back(index);
}
fSets.push_back(vIndexSets);
}
}
}
cout << "the v num is " << vSets.size() << " the f num is " << fSets.size() << endl;
f.close();
}
void ObjLoader::Increase() {
map<pair<int, int>, pair<int, int> > m;//边与两个三角形
map<int, vector<int> > point;//旧点与新点
map<pair<int, int>, int> bian;//边与新点
vector<pair<int, int> > line;//旧边的集合
vector<vector<int> > newfSets;//新的三角形的集合
map<int, bool> triangle;
int num = vSets.size();
int k = 0;
for (int i = 0;i < fSets.size();i++)
{
pair<int, int> l1, l2, l3;
if(fSets[i][0]>= fSets[i][1])l1 = make_pair(fSets[i][0], fSets[i][1]);
else l1 = make_pair(fSets[i][1], fSets[i][0]);
if (fSets[i][0] >= fSets[i][2])l2 = make_pair(fSets[i][0], fSets[i][2]);
else l2 = make_pair(fSets[i][2], fSets[i][0]);
if (fSets[i][1] >= fSets[i][2])l3 = make_pair(fSets[i][1], fSets[i][2]);
else l3 = make_pair(fSets[i][2], fSets[i][1]);
if (!m.count(l1)) { line.push_back(l1);m[l1].first = i; }else{ m[l1].second = i; }
if (!m.count(l2)) { line.push_back(l2);m[l2].first = i; }else{ m[l2].second = i; }
if (!m.count(l3)) { line.push_back(l3);m[l3].first = i; }else{ m[l3].second = i; }
}
//通过point把与旧点连接的新点保存map<旧点坐标,新点坐标集合>
//新三角形,我先更新完所有的新点,并且每个新点记录关联的边,同时每个关联的边有关联的2个三角形
for (int i = 0;i < line.size();i++)//求新点并且关联旧边
{
int v0, v1, v2, v3;
v0 = line[i].first, v1 = line[i].second;
pair<int, int> l = make_pair(v0, v1);
int f1, f2;
f1 = m[l].first, f2 = m[l].second;
vector<int> h1 = fSets[f1], h2 = fSets[f2];
for (int i = 0;i < h1.size();i++){
if (h1[i] != v0 && h1[i] != v1){
v2 = h1[i];
break;
}
}
for (int i = 0;i < h2.size();i++){
if (h2[i] != v0 && h2[i] != v1){
v3 = h2[i];
break;
}
}
if (v2 < v3)swap(v2, v3);
float p1, p2, p3;
p1 = 3.0 / 8.0 * (vSets[v0][0] + vSets[v1][0]) + 1.0 / 8.0 * (vSets[v3][0] + vSets[v2][0]);
p2 = 3.0 / 8.0 * (vSets[v0][1] + vSets[v1][1]) + 1.0 / 8.0 * (vSets[v3][1] + vSets[v2][1]);
p3 = 3.0 / 8.0 * (vSets[v0][2] + vSets[v1][2]) + 1.0 / 8.0 * (vSets[v3][2] + vSets[v2][2]);
vector<float> po;
po.push_back(p1);po.push_back(p2);po.push_back(p3);
vSets.push_back(po);
bian[line[i]] = vSets.size() - 1;
point[v0].push_back(vSets.size() - 1);
point[v1].push_back(vSets.size() - 1);
}
for (map<int, vector<int> >::iterator it = point.begin();it != point.end();it++)
{
double n = (double)it->second.size();
double belt = 1.0 / n * (5.0 / 8.0 - (3.0 / 8.0 + 1.0 / 4.0 * cos(2.0 * 3.1415926 / n))* (3.0 / 8.0 + 1.0 / 4.0 * cos(2.0 * 3.1415926 / n)));
vSets[it->first][0] = (1 - n * belt) * vSets[it->first][0];
vSets[it->first][1] = (1 - n * belt) * vSets[it->first][1];
vSets[it->first][2] = (1 - n * belt) * vSets[it->first][2];
for (int i = 0;i < it->second.size();i++)
{
vSets[it->first][0] += belt * vSets[it->second[i]][0];
vSets[it->first][1] += belt * vSets[it->second[i]][1];
vSets[it->first][2] += belt * vSets[it->second[i]][2];
}
}
for (int f1 = 0;f1 < fSets.size();f1++)//这个就是求4ge个三角形
{
pair<int, int> theline1 = make_pair(fSets[f1][0], fSets[f1][1]);
int v0 = theline1.first, v1 = theline1.second;
if (!triangle.count(f1))
{
int v2;
for (int i = 0;i < fSets[f1].size();i++)
{
if (fSets[f1][i] != v0 && fSets[f1][i] != v1)
{
v2 = fSets[f1][i];
break;
}
}
if (v0 < v1)swap(v0, v1);
if (v0 < v2)swap(v0, v2);
if (v1 < v2)swap(v2, v1);
pair<int, int> theline2 = make_pair(v0, v2);
pair<int, int> theline3 = make_pair(v1, v2);
pair<int, int> theline4 = make_pair(v0, v1);
int v3 = bian[theline4];
int v4 = bian[theline2];
int v5 = bian[theline3];
vector<int> nf1, nf2, nf3, nf4;
nf1.push_back(v0), nf1.push_back(v3), nf1.push_back(v4);
nf2.push_back(v2), nf2.push_back(v5), nf2.push_back(v4);
nf3.push_back(v1), nf3.push_back(v3), nf3.push_back(v5);
nf4.push_back(v5), nf4.push_back(v3), nf4.push_back(v4);
newfSets.push_back(nf1), newfSets.push_back(nf2), newfSets.push_back(nf3), newfSets.push_back(nf4);
triangle[f1] = 1;
}
}
fSets.clear();
fSets = newfSets;
ofstream out("spot.obj");
cout << "success" << endl;
if (out.fail()) {
cout << "error\n";
}
for (int i = 0;i < vSets.size();i++)
{
out << "v";
for (int j = 0;j < vSets[i].size();j++)
{
out << " " << vSets[i][j];
}
out << endl;
}
for (int i = 0;i < fSets.size();i++)
{
out << "f";
for (int j = 0;j < fSets[i].size();j++)
{
out << " " << fSets[i][j]+1;
}
out << endl;
}
}
接下来是主函数:
#include "ObjLoader.h"
using namespace std;
string filePath ="dragon.obj";
ObjLoader objModel = ObjLoader(filePath);
int main(int argc, char* argv[])
{
int n = 2;
for (int i = 0;i < n;i++)//n是迭代的次数,每次迭代翻4倍差不多
{
objModel.Increase();
}
return 0;
}
展示一下结果:
输入:
输出: