Mesh模型的Laplace Deformation(網格形變 - 拉普拉斯形變) - C++程式碼實現
github: https://github.com/GaoYuanBob/LaplaceDeformation
https://blog.csdn.net/Bob__yuan/article/details/81778875,這裡是我的第一篇Laplace形變的學習記錄,在這第二篇學習記錄中,想將做出的幾個結果放上來看一下(紅色是固定區域,綠色是移動區域,由於上一篇文章說了,Laplace Deformation是可以三個軸分著計算的,所以只是將綠色區域在Y方向上進行拖動,所有點的座標也只更新Y座標):
我目前是先實現的最簡單的Laplace Deformation,就是點的Laplace座標就是單純的用周圍一圈點的座標平均(同樣的權重)然後和該點座標做減法。從結果上看還是比較對的,為了檢查在記憶體有洞的情況下是否會影響,加的大圖的測試(紅色為移動錨點,進行旋轉,藍色未固定錨點,固定不動)。
具體到程式碼實現流程如下:
1、先選取錨點
對一個要進行形變的模型,我們先選擇出錨點,包括在形變過程中不動的錨點,我稱之為固定錨點,記錄這些點的索引到fixed_anchor_idx(vector)中;然後選取我們確定形變之後在哪裡的點,我稱之為移動錨點,記錄這些點的索引到move_anchor_idx(vector)中。
2、計算點的連線關係
這個功能在VCG庫裡其實是有的,匯入模型之後自動就有這種屬性,但是為了實現流程,還是自己寫了一下,大致思想就是遍歷所有的面(必須是三角形),由於是三角形面片,所以三角形上的三個點肯定是相連線的,對於所有面片,將每個點的連線點vector中,新增上這個三角形面片的另外兩個點,遍歷完成,所有點的連線關係就確定完成了。
3、計算Laplace矩陣 - L
處理的模型有 N 個點,Laplace矩陣L就是 N * N 的方陣(稀疏矩陣,大多數位置值為0),對角線上代表,第i個點有幾個鄰接點,也就是用到了我們上邊計算出來的連線關係。然後 i,j 位置是 -1 則表示第 j 個點和第 i 個點連線,如果是0,則沒有相連線。如果覺得自己的矩陣可能有問題,可以輸出出來,看看每一行的 -1 的個數加起來和這一行對角線上的值是不是一樣的,不一樣一定是問題的!
具體到程式碼部分,我用到的是VCG中的Eigen庫,標頭檔案方面需要如下幾個。
首先是建立Laplace矩陣L以及之後需要擴充的矩陣Ls,L的大小是N * N,Ls的大小是 (M + N) * N(M是錨點總個數)。然後根據上述方式進行賦初值。這個過程過後,Laplace矩陣L就計算完成了!L用來和形變前模型的所有點座標組成的矩陣相乘,得到等式右邊的Laplace座標,(忘了是啥可以看一下第一篇文章)。Ls還沒有計算完成,還需要新增錨點資訊。
比如說對於只有19的圓盤來說,Laplace矩陣L就如下圖所示。
接下來新增固定錨點和移動錨點資訊,新增方式相同,就是每一個錨點新增一行,這一行中除了這個點的索引處為1,其餘位置全為0;同樣是上邊的模型,Ls矩陣就是多了兩行的L,如下所示。(可以看出,只有兩個錨點,分別是第0號,和第16號點)。
輸出矩陣資訊的時候,Eigen庫過載了cout,所以直接 cout << L << endl; 即可輸出整個矩陣,並且是對齊的。但是矩陣很小時輸出能夠看出一些資訊,矩陣大了肉眼就看不出什麼資訊了。
4、計算等式左邊 - LsTLs
上圖公式左邊,和x相乘的就是Ls矩陣,計算了Ls之後,就是計算等式 左邊了。也就是求出Ls矩陣的轉置,然後左乘上Ls本身,我稱之為 LsTLs 矩陣。所有的矩陣庫都肯定有矩陣求轉置的方法,Eigen庫中直接 LsT = Ls.transpose(); LsTLs = LsT * Ls; 就可以求出我們需要的 LsTLs 矩陣了。
5、計算等式右邊 - LsTb
等式右邊的矩陣稱為 b,上邊的部分就是用我們之前算過的Laplace矩陣左乘上所有點的座標的矩陣,得到的所有點的Laplace座標矩陣,因為xyz三個軸可以分別計算,所以每個軸其實是一個很長的向量。b 矩陣下邊部分是Laplace Deformation的重點!它包括兩個部分,固定錨點和移動錨點,b裡的每一行,對應著Ls中的每一行。這些表示錨點資訊的行,是決定形變方式以及效果的資料。一行和一行之間的對應一定要對!
每一行的賦值,對於固定錨點和移動錨點,賦值的原理是一樣的,每一行除了點的索引位置上有數,其餘位置都是0,索引位置上的數,是這錨點的形變後坐標!!
但是,對於固定錨點,因為固定,所有其實形變後坐標也是形變前座標,固定錨點的每一行就是在上述數學公式中的含義就是,這個點,在形變過程中,座標沒有變。
對於移動錨點,因為移動了,所有不再是形變前座標,這時就需要用到我們指定的這些點形變後的座標,要自己指定。
從程式碼也可以看出,就是先用Laplace矩陣L計算所有點的Laplace座標,也就是 b 矩陣的上半部分,然後通過Eigen庫中的 conservativeResize 方法將 b 矩陣擴充套件為 N + M 行,然後用上述方法指定錨點資訊,我這裡只對Y座標進行改動,對所有移動錨點,將其Y座標減去5,看看形變效果。(從這裡就可以明顯的看出,如果所有點只是在Y軸方向上移動,那麼模型上所有點的X和Z座標形變前後是都不會變的!)。
6、計算形變後的座標
得到形變後的座標 x' 的方式就是解 LsTLs * x' = LsTb 這個線性方程組,方程組左右我們都知道了,並且用矩陣的形式表示,我們使用Eigen庫中提供的Cholesky分解加速計算。vy_new = LsTLs.llt().solve(LsTby); 一行即可計算出 vy_new 這個 Eigen::VectorXf 型別向量,向量的大小是自動計算出來的,不需要指定。
7、對模型進行形變
得到的 vy_new 向量包括了所有點的 y 座標(x和z同樣方式計算),但不是所有點的 y 座標都需要進行更新,因為我們有錨點,所以更新模型的方式就是,遍歷vy_new向量:
(1)如果這個索引上的點不是錨點,那麼這個點的新座標就變為 vy_new 對應位置的值
(2)如果這個索引上的點是固定錨點,那麼這個點的座標不用調整
(3)如果這個索引上的點是移動錨點,那麼這個點的座標調整為指定的形變後坐標
也就是說,只有不是錨點的點的座標是需要計算的,錨點的形變後坐標的我們在形變前就已經知道了,如下圖所示賦值。
這樣,模型的所有點的座標也已經更新了,完整的Laplace形變就完成了。
如果有更多要求限制,比如旋轉不變性,保體積性,等等,也是在這個流程的基礎上新增約束資訊,中心思想是一樣的。
相關文章
- c++實現的一種程式碼膨脹變形殼C++
- 變形實踐
- 變形金剛的能量方塊(含程式碼)
- 圖形處理(十二)拉普拉斯網格優化、最小二乘網格模型光順優化模型
- JavaScript圖片裁剪的無變形實現方法JavaScript
- CSS變形動畫CSS動畫
- 陣列的操作-變形陣列
- 01 揹包的變形
- 中文彩色驗證碼實現(變形/噪點/點選更換
- “破碎-重組-破碎” CSS3實現Lowpoly風格變形動畫終極篇CSSS3動畫
- javascript變形工具集JavaScript
- 技術部落格丨神經網路不再“卷”全靠“變形金剛”神經網路
- 溝槽的組合變形
- 顯示網路圖片變形的處理
- 巧妙運用clip-path,實現CSS形狀變換CSS
- CSS實現圖片等比例縮小不變形CSS
- 乙女遊戲“變形記”遊戲
- canvas實現的多邊形程式碼例項Canvas
- 手寫筆記變PDF-幾行程式碼變命令列程式為圖形化介面筆記行程命令列
- java 方法呼叫,形參改變,實參是否發生改變Java
- css3有趣的transform形變CSSS3ORM
- OpenGL ES 實現頭部形變和頭部晃動效果
- 根據比例縮放圖片的尺寸不變形程式碼例項
- CSS滑鼠變成小手形狀CSS
- CSS3變形記(上):千變萬化的DivCSSS3
- c++形參實參C++
- 最小的 k 個元素--快排變形
- 光碟變形或劃傷的處理
- css實現圖片按寬等比例縮放不變形CSS
- MSRA視覺計算組提出第二代可變形卷積網路,增強形變,更好效果視覺卷積
- canvas實現的動態心形效果程式碼例項Canvas
- canvas實現的圓形鐘錶效果程式碼例項Canvas
- 有關可變形部件模型(Deformable Part Model)的一些說明模型ORM
- 直播原始碼網站,js處理圖片變形、方向、壓縮等原始碼網站JS
- canvas錐形漸變進度條Canvas
- 剪花布條(kmp基礎變形)KMP
- Day5-Python變形(DataWhale)Python
- CSS3-過渡、變形、動畫CSSS3動畫