上一篇文章中,介绍了一个简单数值方法来模拟圆形扩散波的效果,但是这种方法对于自然中像海浪一样的波

就无能为力了。所以,这篇文章介绍用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波 实现简单的水面模拟相关推荐

  1. 水面的简单渲染 – Gerstner波

    发布于 2014年2月7日 作者: John Hany2,986次阅读 渲染三维场景时经常会遇到需要渲染各种水体的情况,比如湖泊.河流.海洋等,不仅需要水体表面要有接近真实的随时间而变化的波动,还要有 ...

  2. 水面模拟--波动方程

    前言 最近想做水面模拟,比较原始的方法是通过解wave function进行模拟,先看一下效果. 一.波动方程 使用的波动方程为: 其中z表示点(x,y)处t时刻的高度,v表示传播的速度. 因为水波随 ...

  3. python分子化学模拟_python简单实现gillespie模拟

    由于专业需求,需要做主方程的随机模拟.在网上并没有找到适合的Python实现,遂自己写了一个,分享一下源码.至于gillespie算法本身就不介绍了,有需要的读者自然会懂,没需要的读者不建议去懂. 源 ...

  4. python模拟行星运动_使用 Python 来简单的动态模拟一下太阳系的运转

    前言 本文的文字及图片来源于网络,仅供学习.交流使用,不具有任何商业用途,版权归原作者所有,如有问题请及时联系我们以作处理. 以下文章来源于Python技术 ,作者派森酱 提到太阳系,大家可能会想到哥 ...

  5. Unreal Engine 4 基于网格的水面模拟实现

    http://blog.csdn.net/shangguanwaner/article/details/51862644 Unreal Engine 4 水面模拟实现 一般游戏里水面的模拟都是实用动态 ...

  6. 使用线程安全型双向链表实现简单 LRU Cache 模拟

    使用线程安全型双向链表实现简单 LRU Cache 模拟 目录

  7. java实现红包要多少钱_JAVA实现简单抢红包算法(模拟真实抢红包)

    闲来无事,最近项目需求要写出用户登录首页来发现金红包,没有限额.我就自己稍微计算了一下如果有限额该怎么写.觉得这样与微信红包差不多.等项目需求完成以后.正好来博客贴一下我自己写的拆红包算法.个人觉得这 ...

  8. 简单的JavaScript模拟时钟

    文章目录 一.示例 二.源码 一.示例 JavaScript时钟 二.源码 html部分 <!DOCTYPE html> <html lang="en">& ...

  9. 简单文件数据库-模拟图书馆管理系统

    题目要求 编写一个程序模拟图书管理系统.用户分为管理员和读者两类,分别显示不同文本格式菜单,通过菜单项对应数字进行选择.读者菜单包括借书.还书.查询等功能.管理员菜单包括图书和读者信息录入.修改和删除 ...

最新文章

  1. Response.Redirect 打开新窗体的两种方法
  2. pythonpandas用sql查询数据表_python pandas read_sql_query使用记录
  3. 单行文本溢出显示省略号,单行文本溢出显示省略号
  4. Go的50坑:新Golang开发者要注意的陷阱、技巧和常见错误[2]
  5. socket.io 之 engine.io
  6. php5.6.14,PHP 5.5.30/5.6.14 发布下载,安全修复版本
  7. 作业9-文件方式实现完整的英文词频统计实例
  8. ini_set(display_errors,On);和error_reporting(E_ALL);
  9. c语言逆序数输三个数,C语言求助!一个三位数的逆序数,总是编不对
  10. 从网络获取数据显示到TableViewCell容易犯的错
  11. 安全辅助 冰刃 IceSword 1.2 中文版 修正号061022
  12. Matlab深度学习上手初探
  13. ArcGIS实验教程——实验四十三:ArcGIS栅格重分类(Reclass)案例详解
  14. Python在线编辑器推荐
  15. 使用AudioRecord实现声音采集
  16. 华硕飞行堡垒加装固态硬盘和内存条
  17. dhcp服务器日志文件,dhcp服务器日志查看
  18. Kaggle实战之 房价预测案例
  19. SDN交换机在云计算网络中的应用场景
  20. WPF自定义控件与样式(13)-自定义窗体Window 自适应内容大小消息框MessageBox

热门文章

  1. 【项目调研+论文阅读】Lattice LSTM神经网络医学文本命名实体识别 | day7
  2. Python中代码书写规范与基本使用
  3. 【pytest】Hook 方法之 pytest_addoption :注册命令行参数
  4. python add_argument()用法解析
  5. java 数组覆盖_JavaSE——数组集合
  6. 命令行删除mysql57_Ubuntu16.04彻底删除MySQL5.7 方法
  7. 手机端html5 面试,今日头条 张祖俭 - H5动画在移动平台上的性能优化实践
  8. 计算机竞赛湖北有哪些,我校29名学生在全国大学生数学竞赛湖北赛区获奖
  9. linux 更换窗口管理器,linux Gnome .KDE.xfce4窗口管理器切换
  10. 使用Arrays sort 方法進行排序