1. 程式人生 > >【CG物理模擬系列】流體模擬--粒子法之SPH(實現)

【CG物理模擬系列】流體模擬--粒子法之SPH(實現)

鄰域搜尋的效率化 SPH等粒子法,由於需要考慮到鄰域粒子帶來的影響,通常鄰域搜尋都會消耗大量時間。如果我們只是單純的計算所有粒子組合的歐氏距離的話,計算時間只會呈指數增加。 而空間分割法的出現,使鄰域搜尋實現了效率化。 空間分割法是一種,把希望檢索到的物體存在的空間以格子等方式分隔開,只計算自身及相鄰分割領域內包含物體的距離,可以使計算時間大幅減少的方法。空間分割法的處理順序是,
  1. 物體的記錄
    1. 分割空間
    2. 計算出各物體所屬的分割領域
  2. 鄰域搜尋
    1. 計算出 搜尋中心座標所屬區域
    2. 列出自身和相鄰區域的物體
    3. 計算列出物體間的距離 
如上所述,分成記錄和檢索兩部分。記錄只在場景中的物體(粒子)位置變化的時候實行,檢索則是在有必要的時候連續進行。 空間分割法的難點在於空間以怎樣的方式分割。其代表性方法為,
  • 等間距格子(下圖左)
  • 八叉樹,四叉樹構造(下圖右)
  • kD樹構造
等等。下圖的四叉樹構造,與等間距網格相比,檢索效率更高,但是物體記錄卻更費事。使用粒子法的時候,為了使每一幀粒子都能實現動態移動,每一幀都要進行區域記錄。為此,分割·記錄較為容易的等間距網格法則會被經常使用。等間距網格,與有分層結構的其他分割方法相比,更容易進行記錄同步化處理。  particle_grid.gif particle_octree.gif 図1 等間距格子(左)和四叉樹(左)

GPU的實現

首先,介紹使用Particle Simulation using CUDA進行粒子搜尋方法,如圖所示。

particle_grid_2.gif 図2 網格和粒子 第一步進行粒子記錄處理。順序如下。
  1. 計算各粒子的網格雜湊值。雜湊值是各網格單元特有的,可以用來處理網格單元的位置資訊。 這裡,從網格單元的位置(i,j)計算hash值為
    hash = i+j・nx
    
    (nx,ny)是網格單元的數量,根據圖2,各區域的左下角寫的數字就是hash值(nx=2)。 雜湊值儲存在GridParticleHash序列中。同時,在SortedIndex中儲存粒子的索引值。(GridParticleHash,SortedIndex的size = 粒子數)
    GridParticleHash 2 0 0 1 1 0 1 2
    SortedIndex 0 1 2 3 4 5 6 7
  2. 以網格雜湊值作為key排序。這裡使用 排序。
    GridParticleHash 0 0 0 1 1 1 2 2
    SortedIndex 1 2 5 3 4 6 0 7
  3. 儲存GridParticleHash中值相同的連續區域(相同分割區域)的起始與終止索引(CellStart好CellEnd序列)。 首先,以0xffffffff 初始化序列(size=index數)。
    CellStart 0xff 0xff 0xff 0xff
    CellEnd 0xff 0xff 0xff 0xff
    搜尋排序後GridParticleHash各元素i的前一元素(i-1)的hash值。
    GridParticleHash[i] != GridParticleHash[i-1]
    
    這樣的話,以i作為起始點CellStart[GridParticleHash[i]], i-1作為前一個網格單元的終點CellEnd[GridParticleHash[i-1]]儲存。
    CellStart 0 3 6 0xff
    CellEnd 2 5 7 0xff
    這時,使用SortedIndex對粒子位置等的儲存序列進行排序。
    SortedPos[i] = Pos[SortedIndex[i]];
    
