简介

Loop 是三角网格常用的细分算法之一. 原理基于二次B样条曲线.

Image

参考链接

https://blog.csdn.net/Hachi_Lin/article/details/90349216

https://www.bilibili.com/video/BV1X7411F744?p=12

https://blog.csdn.net/qq_31804159/article/details/109147352

code

#pragma once
#pragma once
#include "strategy.h"
#include "qwidget.h"
#include <OpenMesh/Core/Mesh/PolyMesh_ArrayKernelT.hh>
#include <OpenMesh/Core/IO/MeshIO.hh>
#include <map> class Loop :public Strategy, public QWidget
{
Q_OBJECT
private:
typedef OpenMesh::PolyMesh_ArrayKernelT<> MyMesh;
MyMesh mesh;
std::map<int, OpenMesh::Vec3d> facePoints;
std::map<int, OpenMesh::Vec3d> edgePoints;
std::map<int, OpenMesh::Vec3d> vertexPoints;
int times;
public:
Loop(Data* data_);
~Loop();
void genCube();
void genFacePoint();
void genEdgePoint();
void genVertexPoint();
void connectPoints();
void genMesh(std::string name);
bool Run();
void getResult();
};
#include "loop_surface_subdivision.h"
#include <iostream>
#include <unordered_map>
#include <QInputDialog> /**
* @description: 构造函数
* @param {type}
* @return {type}
*/
Loop::Loop(Data* data_) : Strategy(data_) {
times = 1;
genCube();
} /**
* @description: 析构函数
* @param {type}
* @return {type}
*/
Loop::~Loop() {} /**
* @description: 一整套流程
* @param {type}
* @return {int} 管线运行是否成功
*/
bool Loop::Run() {
times = QInputDialog::getInt(this, "Surface Mesh", "Please input times",
1, 1, 1000, 1);
for (int i = 0; i <= times; i++) {
if (i == 0) {
char a[20];
sprintf(a, "output%d.off", i);
genMesh(a);
}
else {
genEdgePoint();
genVertexPoint();
connectPoints();
char a[20];
sprintf(a, "output%d.off", i);
genMesh(a);
}
// 清空变量
std::cout << "[DEBUG] 迭代了第 " << i << " 次。" << std::endl;
}
getResult(); return true;
} /**
* @description: 生成一个立方体(四边形网格)
* @param {type}
* @return {type}
*/
void Loop::genCube()
{
MyMesh::VertexHandle vhandle[9];
vhandle[0] = mesh.add_vertex(MyMesh::Point(-1, -1, 1));
vhandle[1] = mesh.add_vertex(MyMesh::Point(1, -1, 1));
vhandle[2] = mesh.add_vertex(MyMesh::Point(1, 1, 1));
vhandle[3] = mesh.add_vertex(MyMesh::Point(-1, 1, 1));
vhandle[4] = mesh.add_vertex(MyMesh::Point(-1, -1, -1));
vhandle[5] = mesh.add_vertex(MyMesh::Point(1, -1, -1));
vhandle[6] = mesh.add_vertex(MyMesh::Point(1, 1, -1));
vhandle[7] = mesh.add_vertex(MyMesh::Point(-1, 1, -1)); std::vector<MyMesh::VertexHandle> face_vhandles;
face_vhandles.push_back(vhandle[0]);
face_vhandles.push_back(vhandle[1]);
face_vhandles.push_back(vhandle[2]);
mesh.add_face(face_vhandles);
face_vhandles.clear();
face_vhandles.push_back(vhandle[0]);
face_vhandles.push_back(vhandle[2]);
face_vhandles.push_back(vhandle[3]);
mesh.add_face(face_vhandles);
face_vhandles.clear(); face_vhandles.push_back(vhandle[7]);
face_vhandles.push_back(vhandle[6]);
face_vhandles.push_back(vhandle[5]);
mesh.add_face(face_vhandles);
face_vhandles.clear();
face_vhandles.push_back(vhandle[7]);
face_vhandles.push_back(vhandle[5]);
face_vhandles.push_back(vhandle[4]);
mesh.add_face(face_vhandles);
face_vhandles.clear(); face_vhandles.push_back(vhandle[1]);
face_vhandles.push_back(vhandle[0]);
face_vhandles.push_back(vhandle[4]);
mesh.add_face(face_vhandles);
face_vhandles.clear();
face_vhandles.push_back(vhandle[1]);
face_vhandles.push_back(vhandle[4]);
face_vhandles.push_back(vhandle[5]);
mesh.add_face(face_vhandles);
face_vhandles.clear(); face_vhandles.push_back(vhandle[2]);
face_vhandles.push_back(vhandle[1]);
face_vhandles.push_back(vhandle[5]);
mesh.add_face(face_vhandles);
face_vhandles.clear();
face_vhandles.push_back(vhandle[2]);
face_vhandles.push_back(vhandle[5]);
face_vhandles.push_back(vhandle[6]);
mesh.add_face(face_vhandles);
face_vhandles.clear(); face_vhandles.push_back(vhandle[3]);
face_vhandles.push_back(vhandle[2]);
face_vhandles.push_back(vhandle[6]);
mesh.add_face(face_vhandles);
face_vhandles.clear();
face_vhandles.push_back(vhandle[3]);
face_vhandles.push_back(vhandle[6]);
face_vhandles.push_back(vhandle[7]);
mesh.add_face(face_vhandles);
face_vhandles.clear(); face_vhandles.push_back(vhandle[0]);
face_vhandles.push_back(vhandle[3]);
face_vhandles.push_back(vhandle[7]);
mesh.add_face(face_vhandles);
face_vhandles.clear();
face_vhandles.push_back(vhandle[0]);
face_vhandles.push_back(vhandle[7]);
face_vhandles.push_back(vhandle[4]);
mesh.add_face(face_vhandles);
} /**
* @description: 考虑边上和内部
* @param {type}
* @return {type}
*/
void Loop::genEdgePoint()
{
edgePoints.clear();
for (auto e_it = mesh.edges_begin(); e_it != mesh.edges_end(); ++e_it)
{
// 得到边所代表的半边
OpenMesh::HalfedgeHandle heh1 = mesh.halfedge_handle(*e_it, 0); // 默认一个方向的半边
OpenMesh::Vec3d edgePoint(0, 0, 0);
int edgePointsNumber = 0;
OpenMesh::DefaultTraits::Point pointV = mesh.point(mesh.from_vertex_handle(heh1)); // 这条(半)边的起点
OpenMesh::DefaultTraits::Point pointW = mesh.point(mesh.to_vertex_handle(heh1)); // 这条(半)边的终点
if (mesh.is_boundary(heh1)) { // 如果这条边处于边界
edgePoints[heh1.idx()] = 1 / 2.0 * (pointV + pointW);
}
else {
OpenMesh::FaceHandle fh1 = mesh.face_handle(heh1);
OpenMesh::HalfedgeHandle heh = mesh.opposite_halfedge_handle(heh1);
OpenMesh::FaceHandle fh2 = mesh.face_handle(heh);
std::vector<OpenMesh::VertexHandle> ss1;
// 得到这两个面的所有顶点
for (auto fv_it : mesh.fv_range(fh1)) {
if (fv_it.idx() != mesh.from_vertex_handle(heh1).idx() &&
fv_it.idx() != mesh.to_vertex_handle(heh1).idx())
ss1.push_back(fv_it);
}
for (auto fv_it : mesh.fv_range(fh2)) {
if (fv_it.idx() != mesh.from_vertex_handle(heh1).idx() &&
fv_it.idx() != mesh.to_vertex_handle(heh1).idx() )
ss1.push_back(fv_it);
}
if (ss1.size() != 2) {
std::cout << "[ERROR] " << ss1.size() << std::endl;
}
edgePoints[heh1.idx()] = 3.0 / 8.0 * (pointV + pointW) + 1.0 / 8.0 * (mesh.point(ss1[0]) + mesh.point(ss1[1]));
}
}
} /**
* @description: 生成新的顶点
* @param {type}
* @return {type}
*/
void Loop::genVertexPoint()
{
vertexPoints.clear();
// 原始点接触的面的所有的面点的均值
for (auto v_it = mesh.vertices_begin(); v_it != mesh.vertices_end(); v_it++)
{
if (mesh.is_boundary(v_it)) { // 如果这个顶点处于边界
OpenMesh::Vec3d point(0, 0, 0);
for (auto vv_it = mesh.vv_begin(v_it); vv_it.is_valid(); vv_it++) {
if (mesh.is_boundary(vv_it)) {
point += mesh.point(vv_it);
}
}
vertexPoints[(*v_it).idx()] = 3.0 / 4.0 * mesh.point(v_it) + 1.0 / 8.0 * (point);
}
else {
// 计算度 n, 度
int n = 0;
for (auto vf_it = mesh.vv_begin(v_it); vf_it.is_valid(); ++vf_it) {
n++;
}
// 计算β
double beta = 1.0 / n * (5.0 / 8.0 - pow(3.0 / 8.0 + 1.0 / 4.0 * cos(2 * M_PI / n), 2) );
OpenMesh::Vec3d point(0, 0, 0);
for (auto vv_it = mesh.vv_begin(v_it); vv_it.is_valid(); ++vv_it) {
point += beta * mesh.point(vv_it);
}
vertexPoints[(*v_it).idx()] = (1 - n * beta) * mesh.point(v_it) + point;
}
}
} /**
* @description: 连接面点和边点
* @param {type}
* @return {type}
*/
void Loop::connectPoints() {
if (!mesh.has_vertex_status()) mesh.request_vertex_status();
if (!mesh.has_face_status()) mesh.request_face_status();
if (!mesh.has_edge_status()) mesh.request_edge_status();
// 先将旧顶点移动到新顶点
std::unordered_map<int, OpenMesh::VertexHandle> vertexHandle;
for (auto v_it = mesh.vertices_begin(); v_it != mesh.vertices_end(); v_it++) {
mesh.set_point(*v_it, (OpenMesh::DefaultTraits::Point)vertexPoints[(*v_it).idx()]);
vertexHandle[(*v_it).idx()] = *v_it;
}
std::vector<MyMesh::VertexHandle> facePointsHandle;
std::vector<MyMesh::FaceHandle> faceHandle;
std::unordered_map<int, MyMesh::VertexHandle> edgesHandle;
// 再加入所有的要加入的边点
for (const auto& eh : mesh.edges()) {
edgesHandle[mesh.halfedge_handle(eh, 0).idx()] = mesh.add_vertex(MyMesh::Point(edgePoints[mesh.halfedge_handle(eh, 0).idx()]));
}
for (const auto& fh : mesh.faces()) {
faceHandle.push_back(fh);
}
std::vector<std::vector<MyMesh::VertexHandle>> v;
for (const auto& fh : mesh.faces()) {
std::vector<MyMesh::VertexHandle> handlerVector(6);
int pointsNumber = 0;
for (const auto& fvh : mesh.fv_range(fh)) {
handlerVector[pointsNumber] = vertexHandle[fvh.idx()];
pointsNumber++;
}
for (const auto& feh : mesh.fe_range(fh)) {
auto fehh = mesh.halfedge_handle(feh, 0);
handlerVector[pointsNumber] = edgesHandle[fehh.idx()];
pointsNumber++;
}
v.push_back(handlerVector);
} for (int i = 0; i < v.size(); i++) {
mesh.delete_face(faceHandle[i], true);
std::vector<MyMesh::VertexHandle> handlerVector;
for (int j = 0; j < v[i].size(); j++) {
handlerVector.push_back(v[i][j]);
} std::vector<MyMesh::VertexHandle> face_vhandles;
face_vhandles.push_back(handlerVector[3]);
face_vhandles.push_back(handlerVector[0]); face_vhandles.push_back(handlerVector[4]);
mesh.add_face(face_vhandles);
face_vhandles.clear(); face_vhandles.push_back(handlerVector[4]);
face_vhandles.push_back(handlerVector[1]); face_vhandles.push_back(handlerVector[5]);
mesh.add_face(face_vhandles);
face_vhandles.clear(); face_vhandles.push_back(handlerVector[3]);
face_vhandles.push_back(handlerVector[4]);
face_vhandles.push_back(handlerVector[5]);
mesh.add_face(face_vhandles);
face_vhandles.clear(); face_vhandles.push_back(handlerVector[2]);
face_vhandles.push_back(handlerVector[3]);
face_vhandles.push_back(handlerVector[5]);
mesh.add_face(face_vhandles);
face_vhandles.clear();
} mesh.garbage_collection();
if (mesh.has_vertex_status()) mesh.release_vertex_status();
if (mesh.has_face_status()) mesh.release_face_status();
if (mesh.has_edge_status()) mesh.release_edge_status();
} /**
* @description: 输出新网格
* @param {string} name 文件名称 默认 output.obj
* @return {type}
*/
void Loop::genMesh(std::string name) {
if (name == "") {
name = "output.off";
}
try {
if (!OpenMesh::IO::write_mesh(mesh, name)) {
std::cerr << "Cannot write mesh to file 'output.off'" << std::endl;
return;
}
}
catch (std::exception& e) {
std::cerr << e.what() << std::endl;
return;
} } void Loop::getResult() {
//if (getData()->edges.size() == 0) {
// getData()->edges.push_back(std::vector<V3f>());
//}
getData()->edges.push_back(std::vector<V3f>());
for (auto e_it = mesh.edges_begin(); e_it != mesh.edges_end(); ++e_it)
{
// 得到边所代表的半边
OpenMesh::HalfedgeHandle heh1 = mesh.halfedge_handle(*e_it, 0); // 默认一个方向的半边
OpenMesh::Vec3d edgePoint(0, 0, 0);
int edgePointsNumber = 0;
OpenMesh::DefaultTraits::Point pointV = mesh.point(mesh.from_vertex_handle(heh1)); // 这条(半)边的起点
OpenMesh::DefaultTraits::Point pointW = mesh.point(mesh.to_vertex_handle(heh1)); // 这条(半)边的终点
getData()->edges[0].push_back({ pointV[0], pointV[1], pointV[2] });
getData()->edges[0].push_back({ pointW[0], pointW[1], pointW[2] });
}
}

