生活随笔
收集整理的這篇文章主要介紹了
图形处理(四)基于梯度场的网格编辑-Siggraph 2004
小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
基于梯度場的網(wǎng)格編輯,對(duì)應(yīng)的Paper為《Mesh Editing with Poisson-Based Gradient Field Manipulation》,是Siggraph 2004上的一篇paper,這篇paper與基于拉普拉斯的網(wǎng)格變形方法,統(tǒng)稱為基于微分域的網(wǎng)格變形算法,這篇paper其實(shí)本質(zhì)上最后的求解公式和基于拉普拉斯的網(wǎng)格變形方法一樣,之所以能夠siggraph,是因?yàn)樗ㄟ^泊松梯度場的原理進(jìn)行推導(dǎo),算法的巧妙之處在于它以頂點(diǎn)(x,y,z)中的每一維作為一個(gè)標(biāo)量場。
這篇paper涉及到的概念:散度、梯度場、標(biāo)量場、向量場等看起來很難的東西,說實(shí)話,對(duì)于這篇paper因?yàn)榫W(wǎng)上找不到源代碼,我把這篇paper看了好多遍,才把它的代碼寫出來。學(xué)這篇paper時(shí)是我第一次學(xué)習(xí)向量場的相關(guān)知識(shí),向量場在三維算法中非常重要,同時(shí)當(dāng)時(shí)給我的感覺也真不是一般的難,我看了好多關(guān)于標(biāo)量場、矢量場的相關(guān)知識(shí)理論,才感覺慢慢理解。
一、相關(guān)理論
數(shù)學(xué)上的泊松方程:
其中f表示標(biāo)量場,w表示梯度場。
三角網(wǎng)格曲面上的微分算子離散化(引用自《勾畫式泊松網(wǎng)格編輯》):
給定定義在網(wǎng)格曲面上的分段線性標(biāo)量場f(v)=fi*φi(v),其中v 為網(wǎng)格曲面上的任意一點(diǎn);fi為標(biāo)量場在網(wǎng)格曲面頂點(diǎn)vi處的函數(shù)值;φi(*)為分段線性基函數(shù),它在頂點(diǎn)vi處取值為1 ,在其余頂點(diǎn)處取值為0。我們有標(biāo)量場f 對(duì)應(yīng)的梯度算子
其中▽?duì)読(*)僅在頂點(diǎn)的鄰接三角形上有非零值,且由于φi(*)分段線性,▽?duì)読(*)在各個(gè)鄰接三角形上為分段常值函數(shù). 從幾何角度,可以容易地給 出▽?duì)読(*)在三角形T =(vi,vj ,vk)上的定義:
其中,R 90 代表繞三角形法向量n T ?旋轉(zhuǎn)90度,A T 是三角形的面積。類似地,給定定義在三角網(wǎng)格曲面上的分段常值的矢量場w,我們定義在頂點(diǎn)vi處w的散度為:
根據(jù)梯度算子和散度算子的 定義,最后可以推導(dǎo)出網(wǎng)格曲面上的標(biāo)量場f在頂點(diǎn)vi處的拉普拉斯算子為:
這篇paper是由浙大的牛人周坤提出來的,算法最后跟拉普拉斯網(wǎng)格編輯的最后公式可以說是一樣的,然而它的標(biāo)量場給我很大的啟示,這篇paper直接把(x,y,z)中的x,y,z分別當(dāng)做一個(gè)標(biāo)量場,然后對(duì)標(biāo)量場求取梯度場,最后求取散度,然后通過泊松方程重建網(wǎng)格模型,實(shí)現(xiàn)網(wǎng)格變形。想要更深入的了解泊松重建,可以看看我的另外一篇博文《 圖 像處理(十二)圖像融合(1)Seamless cloning泊松克隆-Siggraph 2004 》
二、算法實(shí)現(xiàn)
1、求取源網(wǎng)格曲面的梯度場,最后求取梯度場的散度。
[cpp] ?view plaincopy
?? void ?CScaleDeformBrush::Get_Faces_Gradient()?? {?? ????int ?fn=m_BaseMesh->faces.size();?? ????m_BaseMesh->need_adjacentfaces();?? ?? ????#pragma?omp?parallel?for ?? ????for ?( int ?i=0;i<fn;i++)?? ????{?? ????????TriMesh::Face?&f=m_BaseMesh->faces[i];?? ????????vec?vij=m_BaseMesh->vertices[f[1]]-m_BaseMesh->vertices[f[0]];?? ????????vec?vik=m_BaseMesh->vertices[f[2]]-m_BaseMesh->vertices[f[0]];?? ????????vec?normalf=vij?CROSS?vik;?? ????????float ?areaf=0.5f*len(normalf);?? ????????normalize(normalf);?? ????????for ?( int ?k=0;k<3;k++)?? ????????{?? ???????????m_Face_Gradient[i][k]=vec(0,0,0);?? ???????????for ?( int ?j=0;j<3;j++)?? ???????????{?? ????????????vec?ei=m_BaseMesh->vertices[f[(j+2)%3]]-m_BaseMesh->vertices[f[(j+1)%3]];?? ????????????vec?gradient=float (m_BaseMesh->vertices[f[j]][k]*0.5f/areaf)*(normalf?CROSS?ei);?? ????????????m_Face_Gradient[i][k]=m_Face_Gradient[i][k]+gradient;?? ????????????}?? ?? ????????}?? ????}?? ?? }?? void ?CScaleDeformBrush::Compute_Divergence()?? {?? ?????????? ????m_BaseMesh->need_adjacentfaces();?? ????int ?vn=m_BaseMesh->vertices.size();?? ????#pragma?omp?parallel?for ?? ????for ?( int ?i=0;i<vn;i++)?? ????{????? ????????for ?( int ?j=0;j<3;j++)?? ????????{?? ????????????m_vertices[i].VDivergence[j]=0.0f;?? ????????}?? ????????vector<int >&adjacentface=m_BaseMesh->adjacentfaces[i];?? ????????for ?( int ?j=0;j<adjacentface.size();j++)?? ????????{?? ????????????TriMesh::Face?&f=m_BaseMesh->faces[adjacentface[j]];?? ????????????for ?( int ?k=0;k<3;k++)?? ????????????{?? ????????????????if ?(f[k]==i)?? ????????????????{?? ????????????????????vec?ei=m_BaseMesh->vertices[f[(k+2)%3]]-m_BaseMesh->vertices[f[(k+1)%3]];?? ????????????????????vec?e1=m_BaseMesh->vertices[f[(k+1)%3]]-m_BaseMesh->vertices[f[k]];?? ????????????????????vec?e2=m_BaseMesh->vertices[f[(k+2)%3]]-m_BaseMesh->vertices[f[k]];?? ????????????????????double ?cot_angle1=Cot_angle(e2,ei);?? ????????????????????double ?cot_angle2=Cot_angle(-1.0f*e1,ei);?? ????????????????????for ?( int ?xyz=0;xyz<3;xyz++)?? ????????????????????{?? ????????????????????????m_vertices[i].VDivergence[xyz]+=0.5*(cot_angle1*(e1?DOT?m_Face_Gradient[adjacentface[j]][xyz])+cot_angle2*(e2?DOT?m_Face_Gradient[adjacentface[j]][xyz]));?? ????????????????????}?? ????????????????????break ;?? ????????????????}?? ????????????}?? ????????}?? ?? ????}?? ?? }?? ?? double ?CScaleDeformBrush::Cot_angle(vec?v1,vec?v2)?? {?? ????vec?vivo=v1;?? ????vec?vjvo=v2;?? ????double ?dotvector=vivo?DOT?vjvo;?? ????dotvector=dotvector/sqrt(len2(vivo)*len2(vjvo)-dotvector*dotvector);?? ????return ?dotvector;?? }??
2、構(gòu)建泊松方程的系數(shù),矩陣A,也就是計(jì)算拉普拉斯矩陣
[cpp] ?view plaincopy
?? void ?CScaleDeformBrush::CotangentWeights(TriMesh*TMesh, int ?vIndex,vector< double >&vweight, double ?&WeightSum, bool ?bNormalize) ?? {????? ????int ?NeighborNumber=TMesh->neighbors[vIndex].size();?? ????vweight.resize(NeighborNumber);?? ????WeightSum=0;?? ????vector<int >&NeiV=TMesh->neighbors[vIndex];?? ????for ?( int ?i=0;i<NeighborNumber;i++)?? ????{?? ????????int ?j_nei=NeiV[i];?? ????????vector<int >tempnei;?? ????????Co_neighbor(TMesh,vIndex,j_nei,tempnei);?? ????????double ?cotsum=0.0;?? ????????for ?( int ?j=0;j<tempnei.size();j++)?? ????????{?? ????????????vec?vivo=TMesh->vertices[vIndex]-TMesh->vertices[tempnei[j]];?? ????????????vec?vjvo=TMesh->vertices[j_nei]-TMesh->vertices[tempnei[j]];?? ????????????double ?dotvector=vivo?DOT?vjvo;?? ????????????dotvector=dotvector/sqrt(len2(vivo)*len2(vjvo)-dotvector*dotvector);?? ????????????cotsum+=dotvector;?? ????????}?? ????????vweight[i]=cotsum/2.0;?? ????????WeightSum+=vweight[i];?? ????}?? ?? ????if ?(?bNormalize?)??? ????{?? ????????for ?( int ?k=0;k<NeighborNumber;++k)?? ????????{?? ????????????vweight[k]/=WeightSum;?? ????????}?? ????????WeightSum=1.0;?? ????}?? }?? ?? ?? void ?CScaleDeformBrush::Co_neighbor(TriMesh?*Tmesh, int ?u_id, int ?v_id,vector< int >&co_neiv)?? {?? ????Tmesh->need_adjacentedges();?? ????vector<int >&u_id_ae=Tmesh->adjancetedge[u_id];??? ????int ?en=u_id_ae.size();?? ????Tedge?Co_Edge;?? ????for ?( int ?i=0;i<en;i++)?? ????{?? ????????Tedge?&ae=Tmesh->m_edges[u_id_ae[i]];?? ????????int ?opsi=ae.opposite_vertex(u_id);?? ????????if ?(opsi==v_id)?? ????????{?? ????????????Co_Edge=ae;?? ????????????break ;?? ????????}?? ????}?? ????for ?( int ?i=0;i<Co_Edge.m_adjacent_faces.size();i++)?? ????{?? ????????TriMesh::Face?af=Tmesh->faces[Co_Edge.m_adjacent_faces[i]];?? ????????for ?( int ?j=0;j<3;j++)?? ????????{?? ????????????if ((af[j]!=u_id)&&(af[j]!=v_id))?? ????????????{?? ????????????????co_neiv.push_back(af[j]);?? ????????????}?? ????????}?? ????}?? }?? ?? void ?CScaleDeformBrush::Get_Laplace_Matrix()?? {?? ????int ?vn=m_BaseMesh->vertices.size();?? ????int ?count0=0;?? ????vector<int >begin_N(vn);?? ????for ?( int ?i=0;i<vn;i++)?? ????{????? ????????begin_N[i]=count0;?? ????????count0+=m_BaseMesh->neighbors[i].size()+1;?? ????}?? ????typedef ?Eigen::Triplet< double >?T;?? ????std::vector<T>?tripletList(count0);?? ????for ( int ?i=0;i<vn;i++)?? ????{?? ????????VProperty?&?vi?=?m_vertices[i];?? ????????tripletList[begin_N[i]]=T(i,i,-vi.VSumWeight);?? ????????int ?nNbrs?=?vi.VNeighbors.size();?? ????????for ?( int ?k?=?0;k<nNbrs;++k)??? ????????{?? ????????????tripletList[begin_N[i]+k+1]=T(vi.VNeighbors[k],i,vi.VNeiWeight[k]);?? ????????}?? ????}?? ????m_Laplace_Matrix.resize(vn,vn);??? ????m_Laplace_Matrix.setFromTriplets(tripletList.begin(),?tripletList.end());?? ?? }??
3、添加邊界約束條件,并求解泊松方程,更新變形結(jié)果。
實(shí)時(shí)更新函數(shù):
[cpp] ?view plaincopy
void ?CScaleDeformBrush::Update_V_Position()?? {?? ????Get_Faces_Gradient();?? ????int ?fn=m_BaseMesh->faces.size();?? ????if (!m_ScaleFace.empty())?? ????for ?( int ?i=0;i<fn;i++)?? ????{?? ????????if (m_ScaleFace[i])?? ????????{?? ????????????for ?( int ?j=0;j<3;j++)?? ????????????{?? ????????????????m_Face_Gradient[i][j]=1.1f*m_Face_Gradient[i][j];?? ????????????}?? ????????????m_BaseMesh->faces[i].beSelect=false ;?? ????????}?? ????}?? ????Compute_Divergence();?? ????if (!m_MatricesCholesky) ?? ????{?? ????????double ?a=m_Laplace_Matrix.coeff(0,0)?+1;?? ????????m_Laplace_Matrix.coeffRef(0,0)=a;?? ????????m_MatricesCholesky=new ?Eigen::SimplicialCholesky<SparseMatrixType>(m_Laplace_Matrix); ?? ????}?? ????int ?vn=m_BaseMesh->vertices.size();?? ????for ?( int ?i=0;i<3;i++)?? ????{?? ????????Eigen::VectorXd?rhs_xyz(vn);?? ????????for ?( int ?j=0;j<vn;j++)?? ????????{?? ????????????rhs_xyz[j]=m_vertices[j].VDivergence[i];?? ????????}?? ????????rhs_xyz[0]=rhs_xyz[0]+1.0f*m_BaseMesh->vertices[0][i];?? ????????Eigen::VectorXd?xyz=m_MatricesCholesky->solve(rhs_xyz);?? ????????for ?( int ?j=0;j<vn;j++)?? ????????{?? ????????????m_BaseMesh->vertices[j][i]=xyz[j];?? ????????}?? ????}?? ????m_ScaleFace.clear();?? ????m_ScaleFace.resize(fn,false );?? ????m_BaseMesh->normals.clear();?? ????m_BaseMesh->FaceNormal.clear();?? ?? }??
接著我們來看一下用這個(gè)算法實(shí)現(xiàn)的簡單局部編輯結(jié)果:
上面的實(shí)時(shí)局部縮放算法我是通過另外一篇paper《Differential-Based Geometry and Texture Editing with Brushes》的思想實(shí)現(xiàn)的,這篇paper基本上就是拷貝《Mesh Editing with Poisson-Based Gradient Field Manipulation》的思想,唯一的創(chuàng)新點(diǎn)在于它的實(shí)時(shí)交互設(shè)計(jì)方面,因?yàn)槲沂菫榱藢?shí)現(xiàn)實(shí)時(shí)縮放刷,所以縮放的思想就是根據(jù)《Differential-Based Geometry and Texture Editing with Brushes》進(jìn)行寫代碼的。
上面是用了上面的算法進(jìn)行簡單的實(shí)時(shí)編輯。
這篇paper后面還有后續(xù)的算法調(diào)整,比如梯度方向調(diào)整、還有實(shí)現(xiàn)網(wǎng)格融合、幾何紋理Transfer。其中梯度方向調(diào)整是實(shí)現(xiàn)保特征變形的必備條件,因此如果你想要實(shí)現(xiàn)完整的算法,就要對(duì)梯度方向進(jìn)行調(diào)整,這個(gè)可以參考我的以一篇博文《基于旋轉(zhuǎn)不變量的網(wǎng)格變形》。在這里,我就不詳細(xì)講方向調(diào)整了,方向調(diào)整有專門的算法,paper很多。
須知:基于梯度域的變形方法和拉普拉斯網(wǎng)格變形算法一樣,微分坐標(biāo)不具有旋轉(zhuǎn)不變的特點(diǎn),在變形的時(shí)候,會(huì)發(fā)生曲面細(xì)節(jié)扭曲,需要對(duì)微分坐標(biāo),或者梯度方向進(jìn)行調(diào)整,才能實(shí)現(xiàn)保特征變形,要實(shí)現(xiàn)旋轉(zhuǎn)不變的變形,可以參考我的另外一篇博文《基于旋轉(zhuǎn)不變量的網(wǎng)格變形》以此實(shí)現(xiàn)旋轉(zhuǎn)不變的特點(diǎn)。
本文地址:http://blog.csdn.net/hjimce/article/details/46415291? ? 作者:hjimce ? ? 聯(lián)系qq:1393852684 ?? 更多資源請(qǐng)關(guān)注我的博客:http://blog.csdn.net/hjimce ??? ? ? ? ? ? ? ?原創(chuàng)文章,轉(zhuǎn)載請(qǐng)保留本行信息
參考文獻(xiàn):
1、《Mesh Editing with Poisson-Based Gradient Field Manipulation》
2、微分網(wǎng)格處理技術(shù)
3、勾畫式泊松網(wǎng)格編輯
《新程序員》:云原生和全面數(shù)字化實(shí)踐 50位技術(shù)專家共同創(chuàng)作,文字、視頻、音頻交互閱讀
總結(jié)
以上是生活随笔 為你收集整理的图形处理(四)基于梯度场的网格编辑-Siggraph 2004 的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔 網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔 推薦給好友。