梯度泊松方程网格形变

Posted toomanywaytobe

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了梯度泊松方程网格形变相关的知识,希望对你有一定的参考价值。

  网格顶点坐标的3个分量当做3个独立的标量场,如此,三角面片便有3个独立的梯度场,是为网格内在属性。当顶点移动时,问题便为求解方程技术分享图片

  根据变分法得技术分享图片 其中Φ是为形变后的顶点坐标,W为形变后的梯度场。方程进一步用矩阵表示为 技术分享图片,L为网格拉普拉斯矩阵,b为梯度场的散度.

  1.定义梯度算子

  设 f 为分段线性函数,网格三角面片{Xi, Xj, Xk}的顶点处有:f(Xi) = fi ; f(Xj) = fj ; f(Xk) = fk; u = (a, b)是三角面片的重心坐标,Bi, Bj, Bk是三角面片的一次拉格朗日

  插值函数,通过线性插值,f 在三角面片上的梯度表示为技术分享图片

  函数Bi,Bj,Bk满足Bi(u) + Bj(u) + Bk(u) = 1,所以Bi(u) + Bj(u) + Bk(u)的梯度等于0,所以 f 的梯度可表示为技术分享图片

  定义函数B的梯度为顶点到对边的高的倒数,方向为对边指向顶点。B的梯度表示为技术分享图片,A表示三角形面积,表示旋转90度。

  2. 定义散度算子

  设置向量值函数w:S——R3   。

  S表示网格,W表示三角面片上的各个顶点向量,那么W的散度为技术分享图片

  3. 拉普拉斯算子

  技术分享图片

  技术分享图片

  

  技术分享图片

  构建拉普拉斯矩阵