算法实现请参照

https://blog.csdn.net/qq_31804159/article/details/109147352

写的很清晰了

参考链接2

https://github.com/icemiliang/loop_subdivision/blob/master/src/LOOP.cpp

http://www.cs.cmu.edu/afs/cs/academic/class/15462-s14/www/lec_slides/Subdivision.pdf

TIPS



可以看到有一个 n == 3的时候的取值, 但是Loop大佬的毕业论文中并没有发现 神秘的数字 3/16 ?? 但是也在好几篇文章中看到了这个参数.



应该是Loop毕业论文中没有提到这个参数, 是其他人加的. 也就是可加可不加??

Loop 三角网格细分基于openmesh的更多相关文章

  1. 三角网格上的寻路算法Part.1—Dijkstra算法

    背景 最近在研究中产生了这样的需求:在三角网格(Mesh)表示的地形图上给出两个点,求得这两个点之间的地面距离,这条距离又叫做"测地线距离(Geodesic)".计算三角网格模型表 ...

  2. 三角网格(Triangle Mesh)的理解

    最简单的情形,多边形网格不过是一个多边形列表:三角网格就是全部由三角形组成的多边形网格.多边形和三角网格在图形学和建模中广泛使用,用来模拟复杂物体的表面,如建筑.车辆.人体,当然还有茶壶等.图14.1 ...

  3. bullet物理引擎与OpenGL结合 导入3D模型进行碰撞检测 以及画三角网格的坑

    原文作者:aircraft 原文链接:https://www.cnblogs.com/DOMLX/p/11681069.html 一.初始化世界以及模型 /// 冲突配置包含内存的默认设置,冲突设置. ...

  4. 三角网格上的寻路算法Part.2—A*算法

    背景 继上一篇三角网格Dijkstra寻路算法之后,本篇将继续介绍一种更加智能,更具效率的寻路算法-A*算法,本文将首先介绍该算法的思想原理,再通过对比来说明二者之间的相同与不同之处,然后采用类似Di ...

  5. 基于上三角变换或基于DFS的行(列)展开的n阶行列式求值算法分析及性能评估

    进入大一新学期,看完<线性代数>前几节后,笔者有了用计算机实现行列式运算的想法.这样做的目的,一是巩固自己对相关概念的理解,二是通过独立设计算法练手,三是希望通过图表直观地展现涉及的两种算 ...

  6. 关于TRIANGLE二维三角网格生成器在windows下的配置说明

    近期须要用到三角网格生成的一些东西,所以就把TRIANGLE这个库编译了一下,发现编译过程还是略微有些纠结,于是就想到写下来.希望以后有些童鞋看到少走一些弯路. 首先很感谢eryar的帮助,很感谢! ...

  7. 三角网格上的寻路算法Part.1—Dijkstra算法 等

    http://www.cnblogs.com/chnhideyoshi/p/AStar.html

  8. PCL: 根据几何规则的曲面剖分-贪婪法表面重建三角网格

    点云场景中进行物体识别,使用全局特征的方法严重依赖于点云分割,难以适应杂乱场景.使用局部特征,即对点云进行提取类似于3D SURF.ROPS之类的局部特征,需要寻找离散点云块的局部显著性. 点云的基本 ...

  9. VAO VBO IBO大乱炖

    最近对程序中绘制卡顿的问题忍无可忍,终于决定下手处理了.程序涉及的绘制比较多,除了点.线.三角形.多边形.圆柱体之外,还有自组格式模型.开始想全部采用显示列表优化,毕竟效率最高,虽然显示列表存在编译之 ...

  10. 三维网格细分算法(Catmull-Clark subdivision & Loop subdivision)附源码

    下图描述了细分的基本思想,每次细分都是在每条边上插入一个新的顶点,可以看到随着细分次数的增加,折线逐渐变成一条光滑的曲线.曲面细分需要有几何规则和拓扑规则,几何规则用于计算新顶点的位置,拓扑规则用于确 ...

