Project2

前言

本次是CS184的第二个project,主要内容是贝塞尔曲线和曲面,样条和曲面细分。包含了CS184 Spring2019课程第八节和第九节。Section1 是关于贝塞尔曲线和曲面的,Section2 是关于三角形mesh的曲面细分。涉及实现贝塞尔曲面的de Casteljau’s 算法。曲面细分需要用到一种图形学相关数据结构,主要是halfedge mesh这种数据结构。参考这篇文章
本次所有需要需改的代码均在student_code.cpp这个文件里。

Section I: Bezier Curves and Surfaces

Part 1: Bezier curves with 1D de Casteljau subdivision

题目介绍

第一部分关于1D de Casteljau算法的实现,这个算法是比较常见的找到贝塞尔曲面上一点的方法。
需要修改 BezierCurve::evaluateStep 这个函数,BezierCurve 这个类有一个控制点的vector ,提前会把控制点加载进来。

实现原理

这个算法的原理参见下图,比方说一开始四个控制点(我称为一阶控制点),每两个点进行一次插值(获取一个二阶点),一轮插值之后。变为三个控制点,于是再次对这三个二阶进行两两插值,重复这个过程,直到最后一个点。


BezierCurve 类有下面两个vectorevaluatedLevels[0] 就是初始的四个控制点(一阶控制点),evaluatedLevels[1]就是二阶控制点,依次类推。

  1. std::vector<Vector2D> controlPoints; // The original control points of the Bezier curve
  2. std::vector< std::vector<Vector2D> > evaluatedLevels; // Levels of points evaluated through de Casteljau

这个还蛮简单的,实现代码如下。

  1. void BezierCurve::evaluateStep()
  2. {
  3. // TODO Part 1.
  4. // Perform one step of the Bezier curve's evaluation at t using de Casteljau's algorithm for subdivision.
  5. // Store all of the intermediate control points into the 2D vector evaluatedLevels.
  6. int maxLevel = numControlPoints;
  7. int currentLevel = evaluatedLevels.size();
  8. if(currentLevel == maxLevel)
  9. return;
  10. else
  11. {
  12. std::vector<Vector2D> currentLevelPoints = evaluatedLevels[evaluatedLevels.size()-1];
  13. int nextLevelSize = currentLevelPoints.size() - 1;
  14. std::vector<Vector2D> newLevelPoints(nextLevelSize);
  15. for(int i = 0; i < nextLevelSize; i++)
  16. {
  17. newLevelPoints[i] = currentLevelPoints[i] * (1 - t) + currentLevelPoints[i + 1] * t;
  18. }
  19. evaluatedLevels.push_back(newLevelPoints);
  20. }
  21. }

运行结果

结果如下

Part 2: Bezier surfaces with separable 1D de Casteljau subdivision

题目介绍

上一题是贝塞尔曲线,这一题则是贝塞尔曲面,其实道理是一样的,只不过现在我们找到多条贝塞尔曲线,
之后加入第三个维度,再次运用de Casteljau算法形成曲面。如下所示


实现原理