鄰域搜尋的順序如下。
  1. 算出粒子位置(or 任意座標) x 網格單元
  2. 對包含周圍網格在內進行如下處理
    1. 計算網格單元的雜湊值hash
    2. 按照CellStart,CellEnd(CellStart[hash],CellEnd[hash]),對網格內粒子的起始與終止索引(start, end)進行處理
    3. for(int j = start; j <= end; ++j)
      1. 鄰域候選粒子位置pj如下
        pj=SortedPos[j]
        
      2. 若|pj-x|<h成立,粒子為鄰域粒子。
使用SPH法的時候,通過計算與臨近搜尋階段最後找到的臨近粒子間的距離來計算kernel。再以有效半徑h為依據,判斷是否繼續執行。  各粒子的密度計算程式碼如下所示。
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
/*!
 * 通過單元內粒子見得距離計算密度
 * @param[in] gridPos 網格位置
 * @param[in] index 粒子索引
 * @param[in] pos 計算座標
 * @param[in] cell 粒子網格資料
 * @return 密度值
 */
__device__
float calDensityCell(int3 gridPos, uint i, float3 pos0, rxParticleCell cell)
{
    uint gridHash = calcGridHash(gridPos);
 
    // cell內粒子的開始索引
    uint startIndex = cell.dCellStart[gridHash];
 
    float h = params.EffectiveRadius;
    float dens = 0.0f;
    if(startIndex != 0xffffffff){    // cell非空
        // cell內粒子迴圈
        uint endIndex = cell.dCellEnd[gridHash];
        for(uint j = startIndex; j < endIndex; ++j){
            //if(j == i) continue;
 
            float3 pos1 = make_float3(cell.dSortedPos[j]);
 
            float3 rij = pos0-pos1;
            float r = length(rij);
 
            if(r <= h){
                float q = h*h-r*r;
                dens += params.Mass*params.Wpoly6*q*q*q;
            }
        }
    }
 
    return dens;
}
 
/*!
 * 粒子密度計算(kernel函式)
 * @param[out] newDens 粒子密度
 * @param[out] newPres 粒子壓力
 * @param[in]  cell 粒子網格資料
 */
__global__
void sphCalDensity(float* newDens, float* newPres, rxParticleCell cell)
{
    // 粒子檢索
    uint index = __mul24(blockIdx.x,blockDim.x)+threadIdx.x;
    if(index >= cell.uNumParticles) return;    
    
    float3 pos = make_float3(cell.dSortedPos[index]);    // 粒子位置
    float h = params.EffectiveRadius;
 
    // 粒子周圍的網格
    int3 grid_pos0, grid_pos1;
    grid_pos0 = calcGridPos(pos-make_float3(h));
    grid_pos1 = calcGridPos(pos+make_float3(h));
 
    // 包含周圍網格在內的鄰域搜尋,密度計算
    float dens = 0.0f;
    for(int z = grid_pos0.z; z <= grid_pos1.z; ++z){
        for(int y = grid_pos0.y; y <= grid_pos1.y; ++y){
            for(int x = grid_pos0.x; x <= grid_pos1.x; ++x){
                int3 n_grid_pos = make_int3(x, y, z);
                dens += calDensityCell(n_grid_pos, index, pos, cell);
            }
        }
    }
 
    // 密度值儲存
    uint oIdx = cell.dSortedIndex[index];
    newDens[oIdx] = dens;
}
生成表面mesh 此時,如果直接渲染粒子的話,會看不到流體。特別是透明流體渲染的時候,必須要計算出液體表面的位置和法線。這裡,我們使用三角形mesh生成液體表面。 通過MC(Marching Cubes)法來實現 液體表面生成三角形mesh 。MC法的處理順序為,
  1. 在空間內部署立方grid(Cube)
  2. 用插值法或二分法,牛頓法等 算出各Cube邊上隱函式值為0的點
  3. 從Cube內包含點的構造和數中生成三角形mesh
