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

【CG物理模擬系列】流體模擬--粒子法之SPH(程式碼講解)

#include "sph_system.h"
#include "sph_header.h"

SPHSystem::SPHSystem()
{
	max_particle=30000;
	num_particle=0;

	kernel=0.04f;
	mass=0.02f;
	//初始化空間大小,並計算網格size
	world_size.x=0.64f;
	world_size.y=0.64f;
	world_size.z=0.64f;
	cell_size=kernel;
	grid_size.x=(uint)ceil(world_size.x/cell_size);
	grid_size.y=(uint)ceil(world_size.y/cell_size);
	grid_size.z=(uint)ceil(world_size.z/cell_size);
	tot_cell=grid_size.x*grid_size.y*grid_size.z;
	
	gravity.x=0.0f; 
	gravity.y=-6.8f;
	gravity.z=0.0f;
	wall_damping=-0.5f;
	rest_density=1000.0f;	//初始密度
	gas_constant=1.0f;		//氣體系數
	viscosity=6.5f;			
	time_step=0.003f;
	surf_norm=6.0f;
	surf_coe=0.1f;

	poly6_value=315.0f/(64.0f * PI * pow(kernel, 9));;
	spiky_value=-45.0f/(PI * pow(kernel, 6));
	visco_value=45.0f/(PI * pow(kernel, 6));

	grad_poly6=-945/(32 * PI * pow(kernel, 9));
	lplc_poly6=-945/(8 * PI * pow(kernel, 9));

	kernel_2=kernel*kernel;
	self_dens=mass*poly6_value*pow(kernel, 6);
	self_lplc_color=lplc_poly6*mass*kernel_2*(0-3/4*kernel_2);

	mem=(Particle *)malloc(sizeof(Particle)*max_particle);
	cell=(Particle **)malloc(sizeof(Particle *)*tot_cell);

	sys_running=0;
}

SPHSystem::~SPHSystem()
{
	free(mem);
	free(cell);
}

void SPHSystem::update()
{
	if(sys_running == 0)
	{
		return;
	}

	build_table();
	comp_dens_pres();
	comp_force_adv();
	advection();
}

void SPHSystem::init_system()
{
	float3 pos;
	float3 vel;

	vel.x=0.0f;
	vel.y=0.0f;
	vel.z=0.0f;

	for(pos.x=world_size.x*0.2f; pos.x<world_size.x*0.8f; pos.x+=(kernel*0.5f))
	{
		for(pos.y=world_size.y*0.5f; pos.y<world_size.y*0.9f; pos.y+=(kernel*0.5f))
		{
			for(pos.z=world_size.z*0.2f; pos.z<world_size.z*0.8f; pos.z+=(kernel*0.5f))
			{
				add_particle(pos, vel);
			}
		}
	}

	printf("Init Particle: %u\n", num_particle);
}
//新增粒子
void SPHSystem::add_particle(float3 pos, float3 vel)
{
	Particle *p=&(mem[num_particle]);

	p->id=num_particle;

	p->pos=pos;
	p->vel=vel;

	p->acc.x=0.0f;
	p->acc.y=0.0f;
	p->acc.z=0.0f;
	p->ev.x=0.0f;
	p->ev.y=0.0f;
	p->ev.z=0.0f;

	p->dens=rest_density;
	p->pres=0.0f;

	p->next=NULL;

	num_particle++;
}

void SPHSystem::build_table()
{
	Particle *p;
	uint hash;

	for(uint i=0; i<tot_cell; i++)
	{
		cell[i]=NULL;
	}
	//建立hash表
	for(uint i=0; i<num_particle; i++)
	{
		p=&(mem[i]);
		hash=calc_cell_hash(calc_cell_pos(p->pos));

		if(cell[hash] == NULL)
		{
			p->next=NULL;
			cell[hash]=p;
		}
		else
		{
			p->next=cell[hash];
			cell[hash]=p;
		}
	}
}

