高度场流体模拟

news/2025/3/30 18:02:35/文章来源:https://www.cnblogs.com/uwatech/p/18795750

【USparkle专栏】如果你深怀绝技,爱“搞点研究”,乐于分享也博采众长,我们期待你的加入,让智慧的火花碰撞交织,让知识的传递生生不息!


一、原理

参考这个论文:《Real-time Simulation of Large Bodies of Water with Small Scale Details》

核心是这两个公式:

 

我在这篇《波动方程与浅水方程》中作了说明,转贴如下:

浅水方程:
(1)动量方程

 

(2)质量守恒

 

其实浅水方程的动量方程,就是在NS方程的动量方程

 

中令p=ρgη(η为海拔)得来,物理含义是表面隆起(或凹陷)产生压强(或负压强)。注意η和h的区别,h是水深,η是海拔,如果设河床高度为H,则有η=H+h。H是x,y的函数,h是x,y,t的函数。

对于普通水可以忽略粘性,进一步简化为:

 

论文中所用公式为:

 

这其实就是浅水方程。说明如下:
D为物质导数,定义为

 

将(2)式中物质导数展开得

 

可见就是浅水方程的动量方程忽略掉粘性项。再看(1)式,将其中物质导数展开得

 

又因为h是标量v是向量且均为空间的函数,根据散度的运算性质,有

 

代入得

 

可见就是浅水方程的质量守恒方程。

注:

  1. 浅水方程的动量方程(2)用的是海拔η,质量守恒方程(1)用的是水深h,这正是它可以适用于凹凸不平地形,模拟洪泛的原因。假如我们强行让两方程都用h,那就相当于是假定海拔等于水深,即水底是平面,则不再具备模拟洪泛的能力,但仍可模拟水波。

质量守恒方程:

 

求解,对应论文中:

 

动量方程:

 

求解,其中求解对流项:

 

对应论文中:

 

由于没给公式,我暂时忽略了这项。

求解压力和外力项:

 

对应论文中:

 

二、MAC网格

论文中的离散化形式,含有:

 

这种一半的项,是因为引入了称作MAC网格的技巧,即将流入/流出速度看作是在cell的边上,在处理散度相关问题时,经常用到。

下图很直观:
Eulerian Fluid Simulation Notes [Part 1]
Bridson's notes:nov17-cs533d-slides.ppt

 

 

三、实现

最终是要用Shader实现,但初期验证算法出于Debug方便考虑,还是先用Houdini实现。

当然,我们可以真的在Houdini里建一个非常“具象的”MAC网格,就是每个格子表示一个cell,cell的边界上可以附加速度向量。

但为了将来转Shader更直接,我用的更接近二维数组的结构,我用一个二维点阵代表Grid,每个Point代表一个cell,Point有如下属性:

f@h=0;f@u_r=0;//即u[i+1/2,j]

f@w_d=0;//即w[i,j+1/2]

f@h_r=0;//即h[i+1/2,j]

f@h_d=0;//即h[i,j+1/2] 

 

至于为啥没有u_l(u[i-1/2,j]),w_u(w[i,j-1/2]),h_l(h[i-1/2,j]),h_u(h[i,j-1/2]),是因为当前Point的u_l其实就是左边格子的u_r,即它已经在左边格子中存过的,就没有必要在此格中再存一遍了。如果我们想获得u_l,只需如下:

int ID=@ptnum;int i=floor(ID/N);int j=ID%N;int ID_L=j*N+i-1;float u_l=point(0,"u_r",ID_L)

 

w_u,h_l,h_u同理。

这也正是论文中这段:

 

只给出




表达式,而没有写



表达式的原因。因为当前cell的



正是左边cell和上边cell(假设我的Grid是y轴向下)的


 

只要左边cell和上边cell已经计算过,就可以直接访问而无需在本cell中再重新计算一次。

同理,下面这段只给



 

也是同样原因:

 

然后,就只是抄公式了。

四、稳定性

由于是离散模拟,而且用的是最简单的显式积分,所以会有数值稳定性问题,论文中列了几个措施可以提高稳定性:

Overshooting Reduction:
在论文的2.2节,提到有水与没水交界处,h的振幅会因数值问题而被放大,导致边缘出现尖刺甚至撕裂,用如下方法缓解:

 

Stability Enhancements:
在论文的2.1.5节,提到如下约束可提高稳定性:

 

五、参数对效果的影响

g,α,Δx,Δt对效果均有影响。

例如,不同α值对比:

α=0.5

 

α=0.6

 

可见,α=0.5和α=0.6相比,由于速度限制更狠,显得更粘稠。

以上我们实现了凹凸不平地形上的浅水方程模拟,但忽略了对流项。

以下作了些改进,主要添加了对流项、涡度约束和边界条件。

效果

 

六、添加对流项

对流项:

 

有两种实现方式:一种是Gather方法,一种是直接离散化方法。

(一)Gather方法
实际上在Gather方法之前,还有一个Scatter方法,这两种方法都是抛开对流的数学公式,而直接从公式背后的物理含义出发的模拟方法。Scatter方法就是把当前cell的属性传输出去,Gather方法就是把上游点的属性传输给当前cell。在《NS方程与2d流体模拟》中说过,Gather方法由于对GPU友好,更常用。

对流的Gather方法实现,可以参考《游戏中的流体模拟(Fluid Simulation),技术美术教程 》。

由于我要在之前的基础上加对流,而那个模拟过程中只使用到边速度,但上面教程中的实现用的是cell速度,所以需要在边速度和cell速度之间互转。

过程如下:
Pass1:由边速度计算cell速度。

Pass2:计算对流Advect。
1.用9个格加权平均估算当前cell平滑速度。
2.将当前cell平滑速度乘以dt再反向,以此作为偏移量去采样上游目标点的速度(用双线性插值)。
3.用采到的速度替换当前cell的速度。

Pass3:由cell速度计算边速度。
其中需要注意的点有:
1.边速度与cell速度之间的互转。
我问的ChatGPT:

 

由于两个问题我是连续问的,所以它给出的公式也恰好是互逆的。我验证了一下,如果把Pass2屏蔽,只剩Pass1和Pass3,结果确实不受影响。

2.无水处的速度
用9个cell的速度加权平均计算当前cell平滑速度时,不用管各cell是否有水,照加不误。采样上游点速度覆盖当前cell速度时,也不用管上游点处是否有水,照覆盖不误。原因是,只要能保证无水处速度是0,上面的计算就都是合理的。至于如何保证无水处速度为0,见后面"边界条件"一节。

3.数值稳定性及对流快慢和强度控制
以下两项措施可以提高对流的数值稳定性:
(1)缩短步长:将偏移-V*dt采样改为偏移-V*dt*0.5采样,这样虽然会导致对流变慢(如果滴入墨水的话,体现为墨水扩散变慢),但有助于数值稳定性。如果将步长减半的同时又不想对流变慢,可以在一帧内将对流迭代两次。

(2)减弱影响:将“用目标点速度覆盖当前点速度”这一步,改为不完全覆盖,而是按一定比例(如0.5)进行混合。这样虽然会使对流效果减弱,但也有助于数值稳定性。

是否缩短步长和减弱影响,也未必总是出于数值稳定性考虑,有时候为了效果,也需要对对流的快速和强度进行控制。

4.边框处理
计算当前cell平滑速度时,9个cell中超出边框者要去掉。采样上游点时,如果上游点超出边框,则放弃当前cell的对流计算。

(二)直接离散化方法
就是将

 

展开为(设v=(u,w)):

 

其中右边各项离散化如下:

 

这里用的是迎风格式,参考ChatGPT:

 

同样可以用缩短步长和减弱影响对对流快慢和强度进行控制,以及提高数值稳定性。

七、添加墨水

要想让对流效果明显地显示出来,需要添加墨水。
方法如下:
1.标记一些cell为inkSource。
2.每帧对标为inkSource的cell追加ink值:ink+=inkAmount*dt
3.在解算对流时:
如果用的是Gather方法,则除了用上游点的速度覆盖当前点速度外,还要用上游点ink覆盖当前点ink。如果用的是直接离散化方法,则除了计算du、dw,(u,w)+=(du,dw),还要计算dink,ink+=dink。
4.最后将ink可视化。