實現結果 GeForceGTX580環境下。粒子數約25000個,只計算SPH需200fps,加上面的生成約80-90fps。圖3,4是執行結果。 sph_result1.jpg sph_result2.jpg sph_result3.jpg 圖3 左到右:粒子,表面mesh,折射渲染 sph_result4.jpg

原始碼連結

使用FLTK工具包,在Visual Studio 2010 + CUDA5.0環境下, 所需庫
freeglut, GLEW, CUDA, boost, libpng, zlib, libjpeg, ftgl, FLTK

相關推薦

CG物理模擬系列流體模擬--粒子SPH實現

鄰域搜尋的效率化 SPH等粒子法,由於需要考慮到鄰域粒子帶來的影響,通常鄰域搜尋都會消耗大量時間。如果我們只是單純的計算所有粒子組合的歐氏距離的話,計算時間只會呈指數增加。 而空間分割法的出現,使鄰域搜尋實現了效率化。 空間分割法是一種,把希望檢索到的物體存在的空

CG物理模擬系列流體模擬--粒子MPS(理論)

MPS法  前面的文章裡我們講過SPH曾是為了解決壓縮性流體問題而提出的方法,與之相對,這一篇來說說用粒子法處理非壓縮性流體的研究方法--Moving Particle Semi-implici

CG物理模擬系列流體模擬--粒子SPH程式碼講解

#include "sph_system.h" #include "sph_header.h" SPHSystem::SPHSystem() { max_particle=30000; num_particle=0; kernel=0.04f; mass=0.02f; //初始化空間大小,並計算

CG物理模擬系列流體模擬--粒子Position Based Fluids

[1] M. Macklin and M. Muller, "Position based fluids" ACM Trans. Graph., 32, pp.104:1-104:12, 2013. [2] M. Muller, B. Heidelberger, M. Hennix and J. Ratcl

C語言簡單說八:分支結構if1

今天貌似更了很多章了,現在感覺累覺不愛。。。 ┐(—__—)┌ 你說我有啥米辦法咧~(要不叫別人替我更一下?) 繼續更。。。 這一節我們來說一下if語句;這個東西可是很常用的呀;在此之前我們來舉個

webpack系列從零搭建 webpack4+react 腳手架

搭建一個React工程的方式有很多,官方也有自己的腳手架,如果你和我一樣,喜歡刨根究底,從零開始自己一行一行程式碼建立一個React腳手架專案,那你就來對地方了。本教程是針對React新手,以及對webpack還不熟悉的使用者,或者是想了解當前前端工程化方案的使用者。我會在整個系列通過webpack4的

webpack系列從零搭建 webpack4+react 腳手架

html檔案如何也同步到dist目錄?bundle.js檔案修改了,萬一被瀏覽器快取了怎麼辦?如何為匯出的檔案加md5?如何把js引用自動新增到html?非業務程式碼和業務程式碼如何分開打包?如何搭建開發環境?如何實現開發環境的熱更新? 在上一節我們已經搭建了一個最基本的webpack環境,

webpack系列從零搭建 webpack4+react 腳手架