void SPHSystem::comp_dens_pres()
{
	Particle *p;		//當前粒子
	Particle *np;		//周圍粒子

	int3 cell_pos;		//粒子所屬cell座標
	int3 near_pos;		//周圍粒子座標
	uint hash;

	float3 rel_pos;
	float r2;

	for(uint i=0; i<num_particle; i++)
	{
		p=&(mem[i]); 
		cell_pos=calc_cell_pos(p->pos);		

		p->dens=0.0f;
		p->pres=0.0f;

		for(int x=-1; x<=1; x++)
		{
			for(int y=-1; y<=1; y++)
			{
				for(int z=-1; z<=1; z++)
				{
					near_pos.x=cell_pos.x+x;
					near_pos.y=cell_pos.y+y;
					near_pos.z=cell_pos.z+z;
					hash=calc_cell_hash(near_pos);

					if(hash == 0xffffffff)
					{
						continue;
					}

					np=cell[hash];
					//掃描kernel_2範圍內的粒子
					while(np != NULL)
					{
						rel_pos.x=np->pos.x-p->pos.x;
						rel_pos.y=np->pos.y-p->pos.y;
						rel_pos.z=np->pos.z-p->pos.z;
						r2=rel_pos.x*rel_pos.x+rel_pos.y*rel_pos.y+rel_pos.z*rel_pos.z;

						if(r2<INF || r2>=kernel_2)
						{
							np=np->next;
							continue;
						}

						p->dens=p->dens + mass * poly6_value * pow(kernel_2-r2, 3);

						np=np->next;
					}
				}
			}
		}
		//密度及壓力
		p->dens=p->dens+self_dens;
		p->pres=(pow(p->dens / rest_density, 7) - 1) *gas_constant;	//這裡使用了Tait方程,詳見理論那篇文章
	}
}

void SPHSystem::comp_force_adv()
{
	Particle *p;	//當前粒子
	Particle *np;	//周圍粒子

	int3 cell_pos;	//所屬cell位置
	int3 near_pos;	//檢測周圍粒子位置
	uint hash;

	float3 rel_pos;
	float3 rel_vel;

	float r2;
	float r;
	float kernel_r;
	float V;

	float pres_kernel;
	float visc_kernel;
	float temp_force;

	float3 grad_color;
	float lplc_color;

	for(uint i=0; i<num_particle; i++)
	{
		p=&(mem[i]); 
		cell_pos=calc_cell_pos(p->pos);

		p->acc.x=0.0f;
		p->acc.y=0.0f;
		p->acc.z=0.0f;

		grad_color.x=0.0f;
		grad_color.y=0.0f;
		grad_color.z=0.0f;
		lplc_color=0.0f;
		
		for(int x=-1; x<=1; x++)
		{
			for(int y=-1; y<=1; y++)
			{
				for(int z=-1; z<=1; z++)
				{
					near_pos.x=cell_pos.x+x;
					near_pos.y=cell_pos.y+y;
					near_pos.z=cell_pos.z+z;
					hash=calc_cell_hash(near_pos);

					if(hash == 0xffffffff)
					{
						continue;
					}

					np=cell[hash];
					while(np != NULL)
					{
						rel_pos.x=p->pos.x-np->pos.x;
						rel_pos.y=p->pos.y-np->pos.y;
						rel_pos.z=p->pos.z-np->pos.z;
						r2=rel_pos.x*rel_pos.x+rel_pos.y*rel_pos.y+rel_pos.z*rel_pos.z;

						if(r2 < kernel_2 && r2 > INF)
						{
							r=sqrt(r2);
							V=mass/np->dens/2;
							kernel_r=kernel-r;
							//計算壓力項
							pres_kernel=spiky_value * kernel_r * kernel_r;
							temp_force=V * (p->pres+np->pres) * pres_kernel;
							p->acc.x=p->acc.x-rel_pos.x*temp_force/r;
							p->acc.y=p->acc.y-rel_pos.y*temp_force/r;
							p->acc.z=p->acc.z-rel_pos.z*temp_force/r;

							rel_vel.x=np->ev.x-p->ev.x;
							rel_vel.y=np->ev.y-p->ev.y;
							rel_vel.z=np->ev.z-p->ev.z;
							//計算粘性項
							visc_kernel=visco_value*(kernel-r);
							temp_force=V * viscosity * visc_kernel;
							p->acc.x=p->acc.x + rel_vel.x*temp_force; 
							p->acc.y=p->acc.y + rel_vel.y*temp_force; 
							p->acc.z=p->acc.z + rel_vel.z*temp_force; 

							float temp=(-1) * grad_poly6 * V * pow(kernel_2-r2, 2);
							grad_color.x += temp * rel_pos.x;
							grad_color.y += temp * rel_pos.y;
							grad_color.z += temp * rel_pos.z;
							lplc_color += lplc_poly6 * V * (kernel_2-r2) * (r2-3/4*(kernel_2-r2));
						}

						np=np->next;
					}
				}
			}
		}

		lplc_color+=self_lplc_color/p->dens;
		p->surf_norm=sqrt(grad_color.x*grad_color.x+grad_color.y*grad_color.y+grad_color.z*grad_color.z);

		if(p->surf_norm > surf_norm)
		{
			p->acc.x+=surf_coe * lplc_color * grad_color.x / p->surf_norm;
			p->acc.y+=surf_coe * lplc_color * grad_color.y / p->surf_norm;
			p->acc.z+=surf_coe * lplc_color * grad_color.z / p->surf_norm;
		}
	}
}