八、两种对流方法效果对比

左边为Gather,右边为直接离散化

 

可见,大致效果接近,由于两种方法很不同,所以基本可佐证实现是正确的。

九、添加涡度约束

方法在《NS方程与2d流体模拟》中已经写过。

需要注意的一点是,如果GetCurl函数中没有除以Δx,则要在外面补上。另外为了让效果与时间解耦,最终速度调整量还应乘以Δt。

有无涡度约束对比:


左:无涡度约束,右:有涡度约束

 

可见,区别还是很明显的。

十、添加边界条件

1.边框处理
在之前的文章中,我没有限制水不能流出边框,所以水一直沿着河道流,永远不会漫上来。

这次,我加了水不能流出边框的限制,只需将边框视作障碍物,每帧末尾将边框cell的边速度和cell速度均标为0即可。这在论文《hfFluid.pdf》中有写:

 

2.使无水处速度为0
浅水方程本身是无法保证无水处速度为0的,因为从

 

来看,当无水,即h=0时,变为

 

即仍会由于地形本身有梯度而产生速度。所以,要想让无水处速度为0,只能通过人为添加约束实现。

其实,上面Boundary Conditions这段已经给出了方法,就是通过

 

判断右侧边(i+1/2,j)是否为“岸”。类似方法也可以判断出左、上、下各边是否为岸。

而且分析一下会发现,这种判断方法下,不仅岸与非岸的交界边会被判为岸,岸上的边也都会被判为岸,这正是我们想要的。于是对于判断为岸的边,我们在每帧末尾将其边速度强制设为0,则可实现无水处速度为0的目标。至于cell速度,由于它是由边速度算出的,所以只要边速度标对了,cell速度自然也就对了。

另外,由于我们是在每帧末尾将无水处速度标为0的,所以我将对流的计算放在最前面,以保证对流计算是在无水cell速度为0的情况下进行的。

下图是不加岸边速度置0(左)与加岸边速度置0(右),速度场及最终效果对比:


左:不加岸边速度置0,右:加岸边速度置0

 

看起来似乎差别不大,但有一点,如果近看的话,会发现岸边速度不置0的模拟结果,水会在岸边隆起,不太合理,而加了岸边速度置0的模拟结果,水与岸边则是比较平滑的过度,如图所示:


左:不加岸边速度置0,右:加岸边速度置0

 


这是侑虎科技第1787篇文章,感谢作者杨超wantnon供稿。欢迎转发分享,未经作者授权请勿转载。如果您有任何独到的见解或者发现也欢迎联系我们,一起探讨。(QQ群:793972859)

作者主页:https://www.zhihu.com/people/wantnon

再次感谢杨超wantnon的分享,如果您有任何独到的见解或者发现也欢迎联系我们,一起探讨。(QQ群:793972859)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/906636.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Navicat将微软数据库MS-SQLServer表内容导入MySQL数据库

前言全局说明一、说明 1.1 环境: Windows 7 旗舰版 MSSQL 2012 Navicat for MySQL 10.1.7二、MySQL准备 用 Navicat 在 mysql 新建数据库,要和 MSSQL 数据库同名注意:编码也要一致2.1 mysql 新建数据 空白处新,建 test 数据库,2.2 数据库右键查看在mysql里新建数据库编码三…

深度解析:通过 AIBrix 多节点部署 DeepSeek-R1 671B 模型

本文详细介绍了如何通过 AIBrix 分布式推理平台实现 DeepSeek-R1 671B 的多节点部署。DeepSeek-R1 通过渐进式训练框架展现出优秀的逻辑推理能力 —— 在 6710 亿总参数量中,其动态激活的 370 亿参数与 128k 上下文窗口,使其在复杂任务处理中表现卓越。然而,如此庞大的模型规…

玄机靶场 第一章 应急响应-webshell查杀