本章節,我們對如何在腳手架中引入CSS,如何壓縮CSS,如何使用CSS Modules,如何使用less,如何使用postcss等問題進行展開學習。 1 支援css (1)在app目錄,新建一個css,命名為index.css,輸入樣式: h1{

webpack系列從零搭建 webpack4+react 腳手架

本章節,我們一起來探討以下問題:如何對編譯後的檔案進行gzip壓縮,如何讓開發環境的控制檯輸出更加高逼格,如何更好的對編譯後的檔案進行bundle分析等。 1 gzip壓縮 如果你想節省頻寬提高網站速度,壓縮是一種簡單有效的方法。我們模擬一次html的請求,想象一下瀏覽器和伺服器的對

Gin-API系列守護程序和平滑重啟

生產環境的API服務我們都會部署在Linux伺服器上,為了不受終端狀態的影響,啟動服務的時候會讓服務在後臺執行。那麼如何讓服務在後臺執行呢,目前有2種常見的方法。 ### 1、nohub 執行 表示忽略`SIGHUP`(結束通話)訊號,終端退出的時候所發起的結束通話訊號會被忽略。`nohup`一般會結合`&a

Java入門提高篇Day5 Java中的回調

彈出對話框 java入門 也會 color 編程 args performed show clas   Java中有很多個Timer,常用的有兩個Timer類,一個java.util包下的Timer,一個是javax.swing包下的Timer,兩個Timer類都有用到回調

python學習第十七篇:Python中的面向物件

1.什麼是類和類的物件? 類是一種資料結構,我們可以用它來定義物件,後者把資料值和行為特性融合在一起,類是現實世界的抽象的實體以程式設計形式出現。例項是這些物件的具體化。類是用來描述一類事物,類的物件指的是這一類事物的一個個體。例如:“人”就是一個類,而男人,女人,小孩等就是“人”這個類的例項物件;再比如“

MYSQL學習筆記02MySQL的高階應用Explain完美詳細版,看這一篇就夠了

版權宣告:本文為博主原創文章,未經博主允許不得轉載。 https://blog.csdn.net/wx1528159409 最近學習MySQL的高階應用Explain,寫一篇學習心得與總結,目錄腦圖如下: 一、Explain基本概念 1. Explain定義 · 我們知道M

ArrayList集合JDK1.8 集合框架JDK1.8原始碼分析ArrayList

簡述   List是繼承於Collection介面,除了Collection通用的方法以外,擴充套件了部分只屬於List的方法。   常用子類  ?ArrayList介紹 1.資料結構   其底層的資料結構是陣列,陣列元素型別為Object型別,即可以存放所

專案管理與構建Nexus的詳細介紹以及安裝

      前面幾篇博文,我們介紹了怎麼使用maven,這篇博文我們簡單的介紹maven的私服Nexus。簡介        Nexus是Maven倉庫管理器,也可以叫Maven的私服。Nexus是一個

資料結構與演算法002—樹與二叉樹Python

概念 樹 樹是一類重要的非線性資料結構,是以分支關係定義的層次結構 定義: 樹(tree)是n(n>0)個結點的有限集T,其中: 有且僅有一個特定的結點,稱為樹的根(root) 當n>1時,其餘結點可分為m(m>0)個互不相交的有限集T1,T2,……Tm,其中每一個集合本身又是一棵

opencv、機器學習opencv中的SVM影象分類

上一篇博文對影象分類理論部分做了比較詳細的講解,這一篇主要是對影象分類程式碼的實現進行分析。理論部分我們談到了使用BOW模型,但是BOW模型如何構建以及整個步驟是怎麼樣的呢?可以參考下面的部落格http://www.cnblogs.com/yxy8023ustc/p/33

opencv、機器學習opencv中的SVM影象分類

一、影象分類概述 本模組是用在影象內容識別的部分,影象分類是利用計算機對影象進行定量分析,把影象中的每個像元或區域劃歸為若干個類別中的一種,以代替人工視覺判讀的技術。從目視角度來說,對影象進行提高對比度、增加視覺維數、進行空間濾波或變換等處理的目的就是使人們能夠憑藉知識

Android開源專案分析android輕量級開源快取框架——ASimpleCacheACache原始碼分析

ASimpleCache框架原始碼連結 官方介紹 ASimpleCache 是一個為android制定的 輕量級的 開源快取框架。輕量到只有一個java檔案(由十幾個類精簡而來)。 1、它可以快取什麼東西? 普通的字串、J

百度地圖高階例項1-如何利用百度地圖API,製作房產酒店地圖?

<!DOCTYPE html><html><head><meta http-equiv="Content-Type" content="text/html; charset=gb2312"/><title>酷訊酒店地圖</title>