void SPHSystem::advection()
{
	Particle *p;
	for(uint i=0; i<num_particle; i++)
	{
		p=&(mem[i]);
		//速度及位置更新
		p->vel.x=p->vel.x+p->acc.x*time_step/p->dens+gravity.x*time_step;
		p->vel.y=p->vel.y+p->acc.y*time_step/p->dens+gravity.y*time_step;
		p->vel.z=p->vel.z+p->acc.z*time_step/p->dens+gravity.z*time_step;

		p->pos.x=p->pos.x+p->vel.x*time_step;
		p->pos.y=p->pos.y+p->vel.y*time_step;
		p->pos.z=p->pos.z+p->vel.z*time_step;
		//牆壁
		if(p->pos.x >= world_size.x-BOUNDARY)
		{
			p->vel.x=p->vel.x*wall_damping;
			p->pos.x=world_size.x-BOUNDARY;
		}

		if(p->pos.x < 0.0f)
		{
			p->vel.x=p->vel.x*wall_damping;
			p->pos.x=0.0f;
		}

		if(p->pos.y >= world_size.y-BOUNDARY)
		{
			p->vel.y=p->vel.y*wall_damping;
			p->pos.y=world_size.y-BOUNDARY;
		}

		if(p->pos.y < 0.0f)
		{
			p->vel.y=p->vel.y*wall_damping;
			p->pos.y=0.0f;
		}

		if(p->pos.z >= world_size.z-BOUNDARY)
		{
			p->vel.z=p->vel.z*wall_damping;
			p->pos.z=world_size.z-BOUNDARY;
		}

		if(p->pos.z < 0.0f)
		{
			p->vel.z=p->vel.z*wall_damping;
			p->pos.z=0.0f;
		}

		p->ev.x=(p->ev.x+p->vel.x)/2;
		p->ev.y=(p->ev.y+p->vel.y)/2;
		p->ev.z=(p->ev.z+p->vel.z)/2;
	}
}
//計算粒子p所屬的cell位置
int3 SPHSystem::calc_cell_pos(float3 p)
{
	int3 cell_pos;
	cell_pos.x = int(floor((p.x) / cell_size));
	cell_pos.y = int(floor((p.y) / cell_size));
	cell_pos.z = int(floor((p.z) / cell_size));

    return cell_pos;
}
//cell hash值計算
uint SPHSystem::calc_cell_hash(int3 cell_pos)
{
	if(cell_pos.x<0 || cell_pos.x>=(int)grid_size.x || cell_pos.y<0 || cell_pos.y>=(int)grid_size.y || cell_pos.z<0 || cell_pos.z>=(int)grid_size.z)
	{
		return (uint)0xffffffff;
	}

	cell_pos.x = cell_pos.x & (grid_size.x-1);  
    cell_pos.y = cell_pos.y & (grid_size.y-1);  
	cell_pos.z = cell_pos.z & (grid_size.z-1);  

	return ((uint)(cell_pos.z))*grid_size.y*grid_size.x + ((uint)(cell_pos.y))*grid_size.x + (uint)(cell_pos.x);
}
沒有繪製水面,只是實現了最基本的SPH演算法原理,以供參考。
以上~~

參考文獻 

Matthias Müller, David Charypar, and Markus Gross,  Particle-Based Fluid Simulation for Interactive Applications, SCA 2003.