本题相关的类是BezierPatch,该类和第一题中的BezierCurve类似,这是关于曲面。
实现两个函数,BezierPatch::evaluateBezierPatch::evaluate1D。首先和第一题一样controlPoints这个二维数组(假设是4*4),对每四个control point做一次1D de Casteljau,形成四个点,最后对这个四个点再做一次,得到曲面上的一点。
算法流程如下图。


  1. Vector3D BezierPatch::evaluate(double u, double v) const
  2. {
  3. // TODO Part 2.
  4. // Evaluate the Bezier surface at parameters (u, v) through 2D de Casteljau subdivision.
  5. // (i.e. Unlike Part 1 where we performed one subdivision level per call to evaluateStep, this function
  6. // should apply de Casteljau's algorithm until it computes the final, evaluated point on the surface)
  7. // 现在第一次形成四条曲线用的参数u,第二次形成曲面用的参数v
  8. std::vector<Vector3D> p;
  9. for(int i = 0; i < controlPoints.size(); ++i){
  10. p.push_back(evaluate1D(controlPoints[i], u));
  11. }
  12. return evaluate1D(p,v);
  13. }
  14. //返回1D贝塞尔曲线上的一个点
  15. Vector3D BezierPatch::evaluate1D(std::vector<Vector3D> points, double t) const
  16. {
  17. // TODO Part 2.
  18. // Optional helper function that you might find useful to implement as an abstraction when implementing BezierPatch::evaluate.
  19. // Given an array of 4 points that lie on a single curve, evaluates the Bezier curve at parameter t using 1D de Casteljau subdivision.
  20. //我的解决方案
  21. std::vector<Vector3D> tmpControlPoints(points);
  22. std::vector<Vector3D> finalControlPoints;
  23. while (tmpControlPoints.size() >= 2)
  24. {
  25. finalControlPoints.clear();
  26. for (int j = 1; j < tmpControlPoints.size(); j++) {
  27. Vector3D tmp(tmpControlPoints[j - 1] * (1 - t) + tmpControlPoints[j] * t);
  28. finalControlPoints.push_back(tmp);
  29. }
  30. tmpControlPoints.clear();
  31. tmpControlPoints.assign(finalControlPoints.begin(), finalControlPoints.end());
  32. }
  33. Vector3D result;
  34. if (!finalControlPoints.empty())
  35. {
  36. result = finalControlPoints[0];
  37. }
  38. return result;
  39. }

运行结果

在工作目录下运行命令

  1. ../meshedit /bez/teapot.bez

得到一个经典的犹他茶壶。


Section II: Loop Subdivision of General Triangle Meshes

第二个section是关于曲面细分,主要涉及halfedgemesh这个数据结构,很有意思的一个数据结构,参考这篇文章

Part 3: Average normals for half-edge meshes

题目介绍

本题求一个顶点处平均权重的normal,每个顶点有一个normal,但是顶点处的normal不像平面那么直观,一个平面的normal垂直于这个平面上所有的向量,这很明显。但是对于一个vertex的normal,我们是通过将这个顶点周围的所有相邻的平面的normal加权求和。

这么做的好处有什么呢,就是可以使得最终显示在屏幕上的图形更加的平滑。参见运行结果。
原因是计算光照的时候我们要用normal的信息,在本题之前是直接使用每个三角mesh的normal(也就是垂直于该平面的normal),然后采用一个光照模型比如phong shading。不使用vertex normal,直接用每个三角面的normal算光照的话单个三角面内的像素计算出来的颜色都一样
但是使用了vertex normal,比方说用phong shading,就是对三角面的三个顶点分别计算,这样三角形内的每个像素再根据这三个顶点做差值,所以显示出来的图像就是平滑的而不是一个个三角面。

总结一下就是根据三个顶点的颜色插值得到平滑图形,而计算三个顶点光照和颜色需要normal。

原理介绍

只需要完成Vertex::normal这个函数,我们对于该顶点相邻的所有平面(三角形)normal加权求和,权重就是三角形的面积,也就是面积越大权重越大。那么怎么遍历所有的face呢?就是采用halfedgemesh这个数据结构,该数据结构是一整个mesh(有可能整个模型就这一个mesh),这个mesh包含了edgefacevertexhalfedge的链表。其的核心就是halfedge类,halfedge就是一个有向向量,每个halfedge都关联五个其他基本的几何单位,halfedge有指向起点vertex的iterator(c++中类似指针的一个概念),指向edgeface,下一个halfedgetwin halfedge(同一条edge上反方向的一个halfedge)的iterator,这样halfedge就像一个中枢站一样。如上图所示,每个vertex都有关联的一个halfedge,这个halfedge是任意一个以该vertex为起点的halfedge

  1. HalfedgeIter _twin; ///< halfedge on the "other side" of the edge
  2. HalfedgeIter _next; ///< next halfedge around the current face
  3. VertexIter _vertex; ///< vertex at the "base" or "root" of this halfedge
  4. EdgeIter _edge; ///< associated edge
  5. FaceIter _face; ///< face containing this halfedge