随机推荐

  1. Nginx+Windows搭建域名访问环境, 由nginx --> 网关 ---> 服务

    1).修改windows hosts文件改变本地域名映射,将gulimall.com映射到虚拟机ip 2).修改nginx的根配置文件nginx.conf,将upstream映射到我们的网关服务 up ...

  2. 🎀GitHub Pages静态文件发布

    简介 GitHub Pages是GitHub提供的一项服务,允许用户和组织从存储库中的静态文件创建和托管网站.这些静态文件可以是HTML.CSS.JavaScript文件或任何其他可以在浏览器中直接渲 ...

  3. Navicat Premium 16激活教程(NavicatCracker)

    1.安装Navicat Premium 16 (注意版本,这里以此版本为例):并下载激活工具 1.1.Navicat Premium 下载路径: http://www.navicat.com.cn/d ...

  4. 判断返回值长度(比如是否为空),执行后续步骤(if..else、len的用法)

    爬基金数据,净值因涨跌不同,对应的元素路径也不会一样 比如当天是涨的时候,涨跌元素的class信息为"<span class="fix_dwjz  bold ui-color ...

  5. django笔记(3)-数据库操作

    一:路由系统    url    1.url(r'^index/', views.index),url(r'^home/',views.Home.as_view()), 一个url对应一个函数或一个类 ...

  6. 鸿蒙NEXT(四):后台任务的智能调度策略 — 延迟任务管理解析

    @charset "UTF-8"; .markdown-body { line-height: 1.75; font-weight: 400; font-size: 15px; o ...

  7. 工具:Prisms:漏洞扫描器,棱镜开源版

    Prism X 集资产发现.指纹识别.弱密码检测.漏洞验证于一体,采用模块化 YAML 插件策略配置,实现与真实攻击链高度相似的 PoC 验证机制. 跨平台和轻量级设计:支持多种操作系统,易于部署和使 ...

  8. TVM:使用自动调度优化算子

    与基于模板的AutoTVM不同(会依赖手动模板定义搜索空间),自动调度器不需要任何模板.用户只需要编写计算声明,而不需要任何调度命令或模板.自动调度器可以自动生产一个大的搜索空间,并在空间中找到一个好 ...

  9. Seata源码—6.Seata AT模式的数据源代理

    大纲 1.Seata的Resource资源接口源码 2.Seata数据源连接池代理的实现源码 3.Client向Server发起注册RM的源码 4.Client向Server注册RM时的交互源码 5. ...

  10. B1061 判断题 (15 分)

    描述 判断题的评判很简单,本题就要求你写个简单的程序帮助老师判题并统计学生们判断题的得分. 输入格式: 输入在第一行给出两个不超过 100 的正整数 N 和 M,分别是学生人数和判断题数量.第二行给出 ...