相關推薦

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物理模擬系列流體模擬--粒子MPS(理論)

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

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

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

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

NOIP2017模擬二分圖+狀態壓縮DP Graph好題

題解 這道題其實是一個NP完全問題(23333),但是由於資料小啊,我們可以搞一搞。很容易發現,如果我們將一個點拆成兩個點,一個代表出點,一個代表入點。當增加了一條有向邊,就出點向入點連一條邊

NOIP2017模擬思維+轉化+圖論 徒然Children好題

題目描述 全世界都在談戀愛,只有我深愛著學習。——前言。 2333身為單身狗卻意外喜歡的吃狗糧的yuki堅信著:“自古紅藍出cp,黑白天生是夫妻,最是銷魂紅綠配,天然金紫成雙對,生死相隨紅與黃,白紫一逢春滿堂,千里緣牽白與綠,紅黑生來為相聚。”的cp配對原則

學會Matlab走遍天下如何畫正弦餘弦曲線和學習筆記

常用命令: clc %清屏 clear + 變數 %將變數擦除 註釋符:% 矩陣建立 邏輯語法 sum=0;i=1; while(i<=100) sum=sum+i;i=i+1; sum

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

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

Java入門提高篇Day34 Java容器類詳解十五WeakHashMap詳解

public class WeakHashMapTest { public static void main(String[] args){ testWeakHashMap(); } private static void testWeakHashMap

從原始碼看Android03Android MessageQueue訊息迴圈處理機制epoll實現

1 enqueueMessage handler傳送一條訊息 mHandler.sendEmptyMessage(1);經過層層呼叫,進入到sendMessageAtTime函式塊,最後呼叫到enqueueMessageHandler.java public bool

拔刀吧TensorFlowUbuntu16.04系統安裝問題總結已更新

重大更新!!!!! 因為You do not appear to be using the NVIDIA X driver. 這樣的報錯,感覺雖然安裝了nvidia驅動,但是並沒有呼叫起來驅動,遂決定再次重做系統。這次重做的步驟如下: 1. 重做系統。 2. 重啟之後發現可以雙屏顯示,解析度正

休閒遊戲 實戰1推箱子PC端小遊戲附原始碼

效果圖:第100關有些難度,用了449步才過關(我用的是可跳關版的,直接玩的最後一關)原始碼解讀原始碼一共3個檔案:index.html(遊戲介面載入,核心功能),js/mapdata100.js(100個16階矩陣,通過0,1,2,3,4分別對應遊戲中5個元素的圖片來定義每

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

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

OCP題庫-12c最新CUUG OCP 071考試題庫70題

values mman ble ans delete best cau save first 70、(31-2)choose the best answer: View the Exhibit and examine the structure of the Book ta

OCP題庫-12c最新CUUG OCP 071考試題庫69題

sel query dual 試題 owin ans lua The select 69、(31-1)choose the best answer: Evaluate the following query: SELECT INTERVAL ‘300‘ MONTH, INT

OCP題庫-12c最新CUUG OCP 071考試題庫71題

試題 nts 考試題庫 point choose com ued transacti mit 71、(32-18) choose three Which three statements indicate the end of a transaction? (Choose

OCP題庫-12c最新CUUG OCP 071考試題庫72題

reg ive answer statement splay and ren 考試 display 72、View the exhibit for the structure of the STUDENTand FACULTYtables. STUDENT Name Nul

編程訓練-PATA1119 Pre- and Post-order Traversals 30 分

lse vector txt cif sap push_back you any nodes 1119 Pre- and Post-order Traversals (30 分) Suppose that all the keys in a binary tree are

NOIP 模擬模擬+鏈表

color blog 不常用 get 循環鏈表 fig problem bsp 常用 biubiu~~ 這道題實際上就是優化模擬,就是找到最先死的讓他死掉,運用時間上的加速,題解上說,要用堆優化,也就是這個意思。 對於鏈表,單項鏈表和循環鏈表都不常用,最常用的是雙向鏈表

模擬登陸github模擬登陸,列印資訊流

目的:動態獲取cookie 第一:分析登陸過程 1、開啟開發者工具,檢視各自請求 2、可以看到name為session的請求【方式post,傳入的data】 3、檢視name為login的請求,原始碼中獲得token,作為上一個請求中的data的一部分