代码如下
  1. Vector3D Vertex::normal(void) const
  2. {
  3. // TODO Part 3.
  4. // TODO Returns an approximate unit normal at this vertex, computed by
  5. // TODO taking the area-weighted average of the normals of neighboring
  6. // TODO triangles, then normalizing.
  7. Vector3D normal(0,0,0); // initialize a vector to store your normal sum
  8. HalfedgeCIter h = halfedge(); // Since we're in a Vertex, this returns a halfedge
  9. HalfedgeCIter hConst = h;
  10. double sum = 0.;
  11. do
  12. {
  13. //要取得是平面的normal,而不是点的normal
  14. Vector3D nighborNormal(h->face()->normal());
  15. //下面是两个向量,然后做叉乘
  16. Vector3D v1(h->vertex()->position - (h->twin()->vertex())->position);
  17. Vector3D v2(h->vertex()->position - (h->twin()->next()->twin()->vertex()->position));
  18. double tmpWeight = cross(v1, v2).norm();
  19. sum += tmpWeight;
  20. //三角形面积的两倍(平行四边形的面积)乘上normal累加上去
  21. normal += nighborNormal * tmpWeight;
  22. //std::cout<< "暂时输出normal为" << normal<<std::endl;
  23. h = h->twin()->next();
  24. }while(h != hConst);
  25. //最后归一化
  26. normal /= sum;
  27. return normal;
  28. }

运行结果

同样运行以下命令

  1. ../meshedit /dae/teapot.dae

然后我们按下Q键,就会显示重新计算normal之后的模型,可以看到光滑了很多。 按下w键是切换到GLSL shader。

Part 4: Half-edge flip

题目介绍

本题是flip翻转half-edge,将边的连接变换。


