一哆窿、簡介
此次作業(yè)主要實現了一個利用SPH算法計算的流體况褪,交互方式主要采用鼠標鍵盤,另利用安卓手機的加速度傳感器使用socket通信來控制更耻,場景采用openmesh導入一個房間模型测垛。
二、操作說明
電腦端:F1切換粒子顯示模式(用point繪制秧均,帶紋理繪制食侮,模型繪制),F2目胡、F3控制鏡頭遠近锯七,F4切換控制流體容器轉動模式(鼠標拖拽,手機加速度傳感器控制),F5切換是否讓房間跟隨容器轉動誉己,F11眉尸、F12控制光照強度巨双,wasd控制場景移動,上下左右鍵控制流體容器的移動袱蜡。
安卓端:將電腦與手機連在同一wifi下坪蚁,電腦端cmd使用ipconfig查詢ipv4地址,將地址輸入手機敏晤,點擊連接按鈕。(連接需在模型加載完畢后湘换,命令行顯示port 9400 listening后連接)
三、初始思路
在剛看到題目要求的粒子系統(tǒng)時筹我,由于沒有大的限制,一時無從下手结澄。后來在考慮一段時間后突然想到可以實現一個水壺倒水麻献,水落入水盆的場景猜扮,這樣可以使用手機的陀螺儀來控制水壺的傾角,會很有意思齿桃,尤其是如果把水盆的盆面正對屏幕短纵,則可以實現adobe宣傳片中手機模仿灑水僵控,電腦屏幕上出現反應的效果。
因此在考慮過后悠就,預計實現需要完成三塊內容:
1理卑、水的粒子系統(tǒng)實現方式蔽氨。
2帆疟、安卓手機的交互踪宠。
3妈嘹、水的渲染润脸。
在實現的過程中發(fā)現,由于sph算法非常精細復雜倒堕,對于算力的要求很高垦巴,在我的電腦上铭段,沒有加載場景模型的情況下,512個水粒子可以無卡頓憔披,而加載后已經出現卡頓爸吮,如果按照開始的設想去設計拗胜,最終效果很差,因此最后沒有實現倒水锈遥,而是僅僅將流體放在一個立方體容器中勘畔,進行旋轉移動控制
四炫七、難點及攻克歷程
1、SPH算法
①在確定一開始的設想后侠驯,我首先實現了一個簡單的粒子系統(tǒng),接著查詢了相關論文和博客吟策,包括動態(tài)的水面模擬[1]檩坚,OpenGL中基于粒子系統(tǒng)的噴泉模擬實現[2]等,但這兩者的主要內容都在于水面的模擬拖叙,而我想實現的是對于流體細致到每一個粒子的模擬薯鳍,因此找到的幾篇文章都難以幫助我實現預想的效果沪猴。(最后的事實證明SPH算法過于精細运嗜,對于計算能力的要求很高悯舟,沒能實現預想效果抵怎,在提前答辯過程中助教指出sph算法并不是對每個粒子渲染,而是用每個粒子來計算一部分區(qū)域的流體的位置尝艘,對流體進行渲染姿染。)
②接著去問了船建學院的同學,想知道如何計算倒水時的軌跡狡汉,一位同學給了我一個簡單的方程:
但這僅僅是計算了一個水流的直徑闽颇,模擬出來的結果應該是一個管道的形狀,顯然 不是我想要的兵多。
另一個同學給我推薦了一個模擬軟件橄仆,我想看需要調哪些參數已得知要模擬流體需要考慮哪些因素沿癞,但這卻無法得知背后的計算模型矛渴。
③最后詢問荀琳玲助教得知了SPH算法(荀琳玲助教在作業(yè)完成過程中給了我很多幫助),于是上網查詢蚕涤,在找了多個博客后鎖定了這個博客:SPH算法簡介[3]铣猩。
按照我的理解达皿,SPH算法就是將水看成一個個粒子組成,粒子之間互相影響峦椰,計算每個水珠的各種屬性汤功,根據每個水珠在一個時刻的受力計算出它下一時刻的位置,而關鍵就在于計算它的受力色解。
那么餐茵,粒子之間互相影響導致的受力如何計算呢?這里就需要一個“光滑核”的概念萧恕,即粒子的屬性會擴散到周圍票唆,并且隨著距離的增加影響逐漸變小走趋,這種隨著距離而衰減的函數被稱為“光滑核”函數,最大影響半徑為“光滑核半徑”簿煌。
設想流體中某點r(此處不一定有粒子),在光滑核半徑h范圍內有數個粒子,位置分別是惩琉,r0→,r1→,r2→,…rj→瞒渠,則該處某項屬性A的累加公式為:
我們假設流體中一個位置為ri→的點,此處的密度為ρ(ri)伍玖、壓力為p(ri)窍箍、速度為u(ri)椰棘,可以推導出此處的加速度a (ri)為
ri→處的密度計算公式最終為:
壓力產生的加速度部分:
粘度產生的加速度部分:
以上兩個部分的計算分別由sph_fluid_system類中的_computePressure方法和_computeForce計算榄笙。
那么現在的問題變成了,如何知道哪些粒子在當前粒子的光滑核半徑之內呢外恕?
這里采用的方法是sph_grid_container類和sph_neighbour_table類一起作用實現乡翅。具體的方式是蠕蚜,grid_container實現一個內部分為一個個小方格的容器悔橄,方格是按照順序排好順序的,在particlepool中取得粒子后根據粒子位置屬性找出方格序號挣柬,進而找出方格中其它粒子睛挚,也就找到了鄰居,加入到neighbour_table侧到,之后按照公式計算即可。
而在查詢SPH算法相關資料之前故源,我已經寫了一個粒子系統(tǒng),結構是particle類和一個particlepool類绳军,后者負責粒子的管理删铃。寫的時候因為沒有設想到之后會采用復雜的算法猎唁,因此直接將繪制寫為了particle類自身的一個方法顷蟆。之后發(fā)現這樣的實現方式很不清晰帐偎,所以最后將繪制統(tǒng)一放到了源.cpp中,將glut與sph系統(tǒng)解耦豁生,也就是說換一個glew或者glfw這個sph系統(tǒng)還能用漫贞。
源.cpp在每次display時sph_system調用tick方法迅脐,利用以上所述計算了粒子的加速度后計算出下一時刻粒子的位置谴蔑,然后源.cpp獲取particlepool中所有粒子的位置進行繪制。
2窃躲、模型的加載
一開始僅僅在加載粒子模型時需要使用蒂窒,因此使用了在作業(yè)一中寫的objloader刘绣,之后需要加載大的模型,發(fā)現自己寫的不夠用了纬凤。
首先想采用assimp庫加載場景模型停士,但網上大多數教程都是glew,而我用的是glut拇舀,因此最后采用OpenMesh骄崩。此處遇到一個坑薄辅,OpenMesh里應該有ws2def.h,與win2sock.h有沖突脱惰,而后者是使用安卓進行連接時用到的庫拉一,在嘗試使用命名空間分隔無果后旧乞,發(fā)現include順序調換之后不再報錯良蛮,在向幾位學長請教之后發(fā)現命名空間解決的是命名上的沖突决瞳,其它的沖突還是得看報錯進行具體解決皮胡。
3赏迟、容器旋轉,流體的重力向量保持不變泻仙。
由于容器和流體的旋轉是在渲染階段利用glrotate做的旋轉玉转,也就是說在流體看來殴蹄,重力方向還是原本那樣袭灯,即在渲染的時候就跟著旋轉了橘茉,而不是在世界坐標系中的(0,-9.8,0)姨丈。(重力向量是源.cpp傳入fluid_system的參數)因此需要在每次rotate之前把新的重力向量傳入fluid_system。新的重力向量是采用數學方法推出的髓介。繞x軸轉yRotate度唐础,繞y軸轉xRotate度,最后的位置是:
X = -9.8 * sin(xRotate / 180 * PI) * sin(yRotate / 180 * PI),
Y = -9.8 * cos(xRotate/180 * PI),
Z = 9.8 * sin(xRotate / 180 * PI) * cos(yRotate / 180 * PI)
此處遇到一個坑洒沦,math.h中采用弧度制豹绪,glrotate采用角度制,導致一段時間怎么調也不對申眼,而我以為是公式錯了瞒津,導致在此處耽誤了很長時間,最后通過在display函數中加了一個將重力向量繪制出來查看才發(fā)現了這個問題并加以解決括尸。
另外巷蚪,此處的數學計算為之后做交互埋下了一個伏筆。
4濒翻、交互
首先實現鼠標鍵盤交互啦膜,像控制遠近僧家,控制上下左右移動都是為了調整模型到合適的位置方便啸臀,在實現過程中加入的烁落。可以通過f5切換場景模型是否跟著容器變化角度灯萍。視角的變化是采用gltranslate去變換物體位置實現的。
接著是實現安卓手機與之的交互。
首先要解決的問題自然是連接。因為以前做過一個安卓app的項目穷娱,有過cs架構的經驗,而那時服務器是放在阿里云上的,所以我一開始設想了這樣的架構:安卓發(fā)消息給阿里云的服務器缸托,電腦端發(fā)消息去阿里云服務器獲取母谎。后來才想到電腦端用的c++不一定不能寫服務器呀匹摇,于是去學習了一下socket在安卓端的java和電腦端的c++的使用,建立了連接,此時也就遇到了在上述的與openmesh沖突的問題冰悠。
控制方面搬泥,開始時設想采用陀螺儀獲取角加速度燥透,讓流體的容器跟著旋轉,可是實現完成后發(fā)現這個實現無法讓流體容器與手機的角度同步,很難控制瞻讽。我想要的效果是烦磁,手機轉到什么位置呕乎,容器就轉到什么位置,但采用陀螺儀的話容器的旋轉就變成增量式的了。
后來查找了安卓的多個傳感器,發(fā)現加速度傳感器的xyz數值就是重力在三個軸上的分量,也就是是說晃危,只要解上面那個公式的方程即可得到xRotate、yRotate的數值,這樣就可讓容器的轉動與手機同步眠副,即
X = -9.8 * sin(xRotate / 180 * PI) * sin(yRotate / 180 * PI)
Y = -9.8 * cos(xRotate/180 * PI)
Z = 9.8 * sin(xRotate / 180 * PI) * cos(yRotate / 180 * PI).
最終實現成功了忘闻,不過此處的一個坑是asin、acos在遇到不合理的參數比如大于1時會報nan涮瞻,出錯缀遍,導致程序掛掉青伤,因此需要檢查一下再輸入眼溶。
但最終實現效果也沒有預期的好,因為涉及到了網絡,無法做到實時流暢的響應骏庸,還是會有延遲硬猫,考慮更流暢的方式可能是利用計算機攝像頭或者專門用于操控的設備來實現控制衬横。
五、其它技術實現
1、場景:房間模型,可通過f4切換調整臭杰。開始時給了材質的定義将塑,發(fā)現效果很差,最后將其去掉,調整了一下光照位置讓它真實感強一些柑司。
2玻粪、光照:通過設置一個light_intensity值作為light0的ambient,diffuse,specular的輸入蹲缠,通過f11近迁、f12增大減小light_intensity值調整光照瑰步。因為要調整泰演,因此將光照的初始化放在display函數中,且與房間模型的相對位置不隨glrotate改變猾普。
3、粒子系統(tǒng)物理仿真:sph算法
4溜在、粒子系統(tǒng)模型切換:開始時采用自己寫的objloader掖肋,后來既然用了openmesh就直接也用它讀入了一個cube模型纫溃,因為計算量本來就很大郎楼,就沒有讀入更復雜的模型聋庵。
5臼疫、粒子系統(tǒng)光照烫堤、紋理映射:在texture.h中實現紋理的讀入,然后在粒子位置四周畫了一個立方體每一面都貼上相同的waterball.jpg這張紋理。另外鸽斟,透明效果通過glBlendFunc混合顏色來實現拔创。
6、交互控制:鼠標鍵盤富蓄、安卓設備伏蚊。
六、總結
這次大作業(yè)雖然沒有實現預期那么好的效果格粪,但是在利用安卓手機進行交互這一點還是增添了一點趣味性躏吊,并且在實現的過程中讓我意識到了adobe宣傳片的效果的大致實現思路,同時也意識到帐萎,利用網絡進行實時的交互這種方式不可靠比伏,不夠快也不夠穩(wěn)定安全,我覺得這也是需要特定交互硬件的原因之一疆导;在利用SPH算法實現流體的過程中赁项,我開始時認為這個算法過于精細,一定有近似算法澈段,在較低計算量的情況下實現不差于它的效果悠菜。經過提前答辯時助教的點撥才意識到了問題所在,流體不是通過對每個粒子都進行渲染組成的败富,而是根據粒子的位置計算出周圍流體的位置悔醋,然后只對流體看得到的部分進行渲染。
但是由于提前答辯兽叮,有些倉促芬骄,在sph算法和交互上花了過多的時間,導致時間沒有按照打分點來分配鹦聪,甚至寫著代碼跨了年账阻,在其它方面的效果不是很好。有很多需要提升的空間泽本,尤其是SPH算法淘太,自己花了大量的力氣但渲染這塊沒做好導致最后的效果不好,之后會深入研究规丽,爭取能夠實現一開始的設想效果蒲牧。
總的來說,此次大作業(yè)的完成過程中嘁捷,學到了很多造成,同時也收獲了一定的成就感,后續(xù)的改善空間也很大雄嚣,將來如果要實現adobe宣傳片中那種酷炫的效果晒屎,此次作業(yè)也能提供很多參考喘蟆。
最后,感謝老師的教學和助教的指導鼓鲁,以及由于提前答辯獲得了很多來自老師和助教的反饋蕴轨,收貨很大。
代碼量:pc端約1400? 安卓端約200
參考博客/論文:
[1]動態(tài)的水面模擬骇吭,http://blog.csdn.net/zju_fish1996/article/details/52317363
[2]OpenGL中基于粒子系統(tǒng)的噴泉模擬實現橙弱,https://wenku.baidu.com/view/c79b56d476eeaeaad1f33068.html
[3]SPH算法簡介 https://thecodeway.com/blog/?p=83
Learnopengl? https://learnopengl-cn.github.io/intro/