技术分享图片
  1 void mainNode::InitLpls()
  2 {
  3     int vertexNumber = m_vecAllVertex.size();
  4     //构建拉普拉斯矩阵权值,方式二
  5     for (int i = 0; i < vertexNumber; i++)
  6     {
  7         pVERTEX pVertex = m_vecAllVertex.at(i);
  8         float totalWeight = 0.0;
  9         for (int j = 0; j < pVertex->vecNeighborEdge.size(); j++)
 10         {
 11             pEDGE pEdge = pVertex->vecNeighborEdge.at(j);
 12             pVERTEX pA = pEdge->pA;
 13             pVERTEX pB = pEdge->pB;
 14 
 15             pVERTEX pTarget;
 16             if (pA->id == pVertex->id)
 17                 pTarget = pB;
 18             else
 19                 pTarget = pA;
 20 
 21             std::vector<pTRIANGLE> vecTri = pEdge->vecNeighborTri;
 22             pVERTEX pC = NULL;
 23             float weight = 0.0;
 24             for (int k = 0; k < vecTri.size(); k++)
 25             {
 26                 pTRIANGLE pTri = vecTri.at(k);
 27                 pVERTEX p1 = pTri->pA;
 28                 pVERTEX p2 = pTri->pB;
 29                 pVERTEX p3 = pTri->pC;
 30                 if ((pA->id == p1->id && pB->id == p2->id) || (pA->id == p2->id && pB->id == p1->id))
 31                 {
 32                     pC = p3;
 33                 }
 34                 else if ((pA->id == p1->id && pB->id == p3->id) || (pA->id == p3->id && pB->id == p1->id))
 35                 {
 36                     pC = p2;
 37                 }
 38                 else if ((pA->id == p2->id && pB->id == p3->id) || (pA->id == p3->id && pB->id == p2->id))
 39                 {
 40                     pC = p1;
 41                 }
 42                 //开始求取权值
 43                 float cotAngle = 0.0;
 44                 GetCotAngle(pA->pos, pB->pos, pC->pos, cotAngle);
 45                 weight += cotAngle;
 46             }
 47             weight /= 2.0;
 48             pVertex->mapWeightForOther.insert(std::pair<int, float>(pTarget->id, weight) );
 49             totalWeight += weight;
 50         }
 51         pVertex->fTotalWeight = totalWeight;
 52     }
 53     //计算拉普拉斯坐标。方式二
 54     for (int i = 0; i < vertexNumber; i++)
 55     {
 56         pVERTEX pVertex = m_vecAllVertex.at(i);
 57         osg::Vec3 pos = pVertex->pos;
 58         pos = pos * pVertex->fTotalWeight;
 59         osg::Vec3 otherPos = osg::Vec3(0.0, 0.0, 0.0);
 60         for (int j = 0; j < pVertex->vecNeighborVertex.size(); j++)
 61         {
 62             pVERTEX pNeihbor = pVertex->vecNeighborVertex.at(j);
 63             std::map<int, float>::iterator itor = pVertex->mapWeightForOther.find(pNeihbor->id);
 64             float weight = itor->second;
 65             otherPos += pNeihbor->pos * weight;
 66         }
 67         pVertex->lplsPos = pos - otherPos;
 68     }
 69     
 70     int count = 0;
 71     std::vector<int> beginNumber(vertexNumber);
 72     for (int i = 0; i < vertexNumber; i++)
 73     {
 74         beginNumber[i] = count;
 75         count += m_vecAllVertex[i]->vecNeighborVertex.size() + 1;
 76     }
 77 
 78     std::vector<Eigen::Triplet<float> > vectorTriplet(count);
 79     for (int i = 0; i < vertexNumber; i++)
 80     {
 81         pVERTEX pVertex = m_vecAllVertex.at(i);
 82         //原始拉普拉斯矩阵
 83         vectorTriplet[beginNumber[i]] = Eigen::Triplet<float>(i, i, pVertex->fTotalWeight);
 84         //更改后
 85         //vectorTriplet[beginNumber[i]] = Eigen::Triplet<float>(i, i, -pVertex->fTotalWeight);
 86 
 87         int j = 0;
 88         std::map<int, float>::iterator itor;
 89         for(itor = pVertex->mapWeightForOther.begin(); itor != pVertex->mapWeightForOther.end(); itor++)
 90         {
 91             int neighborID = itor->first;
 92             float weight = itor->second;
 93             //原始拉普拉斯矩阵
 94             vectorTriplet[beginNumber[i] + j + 1] = Eigen::Triplet<float>(i, neighborID, -weight);
 95             //更改后
 96             //vectorTriplet[beginNumber[i] + j + 1] = Eigen::Triplet<float>(neighborID, i, weight);
 97             j++;
 98         }
 99     }
100 
101     //头一圈和顶一圈
102     for (int i = 0; i < m_iAroundNumber * 2; i++)
103     {
104         float weight = 1.0;
105         pVERTEX pVertex;
106         if (i < m_iAroundNumber)
107         {
108             pVertex = m_vecAllVertex.at(i);
109         }
110         else
111         {
112             pVertex = m_vecAllVertex.at(i + m_iAroundNumber * 14);
113         }
114 
115         int row = i + vertexNumber;
116         vectorTriplet.push_back(Eigen::Triplet<float>(row, pVertex->id, weight));
117     }
118     
119     
120 
121     m_Matrix.resize(vertexNumber + m_iAroundNumber * 2, vertexNumber);
122     //m_Matrix.resize(vertexNumber, vertexNumber);
123     m_Matrix.setFromTriplets(vectorTriplet.begin(), vectorTriplet.end());
124     //std::cout << m_Matrix << std::endl;
125 }
View Code

 

 计算梯度