#### 实现原理 这道题只要遵循[这篇guide](http://15462.courses.cs.cmu.edu/fall2015content/misc/HalfedgeEdgeOpImplementationGuide.pdf)就不会有什么问题了,就是一些繁琐的操作。但是思路很好,先把图画出来,原先有哪些edge,vertex,halfedge,face,然后在画出flip之后的图,看看这四种基本几何单位又有哪些。然后修改各个类的iterator指向就完事儿了。我的实现和guide中的图一致。
代码如下
  1. EdgeIter HalfedgeMesh::flipEdge(EdgeIter e0)
  2. {
  3. // TODO Part 4.
  4. // TODO This method should flip the given edge and return an iterator to the flipped edge.
  5. // 首先获取所有的可能改变的元素
  6. FaceIter f0 = e0->halfedge()->face();
  7. FaceIter f1 = e0->halfedge()->twin()->face();
  8. if(f0->isBoundary() || f1->isBoundary())
  9. {
  10. return e0;
  11. }
  12. HalfedgeIter h0 = e0->halfedge();
  13. HalfedgeIter h1 = h0->next();
  14. HalfedgeIter h2 = h1->next();
  15. HalfedgeIter h3 = h0->twin();
  16. HalfedgeIter h4 = h3->next();
  17. HalfedgeIter h5 = h4->next();
  18. HalfedgeIter h6 = h1->twin();
  19. HalfedgeIter h7 = h2->twin();
  20. HalfedgeIter h8 = h4->twin();
  21. HalfedgeIter h9 = h5->twin();
  22. VertexIter v0 = h0->vertex();
  23. VertexIter v1 = h3->vertex();
  24. VertexIter v2 = h6->vertex();
  25. VertexIter v3 = h8->vertex();
  26. EdgeIter e1 = h1->edge();
  27. EdgeIter e2 = h2->edge();
  28. EdgeIter e3 = h4->edge();
  29. EdgeIter e4 = h5->edge();
  30. //接着重新分配
  31. //想了一下,分配顺序应该没有关系
  32. h0->next() = h1;
  33. h0->twin() = h3;
  34. h0->face() = f0;
  35. h0->vertex() = v3;
  36. h0->edge() = e0;
  37. h1->next() = h2;
  38. h1->twin() = h7;
  39. h1->face() = f0;
  40. h1->vertex() = v2;
  41. h1->edge() = e2;
  42. h2->next() = h0;
  43. h2->twin() = h8;
  44. h2->face() = f0;
  45. h2->vertex() = v0;
  46. h2->edge() = e3;
  47. h3->next() = h4;
  48. h3->twin() = h0;
  49. h3->face() = f1;
  50. h3->vertex() = v2;
  51. h3->edge() = e0;
  52. h4->next() = h5;
  53. h4->twin() = h9;
  54. h4->face() = f1;
  55. h4->vertex() = v3;
  56. h4->edge() = e4;
  57. h5->next() = h3;
  58. h5->twin() = h6;
  59. h5->face() = f1;
  60. h5->vertex() = v1;
  61. h5->edge() = e1;
  62. h6->next() = h6->next();
  63. h6->twin() = h5;
  64. h6->face() = h6->face();
  65. h6->vertex() = v2;
  66. h6->edge() = e1;
  67. h7->next() = h7->next();
  68. h7->twin() = h1;
  69. h7->face() = h7->face();
  70. h7->vertex() = v0;
  71. h7->edge() = e2;
  72. h8->next() = h8->next();
  73. h8->twin() = h2;
  74. h8->face() = h8->face();
  75. h8->vertex() = v3;
  76. h8->edge() = e3;
  77. h9->next() = h9->next();
  78. h9->twin() = h4;
  79. h9->face() = h9->face();
  80. h9->vertex() = v1;
  81. h9->edge() = e4;
  82. v0->halfedge() = h2;
  83. v1->halfedge() = h5;
  84. v2->halfedge() = h3;
  85. v3->halfedge() = h0;
  86. e0->halfedge() = h0;
  87. e1->halfedge() = h5;
  88. e2->halfedge() = h1;
  89. e3->halfedge() = h2;
  90. e4->halfedge() = h4;
  91. f0->halfedge() = h0;
  92. f1->halfedge() = h3;
  93. return e0;
  94. }

#### 运行结果 在GUI界面中选中边,然后按下S键即可。

Part 5: Half-edge split

题目介绍

本题和上一题思路类似,只不过变成了split分割边。如下所示

实现原理

