cc 细分 网格细分
简介
pixel的大佬,就是cc细分论文的作者。
wiki的步骤是我见过的比较清晰的版本
Recursive evaluation
Catmull–Clark surfaces are defined recursively, using the following refinement scheme:[1]
Start with a mesh of an arbitrary polyhedron. All the vertices in this mesh shall be called original points.
For each face, add a face point
Set each face point to be the average of all original points for the respective face.
For each edge, add an edge point.
Set each edge point to be the average of the two neighbouring face points and its two original endpoints.
For each face point, add an edge for every edge of the face, connecting the face point to each edge point for the face.
For each original point P, take the average F of all n (recently created) face points for faces touching P, and take the average R of all n edge midpoints for (original) edges touching P, where each edge midpoint is the average of its two endpoint vertices (not to be confused with new "edge points" above). (Note that from the perspective of a vertex P, the number of edges neighboring P is also the number of adjacent faces, hence n). Move each original point to the point
\({\displaystyle {\frac {F+2R+(n-3)P}{n}}}{\displaystyle {\frac {F+2R+(n-3)P}{n}}}\)
This is the barycenter of P, R and F with respective weights (n − 3), 2 and 1.
Connect each new face point to the new edge points of all original edges defining the original face.
Connect each new vertex point to the new edge points of all original edges incident on the original vertex.
Define new faces as enclosed by edges.
The new mesh will consist only of quadrilaterals, which in general will not be planar. The new mesh will generally look smoother than the old mesh.
Repeated subdivision results in smoother meshes. It can be shown that the limit surface obtained by this refinement process is at least \({\displaystyle {\mathcal {C}}^{1}}\mathcal{C}^1\) at extraordinary vertices and \({\displaystyle {\mathcal {C}}^{2}}{\mathcal {C}}^{2}\) everywhere else (when n indicates how many derivatives are continuous, we speak of \({\displaystyle {\mathcal {C}}^{n}}\mathcal{C}^n\) continuity). After one iteration, the number of extraordinary points on the surface remains constant.
The arbitrary-looking barycenter formula was chosen by Catmull and Clark based on the aesthetic appearance of the resulting surfaces rather than on a mathematical derivation, although Catmull and Clark do go to great lengths to rigorously show that the method converges to bicubic B-spline surfaces.[1]
参考
链接代码
http://rosettacode.org/wiki/Catmull–Clark_subdivision_surface#Python
https://blog.csdn.net/McQueen_LT/article/details/106102609
code
虽然 rosettacode 提供了 各种类型的细分代码,但是 个人觉得 都不是特别让人容易懂,使用openmesh重写了部分代码,结果很舒适。暂时有点缺憾,不能处理不封闭的网格。
#ifndef __CC_HPP__
#define __CC_HPP__
#include <OpenMesh/Core/Mesh/PolyMesh_ArrayKernelT.hh>
#include <OpenMesh/Core/IO/MeshIO.hh>
#include <map>
typedef OpenMesh::PolyMesh_ArrayKernelT<> MyMesh;
typedef struct FFACE{
int faceId;
int edgeId1;
int vertexId;
int edgeId2;
}FFACE;
class CC{
private:
MyMesh mesh;
std::map<int, OpenMesh::Vec3d> facePoints;
std::map<int, OpenMesh::Vec3d> edgePoints;
std::map<int, OpenMesh::Vec3d> vertexPoints;
int times;
public:
CC(int _times);
~CC();
void genCube();
void genFacePoint();
void genEdgePoint();
void genVertexPoint();
void connectPoints();
void genMesh(std::string name);
int pipeLine();
};
#endif
/*
* @Author: your name
* @Date: 2020-09-02 13:49:22
* @LastEditTime: 2020-09-03 14:35:38
* @LastEditors: Please set LastEditors
* @Description: In User Settings Edit
* @FilePath: /cc/cc.cc
*/
#include "cc.hpp"
#include <iostream>
#include <unordered_map>
/**
* @description: 构造函数
* @param {type}
* @return {type}
*/
CC::CC(int _times):times(_times){
genCube();
}
/**
* @description: 析构函数
* @param {type}
* @return {type}
*/
CC::~CC() {}
/**
* @description: 一整套流程
* @param {type}
* @return {int} 管线运行是否成功
*/
int CC::pipeLine(){
for(int i=0; i<times; i++){
genFacePoint();
genEdgePoint();
genVertexPoint();
connectPoints();
char a[20];
sprintf(a, "output%d.off", i);
genMesh(a);
// 清空变量
std::cout << "[DEBUG] 迭代了第 " << i << " 次。" << std::endl;
}
return 0;
}
/**
* @description: 生成一个立方体(四边形网格)
* @param {type}
* @return {type}
*/
void CC::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]);
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]);
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]);
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]);
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]);
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]);
face_vhandles.push_back(vhandle[4]);
mesh.add_face(face_vhandles);
}
/**
* @description: 生成所有面点
* @param {type}
* @return {type}
*/
void CC::genFacePoint()
{
facePoints.clear();
for (const auto &fh : mesh.faces())
{
OpenMesh::Vec3d facePoint(0, 0, 0);
int facePointsNumber = 0;
for (const auto &fvh : mesh.fv_range(fh))
{
OpenMesh::DefaultTraits::Point point = mesh.point(fvh);
facePoint += point;
facePointsNumber++;
}
facePoint /= facePointsNumber;
facePoints[fh.idx()] = facePoint;
}
}
/**
* @description: 生成所有的边点暂时不考虑hole 就是全是日子结构的
* @param {type}
* @return {type}
*/
void CC::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)); // 这条(半)边的终点
OpenMesh::FaceHandle fh1 = mesh.face_handle(heh1);
OpenMesh::Vec3d fh1p = facePoints[fh1.idx()]; // 得到面点
OpenMesh::HalfedgeHandle heh = mesh.opposite_halfedge_handle(heh1); // 逆半边
OpenMesh::FaceHandle fh2 = mesh.face_handle(heh);
OpenMesh::Vec3d fh2p = facePoints[fh2.idx()]; // 得到面点
edgePoints[heh1.idx()] = (fh1p + fh2p + pointV + pointW) / 4.0;
}
}
/**
* @description: 生成新的顶点
* For each original point P, take the average F of all n (recently created) face points for faces touching P,
* and take the average R of all n edge midpoints for (original) edges touching P,
* where each edge midpoint is the average of its two endpoint vertices (not to be confused with new "edge points" above).
* (Note that from the perspective of a vertex P, the number of edges neighboring P is also the number of adjacent faces, hence n).
* Move each original point to the point
* @param {type}
* @return {type}
*/
void CC::genVertexPoint()
{
vertexPoints.clear();
// 原始点接触的面的所有的面点的均值
for (auto v_it = mesh.vertices_begin(); v_it != mesh.vertices_end(); v_it++)
{
OpenMesh::Vec3d originPoint = (OpenMesh::Vec3d)mesh.point(*v_it);
OpenMesh::Vec3d facePoint(0, 0, 0);
int faceNumber = 0;
for (auto vf_it = mesh.vf_iter(*v_it); vf_it.is_valid(); ++vf_it)
{ //这个顶点所带有的面迭代器
facePoint += facePoints[(*vf_it).idx()];
faceNumber++;
}
facePoint /= faceNumber;
// 原始点接触的边的中间点的值的均值 * 2
OpenMesh::Vec3d edgePoint(0, 0, 0);
int edgeNumber = 0;
for(auto vv_it = mesh.vv_begin(*v_it); vv_it != mesh.vv_end(*v_it); vv_it++){ // 为啥还有孤立点?
OpenMesh::Vec3d point =(OpenMesh::Vec3d)mesh.point(*vv_it);
edgePoint += (point + originPoint) / 2;
edgeNumber++;
}
if(faceNumber != edgeNumber){
std::cout << "[debug] is not equal f " << faceNumber << " e " << edgeNumber << std::endl;
}
std::cout << "[DEBUG] n " << edgeNumber << std::endl;
edgePoint /= edgeNumber;
OpenMesh::Vec3d newPoint = (edgeNumber - 3.0) / (edgeNumber) * originPoint + facePoint * 1.0 / edgeNumber + 2.0 * edgePoint / edgeNumber;
std::cout << "[DEBUG] oldPoint " << originPoint << " newPoint " << newPoint << std::endl;
vertexPoints[(*v_it).idx()] = newPoint;
}
}
/**
* @description: 连接面点和边点 (要严格的封闭的四边形)
* @param {type}
* @return {type}
*/
void CC::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;
// 再加入所有的要加入的面点
for (const auto &fh : mesh.faces()) {
facePointsHandle.push_back(mesh.add_vertex(MyMesh::Point(facePoints[fh.idx()])));
faceHandle.push_back(fh);
}
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()]));
}
std::vector<std::vector<MyMesh::VertexHandle>> v;
int i = 0;
for (const auto &fh : mesh.faces()){
std::vector<MyMesh::VertexHandle> handlerVector(9);
handlerVector[0] = facePointsHandle[i];
i++;
int pointsNumber = 1;
for (const auto &feh : mesh.fe_range(fh)){
auto fehh = mesh.halfedge_handle(feh, 0);
handlerVector[pointsNumber] = edgesHandle[fehh.idx()];
pointsNumber++;
}
for (const auto &fvh : mesh.fv_range(fh)){
handlerVector[pointsNumber] = vertexHandle[fvh.idx()];
pointsNumber++;
}
v.push_back(handlerVector);
}
// 开始删除面
// for(int i=0; i<faceHandle.size(); i++){
// mesh.delete_face(faceHandle[i], false);
// }
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[0]);
face_vhandles.push_back(handlerVector[1]);
face_vhandles.push_back(handlerVector[5]);
face_vhandles.push_back(handlerVector[2]);
mesh.add_face(face_vhandles);
face_vhandles.clear();
face_vhandles.push_back(handlerVector[0]);
face_vhandles.push_back(handlerVector[2]);
face_vhandles.push_back(handlerVector[6]);
face_vhandles.push_back(handlerVector[3]);
mesh.add_face(face_vhandles);
face_vhandles.clear();
face_vhandles.push_back(handlerVector[0]);
face_vhandles.push_back(handlerVector[3]);
face_vhandles.push_back(handlerVector[7]);
face_vhandles.push_back(handlerVector[4]);
mesh.add_face(face_vhandles);
face_vhandles.clear();
face_vhandles.push_back(handlerVector[0]);
face_vhandles.push_back(handlerVector[4]);
face_vhandles.push_back(handlerVector[8]);
face_vhandles.push_back(handlerVector[1]);
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 CC::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;
}
}
image
附赠Makefile
LOCAL_LIBRARY += -L/home/ling/lee/lib/openmesh/static
LOCAL_LDFLAGS += -lm -lpthread -ldl -lOpenMeshCore -lOpenMeshTools
LOCAL_CFLAGS += -I/home/ling/lee/include -I./include -D OM_STATIC_BUILD
CC := g++ -g
TARGETS1 = genCube
SRCS1 = mian1.cc cc.cc
OBJS1 = $(patsubst %.cc, %.o, $(SRCS1))
CFLAGS += $(LOCAL_CFLAGS)
LDFLAGS += $(LOCAL_LIBRARY) $(LOCAL_LDFLAGS)
$(info $(OBJS))
$(info $(TARGETS))
all: $(TARGETS1)
$(TARGETS1):$(OBJS1)
$(CC) -o $@ $^ $(LDFLAGS) $(CFLAGS)
$(OBJS1): %.o:%.cc
$(CC) -c $< -o $@ $(CFLAGS)
clean :
@rm -rf $(TARGETS1) $(OBJS1)
#.SUFFIXES:
.PHONY : all clean
附赠 main.cc
/*
* @Author: your name
* @Date: 2020-09-02 09:25:40
* @LastEditTime: 2020-09-03 11:09:19
* @LastEditors: Please set LastEditors
* @Description: In User Settings Edit
* @FilePath: /cc/mian1.cc
*/
#include <iostream>
#include <OpenMesh/Core/IO/MeshIO.hh>
#include <OpenMesh/Core/Mesh/PolyMesh_ArrayKernelT.hh>
#include "cc.hpp"
int main(){
CC c(5);
c.pipeLine();
return 0;
}
cc 细分 网格细分的更多相关文章
- 关于3DSMAX中opensubdiv细分功能的笔记
说到建模和细分,估计用过3dsmax的同学就会心有余悸,每次添加"涡轮平滑"或者"网格平滑"之前,都会下意识的进行保存,没有为啥,就是因为太容易使软件挂掉了. ...
- 三维网格细分算法(Catmull-Clark subdivision & Loop subdivision)附源码
下图描述了细分的基本思想,每次细分都是在每条边上插入一个新的顶点,可以看到随着细分次数的增加,折线逐渐变成一条光滑的曲线.曲面细分需要有几何规则和拓扑规则,几何规则用于计算新顶点的位置,拓扑规则用于确 ...
- Unity3d 使用DX11的曲面细分
Unity3d surface Shaderswith DX11 Tessellation Unity3d surface shader 在DX11上的曲面细分 I write this articl ...
- 三维网格细分算法(Catmull-Clark subdivision & Loop subdivision)附源码(转载)
转载: https://www.cnblogs.com/shushen/p/5251070.html 下图描述了细分的基本思想,每次细分都是在每条边上插入一个新的顶点,可以看到随着细分次数的增加,折 ...
- Introduction to 3D Game Programming with DirectX 12 学习笔记之 --- 第十四章:曲面细分阶段
原文:Introduction to 3D Game Programming with DirectX 12 学习笔记之 --- 第十四章:曲面细分阶段 代码工程地址: https://github. ...
- DirectX11 With Windows SDK--33 曲面细分阶段(Tessellation)
前言 曲面细分是Direct3D 11带来的其中一项重要的新功能.它引入了两个可编程着色器阶段以及一个固定的镶嵌处理过程.简单来说,曲面细分技术可以将几何体细分为更小的三角形,并以某种方式把这些新生成 ...
- C++实现网格水印之调试笔记(二)
整理了一下要实现的论文Watermarking 3D Polygonal Meshes in the Mesh Spectral Domain,步骤如下: 嵌入水印 à 提取水印 à 优化(网格细分) ...
- CloudCompare 的简单的使用说明
来自:https://blog.csdn.net/datase/article/details/79797795 File open:打开 save:保存 Global Shift settings: ...
- [转载]John Burkardt搜集的FORTRAN源代码
Over the years, I have collected, modified, adapted, adopted or created a number of software package ...
- Unity3D 引擎基础 C# (数据结构入门) Unity3D 界面 UI(NGUI)(动画系统,导航系统)(委托与事件,常用设计模式)
Geomagic Sculpt 2016.2 Windows Software 11个月前 (01-17) 0评论 Geomagic Sculpt 触觉式三维设计 触碰您的设计使用三维工具做三维设计. ...
随机推荐
- 【work记录:c++web聊天服务器】解决了聊天窗口的问题|修复了"没有区分好友或者群聊的聊天窗口"的bug|修复了"群聊消息undefined"的bug
日期:2025.4.24 学习内容: 解决了聊天窗口的问题 修复了"没有区分好友或者群聊的聊天窗口"的bug 修复了"群聊消息undefined"的bug 个人 ...
- Python—Pytorch学习-RNN(一)
前言 有好几个月没搞神经网络代码了,期间也就是回顾了两边之前的文字. 不料,对nn,cnn的理解反而更深入了-_-!. 修改 <零基础学习人工智能-Python-Pytorch学习(四)> ...
- Docker开启远程守护进程访问
默认情况下,Docker守护进程监听Unix套接字上的连接,以接受来自本地客户端的请求.通过将Docker配置为侦听IP地址和端口以及Unix套接字,可以允许Docker接受来自远程主机的请求.有关此 ...
- web框架实例
s5.py from wsgiref.simple_server import make_server def handle_index(): return [b'<h1>Hell ...
- C#/.NET/.NET Core技术前沿周刊 | 第 37 期(2025年5.1-5.11)
前言 C#/.NET/.NET Core技术前沿周刊,你的每周技术指南针!记录.追踪C#/.NET/.NET Core领域.生态的每周最新.最实用.最有价值的技术文章.社区动态.优质项目和学习资源等. ...
- Flutter对于移动端软件跨平台后台保活的技术浅析
@charset "UTF-8"; .markdown-body { line-height: 1.75; font-weight: 400; font-size: 18px; o ...
- Linux vmstat命令快速入门
vmstat是Virtual Meomory Statistics(虚拟内存统计)的缩写,可对操作系统的虚拟内存.进程.CPU活动进行监控.是对系统的整体情况进行统计,不足之处是无法对某个进程进行深入 ...
- Spring扩展接口-FactoryBean
.markdown-body { line-height: 1.75; font-weight: 400; font-size: 16px; overflow-x: hidden; color: rg ...
- C#/.NET/.NET Core优秀项目和框架2025年5月简报
前言 公众号每月定期推广和分享的C#/.NET/.NET Core优秀项目和框架(每周至少会推荐两个优秀的项目和框架当然节假日除外),公众号推文中有项目和框架的详细介绍.功能特点.使用方式以及部分功能 ...
- ChatMoney:你的短视频脚本制作利器
本文由 ChatMoney团队出品 在当今这个快节奏的数字时代,短视频以其短小精悍.内容丰富的特点迅速崛起,成为大众娱乐和信息传播的重要载体.然而,对于许多创作者而言,如何构思一个引人入胜.富有创意的 ...