[DirectX11]Gerstner波 实现简单的水面模拟
上一篇文章中,介绍了一个简单数值方法来模拟圆形扩散波的效果,但是这种方法对于自然中像海浪一样的波
就无能为力了。所以,这篇文章介绍用Gerstner波来模拟水面波纹效果。
一、Gerstner波介绍
Gerstner波是一种动态模拟海面幅度的方法,已有200多年的历史,后被用于计算机图形学。
首先,我们介绍一下几个常用的描述波函数的物理量,便于理解马上要介绍的Gerstner波方程。
A:amplitude 振幅,波相较于平衡位置的最大偏移量。
ω:角速度。
λ:波长
k:波数 (2π/λ)
K:波矢量(wavevector)其大小为波数,方向为波传播的方向,在3D波方程中,是一个2D向量
现在给出Gerstner波的方程,以参数方程的形式给出:
x=x0-(K/k)*Asin(K*x0-ωt)
y=Acos(K*x0-ωt)
肯定有朋友会问,明明是3D的波方程,为何只有两个变量xy。大家请注意,第一个式子中,加粗的部分,上面
已经介绍过,K是一个2D向量,方向表示在x-z平面波传播的方向,所以第一个加粗的部分(包括x,x0)都不是一个标量,而是一个二元量,表示的是在x-z平面上的具体位置,x=(x,z),x0=(x0,z0)。这样第一个式子得出的结果也自然是一个2D向量(K,x0点乘结果为一个标量)。
(x0,z0)又代表什么呢?这个坐标表示的是某一个顶点原本在x-z平面的位置。这里需要解释一下,总所周知,我
们模拟水面,经常是先在x-z平面上构建一个平面网格,然后修改每个顶点的y值,如下图:
Gerstner波会根据固定的(x0,z0)位置,计算出新的在x-z平面的位置(也就是第一个式子的结果,x)。Gerstner波
的特征是 波峰较窄,波谷较宽,这符合自然中水波的特征。而从第二个公式我们可以看出,y是一个余弦函数,波峰波谷是一样的,所以波峰窄,波谷宽的特征便是通过对(x0,z0)进行一定量偏移造成的,如下图:
多个Gerstner波叠加
x=x0-∑(K/k)*Asin(K*x0-ωt)
y=∑Acos(K*x0-ωt)
多个波叠加也是非常好理解的,第一个公式叠加的是偏移量,所以x0不需要进行叠加。
注意
在选择Gerstner波的时候,是k,A是有一定限制的:
kA<1时,kA越大,波峰越窄。
kA>1时,会在波峰处产生环。如下图:
图片来自:《Simulating Ocean Water》 Jerry Tessendorf
二、Gerstner波的DirectX11实现
和上篇文章一样,我们同样适用DirectX11提供的Compute Shader进行并行计算。我们首先适用一个Buffer存放
网格数据,然后用一个RWStructuredBuffer存放计算后的数据。具体的HLSL代码片段如下:
StructuredBuffer<VertexS> gOriVertices; RWStructuredBuffer<VertexS> gOutput; [numthreads(N, N, 1)] void csPos(int3 dispatchID:SV_DispatchThreadID) { int x = dispatchID.x; int z = dispatchID.y; float x0 = gOriVertices[z*gWidth + x].pos.x; float z0 = gOriVertices[z*gWidth + x].pos.z; float2 offset = float2(0.0f, 0.0f); float height = 0; float k = 0; float p; [unroll] for (uint i = 0; i < gWaveNum; ++i) { float2 waveVector = float2(gWaveVectors[i * 2 + 0], gWaveVectors[i * 2 + 1]); p = dot(waveVector, float2(x0, z0)) - gOmegas[i] * gTime; offset += normalize(waveVector)*gAmplitudes[i] * sin(p); height += gAmplitudes[i] * cos(p); } gOutput[z*gWidth + x].pos = float3(x0-offset.x, height, z0-offset.y ); }
计算法线的HLSL片段:
[numthreads(N, N, 1)] void csVector(int3 dispatchID:SV_DispatchThreadID) { int x = dispatchID.x; int z = dispatchID.y; if (x == 0 || x == (gWidth - 1) || z == 0 || z == (gDepth - 1)) { return; } float3 left = gOutput[z*gWidth + x - 1].pos; float3 right = gOutput[z*gWidth + x + 1].pos; float3 top = gOutput[(z - 1)*gWidth + x].pos; float3 bottom = gOutput[(z + 1)*gWidth + x].pos; float3 tangent = normalize(right - left); float3 binormal = normalize(bottom - top); gOutput[z*gWidth + x].normal = normalize(cross(tangent, binormal)); gOutput[z*gWidth + x].tangent = tangent; }
DirectX11部分的详细代码就不放出了,没有什么难度,对DirectX11不是很熟悉的朋友,可以参考此书。Dx11的
经典入门书籍,有DirectX龙书之称。
三、小结
多个适当的Gerstner波叠加,能够制造出较为真实的水面,但是如果需要电影级的水面波纹效果,Gerstner波可
能力不从心了。所以这个时候,就是FFT(快速傅里叶变化)发挥作用的,可能下篇文章会介绍利用FFT实现水面波纹的模拟(本人数学基础不是很好。需要补补课。。)
最后附几张截图:
简陋见解,不足之处,欢迎交流、拍砖。
参考资料:
《Simulating Ocean Water》 Jerry Tessendorf
JohnHany博客文:水面的简单渲染 – Gerstner波
================================The End==============================
[DirectX11]Gerstner波 实现简单的水面模拟相关推荐
- 水面的简单渲染 – Gerstner波
发布于 2014年2月7日 作者: John Hany2,986次阅读 渲染三维场景时经常会遇到需要渲染各种水体的情况,比如湖泊.河流.海洋等,不仅需要水体表面要有接近真实的随时间而变化的波动,还要有 ...
- 水面模拟--波动方程
前言 最近想做水面模拟,比较原始的方法是通过解wave function进行模拟,先看一下效果. 一.波动方程 使用的波动方程为: 其中z表示点(x,y)处t时刻的高度,v表示传播的速度. 因为水波随 ...
- python分子化学模拟_python简单实现gillespie模拟
由于专业需求,需要做主方程的随机模拟.在网上并没有找到适合的Python实现,遂自己写了一个,分享一下源码.至于gillespie算法本身就不介绍了,有需要的读者自然会懂,没需要的读者不建议去懂. 源 ...
- python模拟行星运动_使用 Python 来简单的动态模拟一下太阳系的运转
前言 本文的文字及图片来源于网络,仅供学习.交流使用,不具有任何商业用途,版权归原作者所有,如有问题请及时联系我们以作处理. 以下文章来源于Python技术 ,作者派森酱 提到太阳系,大家可能会想到哥 ...
- Unreal Engine 4 基于网格的水面模拟实现
http://blog.csdn.net/shangguanwaner/article/details/51862644 Unreal Engine 4 水面模拟实现 一般游戏里水面的模拟都是实用动态 ...
- 使用线程安全型双向链表实现简单 LRU Cache 模拟
使用线程安全型双向链表实现简单 LRU Cache 模拟 目录
- java实现红包要多少钱_JAVA实现简单抢红包算法(模拟真实抢红包)
闲来无事,最近项目需求要写出用户登录首页来发现金红包,没有限额.我就自己稍微计算了一下如果有限额该怎么写.觉得这样与微信红包差不多.等项目需求完成以后.正好来博客贴一下我自己写的拆红包算法.个人觉得这 ...
- 简单的JavaScript模拟时钟
文章目录 一.示例 二.源码 一.示例 JavaScript时钟 二.源码 html部分 <!DOCTYPE html> <html lang="en">& ...
- 简单文件数据库-模拟图书馆管理系统
题目要求 编写一个程序模拟图书管理系统.用户分为管理员和读者两类,分别显示不同文本格式菜单,通过菜单项对应数字进行选择.读者菜单包括借书.还书.查询等功能.管理员菜单包括图书和读者信息录入.修改和删除 ...
最新文章
- Response.Redirect 打开新窗体的两种方法
- pythonpandas用sql查询数据表_python pandas read_sql_query使用记录
- 单行文本溢出显示省略号,单行文本溢出显示省略号
- Go的50坑:新Golang开发者要注意的陷阱、技巧和常见错误[2]
- socket.io 之 engine.io
- php5.6.14,PHP 5.5.30/5.6.14 发布下载,安全修复版本
- 作业9-文件方式实现完整的英文词频统计实例
- ini_set(display_errors,On);和error_reporting(E_ALL);
- c语言逆序数输三个数,C语言求助!一个三位数的逆序数,总是编不对
- 从网络获取数据显示到TableViewCell容易犯的错
- 安全辅助 冰刃 IceSword 1.2 中文版 修正号061022
- Matlab深度学习上手初探
- ArcGIS实验教程——实验四十三:ArcGIS栅格重分类(Reclass)案例详解
- Python在线编辑器推荐
- 使用AudioRecord实现声音采集
- 华硕飞行堡垒加装固态硬盘和内存条
- dhcp服务器日志文件,dhcp服务器日志查看
- Kaggle实战之 房价预测案例
- SDN交换机在云计算网络中的应用场景
- WPF自定义控件与样式(13)-自定义窗体Window 自适应内容大小消息框MessageBox
热门文章
- 【项目调研+论文阅读】Lattice LSTM神经网络医学文本命名实体识别 | day7
- Python中代码书写规范与基本使用
- 【pytest】Hook 方法之 pytest_addoption :注册命令行参数
- python add_argument()用法解析
- java 数组覆盖_JavaSE——数组集合
- 命令行删除mysql57_Ubuntu16.04彻底删除MySQL5.7 方法
- 手机端html5 面试,今日头条 张祖俭 - H5动画在移动平台上的性能优化实践
- 计算机竞赛湖北有哪些,我校29名学生在全国大学生数学竞赛湖北赛区获奖
- linux 更换窗口管理器,linux Gnome .KDE.xfce4窗口管理器切换
- 使用Arrays sort 方法進行排序