同样参考这篇guide,这次需要新加上几个点,比如说一个新的vertex,三条edge,六条新的halfedge,两个face。依旧是修改一堆iterator,我的实现图如下。

  1. VertexIter HalfedgeMesh::splitEdge(EdgeIter e0)
  2. {
  3. // TODO Part 5.
  4. // TODO This method should split the given edge and return an iterator to the newly inserted vertex.
  5. // TODO The halfedge of this vertex should point along the edge that was split, rather than the new edges.
  6. FaceIter f0 = e0->halfedge()->face();
  7. FaceIter f1 = e0->halfedge()->twin()->face();
  8. if(f0->isBoundary() || f1->isBoundary())
  9. {
  10. return e0->halfedge()->vertex();
  11. }
  12. HalfedgeIter h0 = e0->halfedge();
  13. HalfedgeIter h1 = h0->next();
  14. HalfedgeIter h2 = h1->next();
  15. HalfedgeIter h3 = h0->twin();
  16. HalfedgeIter h4 = h3->next();
  17. HalfedgeIter h5 = h4->next();
  18. HalfedgeIter h6 = h1->twin();
  19. HalfedgeIter h7 = h2->twin();
  20. HalfedgeIter h8 = h4->twin();
  21. HalfedgeIter h9 = h5->twin();
  22. VertexIter v0 = h0->vertex();
  23. VertexIter v1 = h3->vertex();
  24. VertexIter v2 = h6->vertex();
  25. VertexIter v3 = h8->vertex();
  26. EdgeIter e1 = h1->edge();
  27. EdgeIter e2 = h2->edge();
  28. EdgeIter e3 = h4->edge();
  29. EdgeIter e4 = h5->edge();
  30. //创建新的halfElement
  31. //这里创建的时候已经加入mesh的list里面去了,所以确实改变了mesh的list
  32. //三条新的edge
  33. EdgeIter e5 = newEdge();
  34. EdgeIter e6 = newEdge();
  35. EdgeIter e7 = newEdge();
  36. //六个新的halfedge
  37. HalfedgeIter h10 = newHalfedge();
  38. HalfedgeIter h11 = newHalfedge();
  39. HalfedgeIter h12 = newHalfedge();
  40. HalfedgeIter h13 = newHalfedge();
  41. HalfedgeIter h14 = newHalfedge();
  42. HalfedgeIter h15 = newHalfedge();
  43. //两个新的face
  44. FaceIter f2 = newFace();
  45. FaceIter f3 = newFace();
  46. //一个新的vertex
  47. VertexIter v4 = newVertex();
  48. v4->position = (v1->position + v0->position) / 2;
  49. //重新分配
  50. h0->setNeighbors(h1, h3, v4, e0, f0);
  51. h1->setNeighbors(h10, h6, v1, e1, f0);
  52. h2->setNeighbors(h14, h7, v2, e2, f3);
  53. h3->setNeighbors(h11, h0, v1, e0, f1);
  54. h4->setNeighbors(h12, h8, v0, e3, f2);
  55. h5->setNeighbors(h3, h9, v3, e4, f1);
  56. h6->setNeighbors(h6->next(), h1, v2, e1, h6->face());
  57. h7->setNeighbors(h7->next(), h2, v0, e2, h7->face());
  58. h8->setNeighbors(h8->next(), h4, v3, e3, h8->face());
  59. h9->setNeighbors(h9->next(), h5, v1, e4, h9->face());
  60. h10->setNeighbors(h0, h15, v2, e5, f0);
  61. h11->setNeighbors(h5, h12, v4, e7, f1);
  62. h12->setNeighbors(h13, h11, v3, e7, f2);
  63. h13->setNeighbors(h4, h14, v4, e6, f2);
  64. h14->setNeighbors(h15, h13, v0, e6, f3);
  65. h15->setNeighbors(h2, h10, v4, e5, f3);
  66. e0->halfedge() = h0;
  67. e1->halfedge() = h1;
  68. e2->halfedge() = h2;
  69. e3->halfedge() = h4;
  70. e4->halfedge() = h5;
  71. e5->halfedge() = h10;
  72. e6->halfedge() = h14;
  73. e7->halfedge() = h11;
  74. f0->halfedge() = h0;
  75. f1->halfedge() = h3;
  76. f2->halfedge() = h4;
  77. f3->halfedge() = h2;
  78. v0->halfedge() = h4;
  79. v1->halfedge() = h3;
  80. v2->halfedge() = h2;
  81. v3->halfedge() = h5;
  82. v4->halfedge() = h0;
  83. return v4;
  84. }

运行结果

我随便split,不是很有美感2333.

Part 6: Loop subdivision for mesh upsampling

题目介绍

曲面细分是最后一题也是总体上最复杂的一题,这一题的步骤很多,出了问题debug起来也比较复杂。我会一步一步说明。首先说一说曲面细分,曲面细分就是把一个三角面分割成多个子面(比方说四个),这样增加总体的face,使得模型可以变得更加精细。如下图