玄机靶场 第一章 应急响应-webshell查杀 1.黑客webshell里面的flag flag2. 黑客使用的什么工具的shell github地址的md5 flag 哥斯拉webshell的特征3.黑客隐藏shell的完整路径的md5 flag{md5} 注 : /xxx/xxx/xxx/xxx/xxx.xxx 发现隐藏4.黑客免杀马完整路径 md5 flag 查看这是一…

玄机靶场 第一章 应急响应-Linux日志分析

玄机靶场 第一章 应急响应-Linux日志分析 1.有多少IP在爆破主机ssh的root帐号,如果有多个使用","分割 /var/log/auth.log里面存放了相关的登录信息 直接下载看根据user=root发现三个ip 网上发现神奇妙妙脚本 cat auth.log.1 | grep -a "Failed password for ro…

【每日一题】20250327

改变不了的事,不值得烦恼。【每日一题】 1.(15分) \(\hspace{0.7cm}\)已知数列 \(\{a_n\}\),\(\{b_n\}\) 和 \(\{c_n\}\) 满足: \[a_{n+1}=\frac14b_n \]\[b_{n+1}=a_n+c_n+\frac12b_n \]\[c_{n+1}=\frac14b_n \]且 \(a_n+b_n+c_{n}=1\),\(a_1=0\),\(b_1=1\),\(\displa…

必看!SpringAI轻松构建MCP Client-Server架构

MCP 这个概念相信大家已经听了无数次了,但不同人会有不同的解释,你可能也是听得云里雾里的。 不过没关系,今天这篇内容会通过 Spring AI 给你实现一个 MCP 的 Client 和 Server 架构,让你彻底搞懂 MCP 的概念,以及学会 MCP 的开发技能。 什么是MCP? MCP 是 Model Context…

国产服务器操作系统CTyunOS,技能值拉满!

新一轮科技革命和产业变革深入发展,数字经济迎来新的发展机遇。其中,以操作系统为代表的关键基础软件是新一代信息技术的灵魂,它不仅构筑起信息技术领域的根基磐石,更在守护关键信息基础设施的安全防线、实现核心技术自主可控中,发挥着不可替代的作用。 作为云服务国家队…

CSS 实现自定义滚动条样式

CSS 实现自定义滚动条样式CSS 实现自定义滚动条样式 要兼容主流浏览器(Chrome、Safari、Edge、Firefox 等),需要综合使用 Webkit 的 ::-webkit-scrollbar 伪元素和 Firefox 支持的 scrollbar-width 以及 scrollbar-color 属性。由于目前主流浏览器中只有 Webkit 内核和 Fire…

重置OpenEuler操作系统的root管理员密码

(1)启动OpenEuler系统或在终端执行重启系统命令reboot,在系统引导界面按“e”键进入内核编辑界面。成功进入内核编辑模式界面,要求输入username,这里输入root,然后要求输入密码,默认密码为openEuler#12。(2)用户名和密码输入正确后进入引导编辑界面:(3)按向下光标,…

Spring 和 Spring Boot 之间的比较

概述 本位我们将讨论标准 Spring 框架和 Spring Boot 之间的区别。 将重点讨论 Spring 的模块,如 MVC 和 Security,在 Spring 中使用时与 在Spring Boot 中使用时有何不同。 什么是 Spring 简而言之,Spring 框架为开发 Java 应用程序提供了全面的基础设施支持。它包含了一些…

出现Invalid bound statement (not found)错误

出现Invalid bound statement (not found)错误时,通常是由于MyBatis无法正确匹配Mapper接口与XML文件的映射关系。以下是具体排查步骤和解决方案:

当Kafka化身抽水马桶:论组件并发提升与系统可用性的量子纠缠关系

《当Kafka化身抽水马桶:论组件并发提升与系统可用性的量子纠缠关系》引言:一场OOM引发的血案 某个月黑风高的夜晚,监控系统突然发出刺耳的警报——我们的数据发现流水线集体扑街。事后复盘发现:Kafka集群、Gateway、Discovery服务默契地同时表演了OOM自杀式艺术行为。这场事…