技术分享图片
 1 void mainNode::CalculateGradient()
 2 {
 3     std::map<int, pTRIANGLE>::iterator itorOfTri;
 4     for (itorOfTri = m_mapAllTri.begin(); itorOfTri != m_mapAllTri.end(); itorOfTri++)
 5     {
 6         int triID = itorOfTri->first;
 7         pTRIANGLE pTri = itorOfTri->second;
 8         osg::Vec3 A = pTri->pA->pos;
 9         osg::Vec3 B = pTri->pB->pos;
10         osg::Vec3 C = pTri->pC->pos;
11 
12         
13         osg::Vec3 AB = B - A;
14         osg::Vec3 CA = A - C;
15         osg::Vec3 BC = C - B;
16 
17         osg::Vec3 normal = ((B - A) ^ (C - A));
18         //三角形面积
19         float area = normal.length() * 0.5;
20         normal.normalize();
21 
22         float times = 2.0;
23         osg::Vec3 vecGradientX = ((normal ^ BC) / (times * area) * A.x()) + ((normal ^ AB) / (times * area) * C.x()) + ((normal ^ CA) / (times * area) * B.x());
24         osg::Vec3 vecGradientY = ((normal ^ BC) / (times * area) * A.y()) + ((normal ^ AB) / (times * area) * C.y()) + ((normal ^ CA) / (times * area) * B.y());
25         osg::Vec3 vecGradientZ = ((normal ^ BC) / (times * area) * A.z()) + ((normal ^ AB) / (times * area) * C.z()) + ((normal ^ CA) / (times * area) * B.z());
26         pTri->vecGradient[0] = vecGradientX;
27         pTri->vecGradient[1] = vecGradientY;
28         pTri->vecGradient[2] = vecGradientZ;
29     }
30 }
View Code

  计算散度

技术分享图片
 1 void mainNode::CalculateDivergence()
 2 {
 3     
 4     for (int i = 0; i < m_vecAllVertex.size(); i++)
 5     {
 6         pVERTEX pVertex = m_vecAllVertex.at(i);
 7         std::vector<pTRIANGLE> vecTri= pVertex->vecNeighborTri;
 8 
 9         float divergenceX, divergenceY, divergenceZ;
10         divergenceX = divergenceY = divergenceZ = 0.0;
11 
12         for (int j = 0; j < vecTri.size(); j++)
13         {
14             pTRIANGLE pTri = vecTri.at(j);
15             pVERTEX pA = pTri->pA;
16             pVERTEX pB = pTri->pB;
17             pVERTEX pC = pTri->pC;
18             //A为主顶点
19             osg::Vec3 A, B, C;
20             if (pA->id == pVertex->id)
21             {
22                 A = pA->pos;
23                 B = pB->pos;
24                 C = pC->pos;
25             }
26             else if (pB->id == pVertex->id)
27             {
28                 A = pB->pos;
29                 B = pC->pos;
30                 C = pA->pos;
31             }
32             else if (pC->id == pVertex->id)
33             {
34                 A = pC->pos;
35                 B = pA->pos;
36                 C = pB->pos;
37             }
38             //
39             osg::Vec3 AB, AC;
40             float cotAngleABC, cotAngleACB;
41             AB = B - A;
42             AC = C - A;
43             GetCotAngle(A, C, B, cotAngleABC);
44             GetCotAngle(A, B, C, cotAngleACB);
45 
46             for (int k = 0; k < 3; k++)
47             {
48                 float divergenceTemp = 0 - 0.5 * (cotAngleABC * (AC * pTri->vecGradient.at(k)) + cotAngleACB * (AB * pTri->vecGradient.at(k)));
49                 if (k == 0)
50                 {
51                     divergenceX += divergenceTemp;
52                 }
53                 else if (k == 1)
54                 {
55                     divergenceY += divergenceTemp;
56                 }
57                 else if(k == 2)
58                 {
59                     divergenceZ += divergenceTemp;
60                 }
61             }
62         }
63 
64         pVertex->vecDivergence[0] = divergenceX;
65         pVertex->vecDivergence[1] = divergenceY;
66         pVertex->vecDivergence[2] = divergenceZ;
67     }
68 
69 }
View Code

 

  参考论文:

  Mesh Editing with Poisson-Based Gradient Field Manipulation

  三角网格曲面上的Laplace算子及其应用

  http://blog.sina.com.cn/s/blog_73428e9a0101h4l9.html

 

 

  

  

  




以上是关于梯度泊松方程网格形变的主要内容,如果未能解决你的问题,请参考以下文章

三维动画形变算法(Mixed Finite Elements)

从泊松方程的解法,聊到泊松图像融合

使用FFT和CUDA求解泊松方程

机器学习梯度下降与正规方程(附例题代码)

协同旋转不变网格形变

从使用点云库通过泊松重建构建的网格中去除水密性属性