实现原理

  • (1)首先遍历mesh中的全部顶点,将每个顶点的isNew属性标记为false,同时计算每个顶点在曲面细分之后的新位置,存在该顶点的newPosition属性之中。 计算方法如下,新创建的点根据八分之三,八分之一的权重加权求和。旧的顶点在曲面细分之后也会有一个新的位置,计算方式也是加权求和,与自身相邻的顶点权重是u,自身权重是1-(n*u)。

  • (2)然后遍历全部的edge,确定将要创建的新点的位置。

  • (3) 创建一个新的originEdgeList,这里之所以要复制一个Edgelist,而且这个list存储的是指向指向edge的iterator的iterator。

    是因为如果split了edge,那么mesh的edge链表就会添加进指向新创建的edge的iterator,那么遍历的时候就会遍历到这些新创建的然后再次split,陷入死循环。

  • (4)split全部的edge,记住要遍历originEdgeList,然后设置新创建的订点的newPosition属性以及新创建的edge的isNew属性。这些属性的设置非常关键!!!因为倒数第二步的时候会根据这些属性来决定那些边需要flip。

  • (5)flip所有连接 新顶点旧顶点新边!!!之所以先split再flip是这样实现起来方便一些。

  • (6)再次遍历一遍所有的顶点,将每个顶点的新位置覆盖当前位置。


  1. void MeshResampler::upsample(HalfedgeMesh &mesh)
  2. {
  3. // TODO Part 6.
  4. // This routine should increase the number of triangles in the mesh using Loop subdivision.
  5. // Each vertex and edge of the original surface can be associated with a vertex in the new (subdivided) surface.
  6. // Therefore, our strategy for computing the subdivided vertex locations is to *first* compute the new positions
  7. // using the connectity of the original (coarse) mesh; navigating this mesh will be much easier than navigating
  8. // the new subdivided (fine) mesh, which has more elements to traverse. We will then assign vertex positions in
  9. // the new mesh based on the values we computed for the original mesh.
  10. // TODO Compute new positions for all the vertices in the input mesh, using the Loop subdivision rule,
  11. // TODO and store them in Vertex::newPosition. At this point, we also want to mark each vertex as being
  12. // TODO a vertex of the original mesh.
  13. // 第一步,计算旧顶点的新位置同时标记isNew为false。
  14. for(auto it = mesh.verticesBegin(); it!= mesh.verticesEnd(); it++)
  15. {
  16. //先计算新的位置
  17. it->isNew = false;
  18. Vector3D newPosition(0, 0, 0);
  19. float sum = 0;
  20. auto nit = it->halfedge()->twin();
  21. auto originNit = nit;
  22. auto n = it->degree();
  23. //注意!!!!浮点数,如果直接3/16是0
  24. float u = (n == 3) ? (3.0/16.0) : (3.0 / (8.0 * n));
  25. do{
  26. sum += u;
  27. newPosition = newPosition + u * (nit->vertex()->position);
  28. nit = nit->next()->twin();
  29. }while (nit != originNit);
  30. sum += 1 - n*u;
  31. newPosition = newPosition + (1.0 - n*u) * it->position;
  32. newPosition = newPosition / sum;
  33. it->newPosition = newPosition;
  34. }
  35. // TODO Next, compute the updated vertex positions associated with edges, and store it in Edge::newPosition.
  36. //第二步计算所有新点的位置(每个edge上一个新的点),然后存在edge的newposition属性里
  37. for(auto it = mesh.edgesBegin(); it!=mesh.edgesEnd(); it++)
  38. {
  39. //每个边上一个新的点
  40. Vector3D newPosition(0, 0, 0);
  41. if(it->halfedge()->face()->isBoundary() || it->halfedge()->twin()->face()->isBoundary())
  42. {
  43. //边界情况直接中点
  44. newPosition = it->halfedge()->vertex()->position / 2.0 + it->halfedge()->twin()->vertex()->position / 2.0;
  45. }
  46. else
  47. {
  48. Vector3D v0 = it->halfedge()->vertex()->position;
  49. Vector3D v1 = it->halfedge()->twin()->vertex()->position;
  50. Vector3D v2 = it->halfedge()->next()->twin()->vertex()->position;
  51. Vector3D v3 = it->halfedge()->twin()->next()->twin()->vertex()->position;
  52. //注意啦,这里也被round to zero了,记住浮点数
  53. newPosition = v0 * 3.0/8.0 + v1 * 3.0/8.0 + v2 * 1.0/8.0 + v3 * 1.0/8.0;
  54. }
  55. it->newPosition = newPosition;
  56. }
  57. // TODO Next, we're going to split every edge in the mesh, in any order. For future
  58. // TODO reference, we're also going to store some information about which subdivided
  59. // TODO edges come from splitting an edge in the original mesh, and which edges are new,
  60. // TODO by setting the flat Edge::isNew. Note that in this loop, we only want to iterate
  61. // TODO over edges of the original mesh---otherwise, we'll end up splitting edges that we
  62. // TODO just split (and the loop will never end!)
  63. //第三步,复制所有指向edge的iterator
  64. vector<EdgeIter> originEdgeList;
  65. auto copyIt = mesh.edgesBegin();
  66. do{
  67. auto newIt = copyIt;
  68. newIt->isNew = false; //一开始遍历的时候每次都改成false
  69. originEdgeList.push_back(newIt);
  70. copyIt++;
  71. }while(copyIt != mesh.edgesEnd());
  72. //第四步,split所有的边
  73. //注意这里,it是EdgeIter的iterator,而*it才是EdgeIter类型
  74. int countIt = 0;
  75. for(auto it = originEdgeList.begin(); it != originEdgeList.end(); it++)
  76. {
  77. // 返回split之后的点
  78. countIt++;
  79. auto v = mesh.splitEdge(*it);
  80. // 注意isNew的修改,这里将除了最初的edge以外的新创建的edge全部改为true;
  81. v->isNew = true;
  82. // newPosition 到 position的时候就为空了。
  83. v->newPosition = (*it)->newPosition;
  84. auto newHalfIt = v->halfedge();
  85. newHalfIt->edge()->isNew = false;
  86. newHalfIt->twin()->next()->edge()->isNew = true;
  87. newHalfIt->twin()->next()->twin()->next()->edge()->isNew = false;
  88. newHalfIt->next()->next()->edge()->isNew = true;
  89. }
  90. // TODO Now flip any new edge that connects an old and new vertex.
  91. // 第五步,flip所有连接新点和旧点的新边。
  92. for(auto it = mesh.edgesBegin(); it != mesh.edgesEnd(); it++)
  93. {
  94. // != 与异或的功能一致
  95. if(it->halfedge()->vertex()->isNew != it->halfedge()->twin()->vertex()->isNew )
  96. {
  97. if(it->isNew)
  98. mesh.flipEdge(it);
  99. }
  100. }
  101. // TODO Finally, copy the new vertex positions into final Vertex::position.
  102. // 第六步,最后遍历一遍全部顶点,将新位置赋值给当前位置
  103. for(auto it = mesh.verticesBegin(); it != mesh.verticesEnd(); it++)
  104. {
  105. it->position = it->newPosition;
  106. }
  107. }

运行结果

下面分别是没有曲面细分,进行一次曲面细分,进行两次曲面细分的结果。


(ps:这次的project下文件夹dae里有好多模型数据,大家可以玩玩看,而且还有最后一个自选的模型模块,我偷懒没搞,下次有机会再把这个坑填上去吧)

总结

这次project的前面部分是比上一个简单一些的,通过这次project,基本上了解了贝塞尔曲线,de Casteljau算法,曲面细分,一个很有意思的数据结构halfedgemesh。但是感觉自己的知识还是很浅薄啊,尚需努力,我可是要立志成为图形学master的男人啊kora!!!



计算机图形学      图形学 计算机课程 项目总结

本博客所有文章除特别声明外,均采用 CC BY-SA 3.0协议 。转载请注明出处!