本文介绍利用流体算法,模拟水墨特效。
本文主要参考了 恬纳微晰 的github 实现和 GPU Gems 第38章
https://github.com/TNWX-Z/EnhanceSmokeSimulationPro
https://developer.download.nvidia.com/books/HTML/gpugems/gpugems_ch38.html
恬纳微晰的实现效果。
GPU Gems
之前在网上搜索流体算法的时候看到了 恬纳微晰 的实现, 但是是通过shader在image effect 里实现的。 恰好中文的流体算法的看得懂的相关资料不多。 于是我就做了一个 compute shader 的实现并学习了相关算法。
这个流体算法, 大体上可以分为 平流模拟(advection) 和 旋涡模拟(Vorticity)。使用的是网格的方式模拟每个网点的流速,压强和密度。
Compute Shader 入门可以看我之前的文章
https://zhuanlan.zhihu.com/p/33675797
计算的数据用了
RWTexture2D<float4> Result; //渲染结果
RWTexture2D<float4> Flowmap; //流速图
RWStructuredBuffer<float2> VelocityW; //流速 写入
StructuredBuffer<float2> VelocityR; //流速 读取
RWStructuredBuffer<float> Curled; // 卷曲度
RWStructuredBuffer<float> Divergenced; //发散度
RWStructuredBuffer<float> DensityW; //密度 写入
StructuredBuffer<float> DensityR; //密度 读取
RWStructuredBuffer<float4> ColorW; //颜色 写入
StructuredBuffer<float4> ColorR; //颜色 读取
主要计算步骤为
Advection //平流计算
Curl //卷曲计算
Vorticity //旋涡
Divergence //发散
Pressure //压力
GradientSubtract //梯度减法
RenderTex //渲染
Splat //笔刷绘制
Advection 平流的计算为, 根据当前的流速, 反推时间 deltaTime 后流到这个位置的 网点的信息, 作为自己 deltaTime 后的信息.
比如当前流速是 v = 1m, 那么 t = 0.1s 后会流到这个位置的坐标 c 就是当前坐标 减去 v * t
Curl 卷曲计算 为根据当前格子周围格子的速度, 计算出当前网点的旋转速度. 这个量是垂直于计算面的.
蓝色为周围网格的流速, 黄色箭头为卷曲度, 即当前网点的 旋转速度。
Vorticity 旋涡计算的是当前网点附近的 卷曲值 对网点流速的影响。 周围网点流速的不均衡导致当前网点的流速获得一个改变方向的力。
Divergence 发散 和 Pressure 压力的迭代, 模拟了压力在网点间的传播。
大部分2D 水面模拟会用 压强和密度来计算水波的传播, 比如 奥日与黑森林
蓝色为周围网点的流速, 绿色箭头为压力。 当周围向中心流动时, 压强上升, 反之则压强下降。
黑色折现为当前压力。 绿色为与相邻网点的平均值。
每个网点会尽量保证自己的压强跟周围的网点一致。压强的传播就代表流速的传播。根据液体粘稠度的不同,传播速度也会不同。
维基百科:粘度https://zh.wikipedia.org/wiki/%E9%BB%8F%E5%BA%A6
将这两个函数迭代一定次数后, 进行 Gradient Subtract 梯度衰减
RenderTex 为将当前颜色输出到 Result。 我这里做水墨黑白效果所以就直接反了个色。
Splat 为用画笔写入颜色。 传入鼠标位置 和绘制半径, 根据当前网格与鼠标位置计算绘制的颜色和流速。传入一张笔触贴图作为控制。
最终结果
左边为流速图, 右边为绘制面板。
按B 清除画布
操纵杆依次为 笔刷大小, 浓度, 时间步长, 迭代次数, 液体阻尼, 密度阻尼, 颜色淡出, 笔刷移动速度的影响。
项目源文件
https://github.com/sacshadow/WaterEffectResearch
其他参考
暂无关于此日志的评论。