精华内容
下载资源
问答
  • 【笔记】Loop曲面细分算法c++实现

    千次阅读 2019-01-12 20:03:30
    基于半边数据结构实现了Loop曲面细分算法。   二、思路 1、Loop算法简述 Loop细分是一种三角网络的细分法则。算法本身挺简单的,主要就是把1个旧三角面片分成4个新三角面片,涉及到半边数据结构操作的部...

    一、概述
    二、思路
           1、Loop算法简述
           2、更新策略
           3、三角面细分操作
           4、操作流程
    三、数据结构
    四、参考资料


    一、概述

    上课作业。基于半边数据结构实现了Loop曲面细分算法。

     

    二、思路

    1、Loop算法简述

    Loop细分是一种三角网络的细分法则。算法本身挺简单的,主要就是把1个旧三角面片分成4个新三角面片,涉及到半边数据结构操作的部分有点麻烦。具体:每次细分,每条边上计算插入一个新顶点,同时每个原始顶点更新位置。边界边/点和非边界边/点按不同策略插入/更新坐标。

     

    2、更新策略

    每条边上新插入的点计算坐标后暂时存储在边结构的newPos变量中,每个旧顶点计算完更新后顶点位置后暂时存储在顶点结构的newPos变量中。

    更新策略如下图所示,图上为非边界情况的更新策略,图下为边界情况的更新策略。

    顶点更新策略

     

    3、三角面细分操作

    采用如下策略进行三角面的划分:(更详细说明见参考资料链接第二条)

    • Split:对每一条旧边执行split操作,可按任意顺序执行。具体操作为:按照该边中存储的前一步已计算好的新顶点的坐标将该边断成两条边,并插入2条边连接新顶点与所处三角形的对角顶点(如果该边是边界,则新插入的边为1条)。
    • Flip:对连接新旧顶点的新插入的边(新插入——不是由断裂原有边形成)进行flip操作。

     

    4、操作流程

    • 标记原始mesh的所有顶点和边。
    • 按策略计算每条边上新插入的顶点坐标,数据暂存在边结构中。
    • 按策略计算每个旧顶点更新后的位置,数据暂存在顶点结构中。
    • 对每条旧边执行split操作。PS:要避免split新插入的边
    • 对特定边执行flip操作。PS:注意特定边的要求
    • 将暂存在顶点结构中的顶点更新后坐标,替换到顶点坐标中去。

     

    三、数据结构

    这部分主要是半边数据结构。之前实现欧拉操作的时候已经写过一遍,这次写起来还算顺手。区别于实现miniCAD的欧拉操作中用到的半边数据结构,本次用到的半边在层级结构上去掉了Loop那一层(此Loop非彼Loop,欧拉操作中为Solid--Face--Loop--Halfedge--Vertex + Edge--Halfedge)。

    因为半边数据结构中各种面、边、点结构指来指去,为了防止内存泄漏用了智能指针。但基本都是自己瞎瘠薄用,总觉得自己用得不伦不类的,所以仅供参考。

    • 顶点结构
    class Vertex
    {
    public:
    	Point3d pos;			// 顶点坐标
    	Point3d newPos;			// 更新后顶点坐标
    	bool ifCalNewPos = false;		// 用于区分新旧顶点
    	bool isOnBoundary = false;	// 用于判断是否位于边界
    	Index idxForSave;		// 用于新mesh写出
    
    public:
    	Vertex(Point3d& p) : pos(p) {}
    };
    • 半边结构
    class Halfedge
    {
    public:
    	shared_ptr<Vertex> v;
    	weak_ptr<Halfedge> twin;
    	weak_ptr<Halfedge> next;
    	weak_ptr<Halfedge> prev;
    	weak_ptr<Edge> e;
    	weak_ptr<Face> f;
    	
    public:
    	Halfedge() {}
    	Halfedge(shared_ptr<Vertex>&  _v) {
    		v = _v;
    	}
    };
    • 边结构
    class Edge
    {
    public:
    	Index ID;
    	shared_ptr<Halfedge> he1;
    	shared_ptr<Halfedge> he2;
    	Point3d newPos;			// 用于存储新顶点的坐标
    	bool ifCalNewPos = false;		// 用于判断是否计算了新顶点
    	bool ifNew = false;		// 用于判断是否为新插入的边
    	bool isBoundary = false;		// 用于判断是否为边界
    
    public:
    	Edge() {
    		static Index id = 0;
    		ID = id++;
    	}
    };
    • 面结构
    class Face
    {
    public:
    	shared_ptr<Halfedge> he;
    	Point3d normal;
    	bool ifNeedDelete = false;
    
    public:
    	Face(){}
    	Face(shared_ptr<Halfedge>& _he) {
    		he = _he;
    	}
    };

     

    四、参考资料

    强烈推荐链接2里的Scotty3D项目,虽然Scotty3D本身是个空项目,但是整个系统都搭建起来了,代码里有大量的TODO注释,还有wiki里各种生动形象且详细到令人发指的知识点介绍。半边数据结构也有介绍(虽然我是按照自己的理解瞎瘠薄写的)。看着cmu462/Scotty3D这个项目,心里满满都是羡慕qaq

    展开全文
  • 半边结构参考:【图形学】Loop细分算法及半边结构实现(C++) 原理部分:GAMES101 实现的功能 仅显示顶点,边或者面的切换,空格键切换 loop subdivision 细分,键盘右键→ 细分后保存同目录下的obj文件,按下q...

    代码基于LearnOpenGL的模型导入篇章的代码模板实现。

    OpenGL 3.3
    VS2019

    代码:https://github.com/LoserFight/LoopSubdivision

    参考:

    半边结构参考:【图形学】Loop细分算法及半边结构实现(C++)

    原理部分:GAMES101

    实现的功能

    • 仅显示顶点,边或者面的切换,空格键切换

    • loop subdivision 细分,键盘右键→

    • 细分后保存同目录下的obj文件,按下q键

    • 支持二进制stl和obj格式的导入


    • wasd移动摄像机,配合鼠标移动视角

    • 按住键盘上的X Y或Z,上下移动鼠标可以旋转模型

    • ./xxx.exe 模型的目录/模型 可以打开指定模型

    结果截图

    细分:

    image-20210827102338526

    导出的模型:

    image-20210827102459708

    仅显示顶点:

    image-20210827102541266

    实现的思路

    因为Loop细分需要一个顶点周围点的信息,所以选择了能较容易得到周围点、边、面的半边数据结构存储存储三角形网格。

    什么是半边数据结构?

    一般的3D模型文件会包含里面所有点或者面的相关信息,半边数据结构很巧妙,每个三角形可以用用三个顺时针或逆时针的向量边表示,因为是单向所以是半边。
    一个半边数据结构的结构体应该包含

    1. 该半边指向的顶点
    2. 该半边的三角形的下一个半边
    3. 与该半边相反的半边,即相邻三角形的一条半边
      具体见文章开头地址的源码

    半边数据结构的建立

    基本思路是先导入需要的顶点数据,再用三角形的三个顶点构造面和半边。

    如下所示的face构造,已经完成了必要顶点vertexs[3]的导入,在这个函数中将三条半边用next连接起来,实际上这个操作才是确定了一个面的三个面和三个顶点的所有信息,这步操作后,只需要一个半边或一个顶点就能得到所有关于三角形的数据。

    //输入一个三角形的三个顶点数据,生成半边和面
    Face* TriMesh::createFace(Vert* vertexs[3]) {
    	Face* face = new Face();
    	HalfEdge* edges[3];
    	//建立一个三角形的半边
    	for (int i=0; i < 3; i++) {
    		edges[i] = createEdge(vertexs[i % 3], vertexs[(i + 1) % 3]);
    		if (edges[i] == NULL) {
    			std::cout << "Create Face went wrong" << std::endl;
    			return NULL;
    		}
    
    	}
    	//将对边通过next连接起来,并和该face绑定
    	for (int i = 0; i < 3; i++) {
    		edges[i]->next = edges[(i + 1) % 3];
    		edges[i]->face = face;
    		m_edges.push_back(edges[i]);
    	}
    
    	//给面赋值任意一个半边
    	face->halfEdge = edges[0];
    	m_faces.push_back(face);
    	return face;
    }
    

    举个例子

    下面是一个输入顶点地址,返回一个从顶点出发的半边vector

    std::vector<HalfEdge*> TriMesh::getEdgesFromVertex(const Vert* vertex) {
    	std::vector<HalfEdge*> result;
    	HalfEdge* he = vertex->halfEdge;//he为从vertex出发的半边
    	HalfEdge* temp = he;
    	do {
    		if (temp->isBoundary) {
    			result.push_back(temp);
    			break;
    		}
    		result.push_back(temp);
    	} while (temp != he);
    	
    	if (temp->isBoundary) {
    		temp = he->next->next;
    		//换个方向
    		std::reverse(result.begin(), result.end());
    		do {
    			if (temp->isBoundary || temp == NULL) {
    				break;
    			}
    			result.push_back(temp->opposite);
    			temp = temp->opposite->next->next;
    
    		} while (temp->vert->id == vertex->id);//true
    		//换回来
    		std::reverse(result.begin(), result.end());
    
    	}
    	
    	return result;
    
    }
    

    image-20210901024101076

    可以看这张图,输入P,应该输出V0,V1,V2,V3.

    文件导入

    obj格式导入较容易,因为文件内就是用点的引索表示面。

    stl就有点复杂,因为它表示 面时候是直接给出了三个顶点的坐标,有些点是重复的。

    所以我建立了一个顶点坐标到顶点引索的表。当这个顶点不存在表内,说明是新的点,构造半边结构的点,否则返回已经存在表内对应顶点的索引值。

    //model.h
    
    //哈希操作
    struct VKeyHashFuc 
        {
            std::size_t operator()(const glm::vec3& key) const
            {
                return std::hash<float>()(key.x) + 
                    std::hash<float>()(key.y)+ 
                    std::hash<float>()(key.z);
            }
        };
    
    int loadStl(string const& path) {
            ifstream targetStl(path,ios::in|ios::binary);
            string line;
            char name[80];
            int v_id = 0;
            unsigned int triangles;
            //float normal;
        //声明一个哈希表
            std::unordered_map<glm::vec3, int, VKeyHashFuc> hashmap_vid;
            if (!targetStl) {
                cout << "Open .stl went wrong" << endl;
                return 0;
            }
            targetStl.read(name, 80);
            targetStl.read((char*)&triangles, sizeof(unsigned int));
            if (triangles == 0)
                return -1;
            for (unsigned int i = 0; i < triangles; i++) {
                float XYZ[12];//4*3
                int face[3];
                targetStl.read((char*)XYZ, 12 * sizeof(float));
                for (int j = 1; j < 4; j++) {
                    glm::vec3 v = { XYZ[j * 3],XYZ[j * 3 + 1],XYZ[j * 3 + 2] };
                    if (hashmap_vid.find(v) != hashmap_vid.end()) {
                        //顶点已经存在
                        face[j - 1] = hashmap_vid[v];
                    }
                    else {//全新顶点数据,更新哈希表,将索引Vid存入面j,这样通过哈希表和face[j]就能寻找到对应的顶点的xyz坐标。
                        hashmap_vid[v] = v_id;
                        face[j - 1] = v_id;
                        TMeshOri->createVertex(v, v_id++); 
                        
                    
                    }
                }
                //生成面
                Vert* ve[3];
                auto& mV = TMeshOri->Vertexs();
                ve[0] = mV[face[0]];
                ve[1] = mV[face[1]];
                ve[2] = mV[face[2]];
                TMeshOri->createFace(ve);
    
                targetStl.read((char*)XYZ, 2);
            }
            targetStl.close();
    
            TMeshOri->createBoundary();
            TMeshOri->calculateNormal();
    
            this->meshes.push_back(TriToMesh(TMeshOri));
            return 0;
    
            
    
        }
    

    这样就可以像obj一样构建半边结构了。

    细分

    先对旧点遍历,得到每个点周围的顶点,乘上相应系数得到新的顶点位置,加入新的半边结构内

    在对旧边遍历,得到新增点的位置,加入新的半边结构中

    对旧面遍历,用之前已经插入的新顶点将每个面拆成4个小面。

    这时候就能体现出半边数据结构的优点了,很轻松就能得到周围点的信息。

    image-20210901024902345

    image-20210901024948556

    不过本文中,关于Old Vertex 用的是Loop最初提出用三角函数的系数,

    如果是边节上的新点或者旧点,计算方法完全不一样

    系数mask

    ...		
        //以旧点为例
    		if (!oriVertexs[i]->isBoundary) {
    			auto neighborV = ori->getNeighborVertexs(oriVertexs[i]);
    			int n = neighborV.size();
    			float beta = (5.0 / 8.0 - pow(3.0 / 8.0 + 1.0 / 4.0 * std::cos(2.0 * My_PI / n), 2.0)) / n;
    			for (int j=0; j < neighborV.size(); j++) {
    				newPos += neighborV[j]->vcoord;
    
    			}
    			//计算even points
    			newPos = newPos * beta;
    			newPos += oriVertexs[i]->vcoord * (1 - n * beta);
    
    		}
    		else {
    			auto BoundaryNV=ori->getBoundaryNeighborVertexs(oriVertexs[i]);
    			newPos = 0.125f * (BoundaryNV[0]->vcoord + BoundaryNV[1]->vcoord) + 0.75f * oriVertexs[i]->vcoord;
    		}
    ...
    

    再生成所有新点和更新所有旧点时,这些新生成本质上是新加入的点,可以像半边数据结构的建立一样构造loop Subdivision后的点。

    新的法线

    https://pbr-book.org/3ed-2018/Shapes/Subdivision_Surfaces

    生成新的法线用的方式是从这个网站得到,细分也很全,前文基本上也参照了其中内容

    每次细分后, 都要根据新的顶点和面生成法线,这个法线和原格式的法线很有可能不一样。

    image-20210827104257835

    不过我算的时候S X T得到的刚好相反,改成了T X S。

    三种显示

    对mesh类和model类,增加了相应的图元绘制,见model.h和mesh.h。

    同时还要把半边结构转化成对应的数据格式,如网格画线,只需要传入线段对应顶点。如下

    //Line
    static Mesh TriToLine(TriMesh* T) {
            vector<Vertex> vertexs;
            vector<unsigned int> ind;
    
            auto& mv = T->Vertexs();
    
            for (int i = 0; i < mv.size(); i++) {
                Vertex newP;
                newP.Position = mv[i]->vcoord;
                newP.Normal = mv[i]->ncoord;
                vertexs.push_back(newP);
            }
    
            auto& mf = T->HalfEdges();
    
            for (int i = 0; i < mf.size(); i++) {
                HalfEdge* he = mf[i];
                ind.push_back(he->vert->id);
                ind.push_back(he->next->next->vert->id);
            }
            vector<Texture>      tex;
            return Mesh(vertexs, ind, tex);
        
        }
    
    //Mesh
     static Mesh TriToMesh(TriMesh* T) {
            vector<Vertex> vertexs;
            vector<unsigned int> ind;
            auto& mv = T->Vertexs();
    
            for (int i = 0; i < mv.size(); i++) {
                Vertex newP;
                newP.Position=mv[i]->vcoord;
                newP.Normal = mv[i]->ncoord;
                vertexs.push_back(newP);
            }
    
           // unsigned int vi = 0;
            auto& mf = T->Faces();
            for (int i = 0; i < mf.size(); i++) {
                Face* f = mf[i];
                HalfEdge* he = f->halfEdge;
                do {
                    ind.push_back(he->vert->id);
                    he = he->next;
    
                
                } while (he != f->halfEdge);
    
            }
                 vector<Texture>      tex;
            return Mesh(vertexs, ind, tex);
    
        }
    //不需要画点,因为如果画点,只需要不加索引就可以了
    
         
    
    展开全文
  • loop细分曲面算法

    2020-10-21 05:01:05
    loop细分曲面算法
  • 曲面细分与曲面简化1 曲面细分(Mesh Subdivision)1.1 Loop细分(Loop Subdivision)1.2 Catmull-Clark细分(Catmull-Clark Subdivision)2 曲面简化(Mesh Smplication)总结Reference 曲面细分与曲面简化分别是什么,以及...

    (本篇文章同步发表于知乎专栏:https://zhuanlan.zhihu.com/p/144400261 欢迎三连关注)

    摘要

    曲面细分与曲面简化分别是什么,以及为什么需要这些技术呢?
    其实这二种技术所做的事情从名字就能看出一二:

    曲面细分是指将一个模型的面合理的分成更多小的面,从而提升模型精度,提高渲染效果
    曲面简化是指将一个模型的面合理的合成更少的面,从而降低模型精度,为特定情形下提供使用(如LOD技术)

    明白二者定义及目的之后,来看两张示意图提供intuitive的理解


    当然除了细分与简化之外,还有另外一种同属一类的操作叫做曲面规则化(Mesh Regularization)其所作的便是将三角面都变的尽可能相同,从而也达到提升模型效果的目的,对于该类技术本文不做详解

    接下来,我们就来看看这细分与简化究竟是怎么做到的。

    1 曲面细分(Mesh Subdivision)

    1.1 Loop细分(Loop Subdivision)

    (这里的loop是人名,可不是循环的意思噢)
    Loop细分是一种专门针对三角形面的细分方法,其核心步骤也十分容易理解

    1 生成更多三角形或顶点

    如图所示在,连接每条边的中点生成一个新的三角形,原来的三角形就会被分割成4个三角形。

    2 调整这些三角形或顶点的位置

    我们将所有的顶点分为两类,一类是新生成的顶点,一类是老的原来就有的顶点,对于新生成的顶点做如下处理:

    这里新的顶点就是白色的那个顶点,其位置为周围4个顶点的权重之和,各顶点权重如图所示,其余边上的新顶点处理类似。

    对于旧的顶点,做如下处理:

    其实旧每个顶点的处理也十分类似,这里以图中一个白色旧顶点为例,也是其自身以及邻接顶点的权重和,但权重的设置与该旧顶点度数有关,具体如图中右下部分所示。

    以上就是Loop细分的全过程了,最后看看效果:
    (这是一个不断进行Loop细分的例子)

    1.2 Catmull-Clark细分(Catmull-Clark Subdivision)

    正如上文所说Loop细分针对是所有三角形面,那么对于不仅仅只有三角形面该怎么办呢?这也就有了Catmull-Clark细分,这里以四边形面和三角面的混合为例:
    首先做一些定义:

    1 对于所有不是四边形的面,称之为Non-quad face
    2 所有度不为4的顶点称之为奇异点
    3 每次细分步骤如图中右下角所示,在每个面中都添加一个点,在每条边的中点也都添加一个点,面上的新顶点连接所有边上的新顶点,结果如下图所示:

    考虑右下角几个问题,其实也是一些Catmull-Clark细分的特点
    1 有几个非四边形面,就会多出几个奇异点,所以现在一共有2+2 = 4个
    2 新多出来的奇异点的度数与原来所在面的边数相等,如这里就是3度
    3 第一次细分之后所有面都会变成四边形,且往后奇异点数目不再增加

    这里给出一个再一次细分的结果:

    可以看到奇异点依然是4个,不再改变。

    以上我们明白了如何增加新顶点,与Loop细分类似,同样需要去调整各类顶点的位置,这里将所有的顶点分为三类,对于各类顶点位置调整如下图所示:

    图中对于3类顶点每次细分之后新的位置讲述的已经很清楚了,读者也不必太过纠结,明白过程大概即可。

    Loop细分 V.S Catmull-Clark细分:

    2 曲面简化(Mesh Smplication)


    其实曲面简化所利用的一个方法叫做边坍缩,如上图所示就是将一条边的两个顶点合成为一个顶点。但随之而来的问题就是,曲面简化需要尽量保持原本模型的shape,如何坍缩一条边,或者说坍缩哪一条边能够使得原模型样貌被改变的程度最小,这就是曲面简化的关键所在。

    为此引入一个度量,即二次误差度量(Quadric Error Metrics)

    即坍缩之后蓝色新顶点所在的位置与原来各个平面的垂直距离之和。如果能够使得这个误差最小那么对整个模型样貌修改一定程度上也会较小。

    那么其实到这整个曲面简化的算法流程已经比较清晰了

    1 为模型每条边赋值,其值为坍缩这条边之后,代替两个老顶点的新顶点所能得到的最小二次误差度量
    2 选取权值最小的边做坍缩,新顶点位置为原来计算得出使得二次误差最小的位置
    3 坍缩完之后,与之相连其他的边的位置会改动,更新这些边的权值
    4 重复上述步骤,直到到达终止条件

    这其实是一个标准的贪心算法,可能到不了全局最优解,但事实证明最终的结果依然相当不错

    总结

    至此整个几何部分就已经完结了,本系列涉及的几何内容相对而言还是较少,不过还是能够有一个大概的把握,从隐式曲面,显示曲面的各种类型,到显示曲面当中贝塞尔曲线曲面的详细讲解,再到今天的曲面细分与曲面简化。整体内容与闫令琪老师课上的内容一致(也是很期待暑假的进阶课程)。接下来的本系列笔记就会正式开始基础的光线追踪与PBR了。

    (如果读者觉得本系列笔记还算有用的话,希望点个赞,收藏关注一下啥的,毕竟已经写了这么多感觉没啥反馈,不过其实写作过程也是对自己知识体系的一个梳理,个人而言收获还是挺多的)

    Reference

    [1] GAMES101-现代计算机图形学入门-闫令琪

    展开全文
  • 本文用于在无法获取压感的设备上实现书法效果,因此所有的书写效果的笔触粗细变化,均是通过速率进行确认。最后实现效果如下(不会... 一、推导: 在正常的情况下,使用Canvas和Path分段绘制用户的书写结果时...

    本文用于在无法获取压感的设备上实现书法效果,因此所有的书写效果的笔触粗细变化,均是通过速率进行确认。最后实现效果如下(不会书法,只能让大家体会一下效果),代码基于之前的文章https://blog.csdn.net/cjzjolly/article/details/88661286 上进行修改而成:

    一、推导

          在正常的情况下,使用Canvas和Path分段绘制用户的书写结果时,一般不会去改变Paint的大小,因此无论两次触摸事件点到点之间的长度多大——也就是速率多大,笔画都不会产生粗细变化。

           那么如果我希望书写的时候,笔划的粗细可以随着速率的变化而变化的话,观察我们平常书写时笔划的粗细变化,那么我们可以定义如下规则:

            1、速率快到一定程度,笔触成比例持续缩小,但设定一个最小比例值

           

    展开全文
  • 质心 Voronoi 曲面细分的 Jump Flood 算法 作者:Bo Zhou, J CUBE Inc. 日本东京 简短的 这是用于集中式 Voronoi Tessellation ( CVT ) 的 Jump Flood 算法 ( JFA ) 的简单实现,CPU 和 GPU 版本均可用。 您可以将...
  • 以{positions: subdividedPositions, cells: subdividedCells}的形式返回对象中的细分网格。 positions [ [1.0,2.0,3.0], [3.4,1.3,4.2],...]上的输入网格的顶点位置 cells输入网cells的索引。 这是一个四边形索引...
  • Geometry 曲面细分和曲面简化1 曲面细分a) Loop细分b) Catmull-Clark细分(Catmull-Clark Subdivision)曲面简化边坍缩总结: 1 曲面细分 曲面细分是指将一个模型的面合理的分成更多小的面,从而提升模型精度,提高...
  • 基于CAD产品版权保护的细分曲面水印算法.pdf
  • 通过渲染管线的曲面细分功能 得到一个高模的结果 当我们开启渲染管线的tessellation开关时,我们传统意义上的顶点着色器功能就发生了改变。因为此时我们提交给IA阶段的内容不再被看作是有三个顶点(vertex)的三角...
  • 高级计算机三维建模课程最后需要提交一个大作业,比较Catmull-Clark细分曲面与Loop细分曲面算法。先把Catmull-Clark细分曲面弄懂了,这里做个记录。昨天一直在网上找Catmull-Clark细分曲面相关的解释教程,但是并...
  • Unity3d 使用DX11的曲面细分

    万次阅读 2015-01-08 18:18:06
    计算机不能直接生成曲线...多边形越多,曲面就会更为真实,曲面细分(Tessellation)就是这样的一个技术。 曲面细分,有时甚至能化低模为高模,省去不少功夫,产生效果也真是漂亮, 我们看看unity是怎么实现它的。。。
  • DirectX 11 Tessellation (曲面细分)—什么是 Tessellation (曲面细分) ?它为什么能够起到如此重要的作用?随着最近人们对 DirectX 11 的议论纷纷,你可能已经听说了有关 DirectX 11 最大新特性 Tessellation (曲面...
  • OpenGL-PN算法曲面细分

    2021-04-16 17:18:30
    原理 原理是通过一个几何上的贝塞尔表面来替换原始的网格数据进而对精细度较低的网格进行平滑处理。
  • 细分曲面Catmull-Clark Subdivision算法

    千次阅读 2010-10-13 17:00:00
    细分曲面也有很简单的算法实现, 比如Catmull-Clark Subdivision算法,其可以对任意拓扑结构的多边形进行细分。下面简要介绍下。 细分新的曲面,先求出新的曲面的顶点: Face point(位于原来多边形面里的新顶点)...
  • OpenGL-曲面细分

    2021-04-09 12:31:46
    曲面细分名词 TCS(细分控制着色器) Tessellation control shader细分控制着色器 TES(细分曲面计算着色器) Tessellation EValuation shader 控制点 这些被称为控制点的顶点定义了一个曲面 patch 一组控制点被称为一...
  • [摘要]本文我们先介绍OpenGL中的曲面细分的一些基本概念,然后给两个例子说明不得不用这项技术的理由。 曲面细分是OpenGL 4.0之后才定义的功能,使用之前请确认你的显卡驱动支持OpenGL4.0以上。 1. 曲面细分的目的 ...
  • 曲面细分 本部分我们主要来看一下图形管线中的可选功能:曲面细分曲面细分靠近管线的最前端,紧挨着顶点着色器之后,它是以图元片(patch)作为输入:每一个都是一个用顶点表示的控制点集合,并把片段分解成更小、...
  • 基于Loop细分模式,综合运用生成特征的细分方法和自适应方法等,提出一种计算简便的保持特征的自适应Loop细分曲面生成算法.该算法能够实现自动提取初始网格的尖锐特征,并可以通过交互式方式选择控制尖锐特征边、特征点...
  • OpenGL着色器程序解析--曲面细分基础

    千次阅读 2018-07-06 21:51:14
    曲面细分处理的核心是3d模型的细节和多边形数量等静态属性,例如当我们观察一个复杂的模型,像人的脸部,当靠近看时我们应该使用一个高度精细的模型来展示小的细节(例如皮肤表面的小突起等)。而高度精细的模型意味...
  • 曲面细分基础 原文: http://ogldev.atspace.co.uk/www/tutorial30/tutorial30.html CSDN完整版专栏: http://blog.csdn.net/column/details/13062.html 背景 曲面细分技术是OpenGL 4.x加入的一个令人振奋...
  • 三角网格细分算法 —— Loop 算法

    千次阅读 2020-12-26 16:11:42
    三角网格细分算法 —— Loop细分 主要参考文章: https://zhuanlan.zhihu.com/p/144400261 https://blog.csdn.net/McQueen_LT/article/details/106136324 Loop细分: 是一种专门针对三角形面的细分方法——...
  • 为了实现高清晰的3D图像的制作以及各种机械器件A级曲面的构造,通过以六边形网格细分理论为基础,在图形顶点的几何推理约束限制条件下,改进几何推理技术,并将自由曲面构造中的FFD方法与派生曲面构造法相结合,最终...
  • 提出的细分曲面水印算法是基于图像水印算法和Fourier加法性质。首先将水印信息嵌入一幅图像,通过嵌入水印的图像和原始图像在空域作比较得到含有水印信息的矩阵,然后将细分曲面初始网格迭代三次以使网格的顶点数...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 1,892
精华内容 756
关键字:

曲面细分算法