作者:i_dovelemon

日期:2020-04-25

主题:Perlin Noise, Curl Noise, Finite Difference Method

引言

最近在研究流体效果相关的模拟。经过一番调查,发现很多的算法都基于一定的物理原理进行模拟,计算量相对来说都比较高昂。最终寻找到一个基于噪音实现的,可在视觉上模拟流体效果的方法:Curl Noise。题图就是通过 Curl Noise 模拟的流体向量场控制的百万粒子的效果。

背景知识

在讲解什么是 Curl Noise 之前,我们需要了解一些相关背景知识。

向量场(Vector Field)

一个2D 或者 3D 的向量场,表示的是赋予空间中任意点一个 2D 或者 3D 向量的函数。公式表示如下所示:

$\vec{F}\left(x,y \right)=P\left(x,y\right)\vec{i}+Q\left(x,y\right)\vec{j}$

$\vec{F}\left(x,y,z \right)=P\left(x,y,z\right)\vec{i}+Q\left(x,y,z\right)\vec{j}+R\left(x,y,z\right)\vec{k}$

其中,$P$,$Q$,$R$ 各表示一个标量函数,即它们的返回值是一个标量;$\vec{i}$,$\vec{j}$,$\vec{k}$ 各表示一个基向量。(参考文献[1])

上面数学的解释大家可能不熟悉,但是很多人或多或少的都看过向量场的图片形式,如下所示:

散度和旋度(Curl and Divergence)

首先,我们来定义一个 $\nabla$ 操作,如下所示:

$\nabla=\frac{\partial }{\partial x}\vec{i}+\frac{\partial }{\partial y}\vec{j}+\frac{\partial }{\partial z}\vec{k}$

其中$\partial$表示的是偏导数符号,不熟悉的读者可以去复习下微积分或者参考文献[2]。有了这个操作符之后,我们定义旋度为:

$curl\vec{F}=\nabla\times\vec{F}=(\frac{\partial R}{\partial y}-\frac{\partial Q}{\partial z},\frac{\partial P}{\partial z}-\frac{\partial R}{\partial x},\frac{\partial Q}{\partial x}-\frac{\partial P}{\partial y})$

其中$\times$为叉积操作符(参考文献[3])。

有了旋度之后,我们再来定义散度,同样的,公式如下所示:

$div\vec{F}=\nabla\cdot \vec{F}=\frac{\partial P}{\partial x}+\frac{\partial Q}{\partial y} + \frac{\partial R}{\partial z}$

特别的,散度和旋度之间有如下的一个关系:

$div(curl\vec{F})=0$

以上内容,参考文献[4]。

根据上面的公式,我们可以知道,对于一个向量场的旋度场,它的散度为 0,即它是一个无源场(Divergence-Free)。而一个散度为 0 的向量场,表示这个场是不可压缩的流体,这对日常所见的流体来说是一个很重要的视觉性质,所以据此我们可以使用一个场的旋度场来模拟流体效果。

Curl Noise

所谓 Curl Noise,即是对一个随机向量场,进行 Curl 操作之后得到的新场。因为满足散度为 0 的特性,所以这个场看上去就具有流体的视觉特性。如果用这个场作为速度去控制粒子,即可得到开头视频中流动的效果。

2D Curl Noise

前面我们说过,需要一个随机的向量场。这里我们使用 Perlin Noise 来进行模拟,关于 Perlin Noise 网上一堆资料,这里就不再赘述。

我们假设 Perlin Noise 的函数为:

$N(x,y)$

它的返回值是一个标量值。然后据此建立一个新的向量场:

$\vec{F}(x,y) = (N(x,y), N(x,y))$

然后对这个新的向量场进行 Curl 操作,即可得到旋度场。

前面只说过 3D 情况下的 Curl 操作是怎么样的,这里给出 2D 版本的 Curl 操作:

$curl\vec{F}(x, y) = (\frac{\partial N(x,y)}{\partial y}, -\frac{\partial N(x,y)}{\partial x})$

这里就只剩下了最后一个问题,那就是形如 $\frac{\partial N(x,y)}{\partial x}$ 这样的偏导数,该怎么计算。我们这里使用一个名为有限差分的方法(Finite Difference Method)来近似求解。

Finite Difference Method

根据文献[2]中对于偏导数的描述,我们知道 $\frac{\partial N(x,y)}{\partial x}$ 只是一种表达方式,它的精确表示方法为:

$\frac{\partial N(x,y)}{\partial x}= N_x(x,y) = \lim_{h\to0}{\frac{N(x + h,y)-N(x,y)}{h}}$

而后面极限的表达方式则给了我们近似计算这个偏导数的方法,只要给定一个较小的 $h$ 值,就能够近似的得到偏导数的结果。而这种计算方法即为:有限差分方法(Finite Difference Method)。

除了上面的极限表示方法之外,还有另外一种极限表示方法,如下所示:

$\frac{\partial N(x,y)}{\partial x}= N_x(x,y) = \lim_{h\to0}{\frac{N(x,y)-N(x-h,y)}{h}}$

这两种差分方法分别称之为前向差分(Forward Difference)和逆向差分(Backward Difference)方法。我这里主要使用逆向差分方法。

有了计算偏导数的方法之后,我们就可以实际带到 2D Curl 操作的公式进行计算,如下是计算 2D Curl Noise 的伪代码:

vec2 computeCurl(float x, float y)
{
float h = 0.0001f;
float n, n1, n2, a, b; n = N(x, y);
n1 = N(x, y - h);
n2 = N(x - h, y);
a = (n - n1) / h;
b = (n - n2) / h; return vec2(a, -b);
}

知道怎么计算 2D Curl Noise 之后,我们用计算出来的 Curl Noise 作为速度场去控制粒子进行运动,如下是 2D Curl Noise 控制粒子运动的效果:

3D Curl Noise

有了前面 2D Curl Noise 的实现,如法炮制的实现 3D Curl Noise 的推导。

3D Perlin Noise 函数定义为:

$N(x,y,z)$

以此构造出来的 3D 向量场为:

$\vec{F}(x,y,z)=(N(x,y,z),N(x,y,z)N(x,y,z))$

对这个场进行 Curl 操作,得到:

$curl\vec{F}=(\frac{\partial N(x,y,z)}{\partial y}-\frac{\partial N(x,y,z)}{\partial z},\frac{\partial N(x,y,z)}{\partial z}-\frac{\partial N(x,y,z)}{\partial x},\frac{\partial N(x,y,z)}{\partial x}-\frac{\partial N(x,y,z)}{\partial y})$

据此,给出计算 3D Curl Noise 的伪代码:

vec3 computeCurl(float x, float y)
{
vec3 curl;
float h = 0.0001f;
float n, n1, a, b; n = N(x, y, z); n1 = N(x, y - h, z);
a = (n - n1) / h; n1 = N(x, y, z - h);
b = (n - n1) / h;
curl.x = a - b; n1 = N(x, y, z - h);
a = (n - n1) / h; n1 = N(x - h, y, z);
b = (n - n1) / h;
curl.y = a - b; n1 = N(x - h, y, z);
a = (n - n1) / h; n1 = N(x, y - h, z);
b = (n - n1) / h;
curl.z = a - b; return curl;
}

以下是根据得到的 3D Curl Noise,并一次控制粒子进行运动的效果:

结论

Curl Noise 在游戏中有大量的运用,Unity 的粒子系统的 Noise Module 就内置了 Curl Noise 的实现。作为游戏开发的人员,很有必要了解下这个技术的原理,便于在实际开发中灵活运用。本文的主要原理来自于参考文献[5],感兴趣的可以深入去了解。

源代码已上传 Github:https://github.com/idovelemon/UnityProj/tree/master/CurlNoise 。

参考文献

[1] Section 5-1 : Vector Field

[2] Section 2-2:Partial Derivatives

[3] Section 5-4:Cross Product

[4] Section 6-1:Curl And Divergence

[5] Curl-Noise for Procedural Fluid Flow

GraphicsLab Project 之 Curl Noise的更多相关文章

  1. GraphicsLab Project之辉光(Glare,Glow)效果 【转】

    作者:i_dovelemon 日期:2016 / 07 / 02 来源:CSDN 主题:Render to Texture, Post process, Glare, Glow, Multi-pass ...

  2. GraphicsLab Project学习项目

    作者:i_dovelemon 日期:2016 / 05 / 30 主题:3D,Graphics 引言 进公司以来,主要在学习的就是如何保证代码的质量,以前热爱的图形学也放置了.但是,作为游戏程序员,特 ...

  3. GraphicsLab Project之Diffuse Irradiance Environment Map

    作者:i_dovelemon 日期:2020-01-04 主题:Rendering Equation,Irradiance Environment Map,Spherical Harmonic 引言 ...

  4. 数字图像处理实验(11):PROJECT 05-02,Noise Reduction Using a Median Filter 标签: 图像处理MATLAB 2017-05-26 23:

    实验要求: Objective: To understand the non-linearity of median filtering and its noise suppressing abili ...

  5. GraphicsLab Project 之 Screen Space Planar Reflection

    作者:i_dovelemon 日期:2020-06-23 主题:Screen Space Planar Reflection, Compute Shader 引言 前段时间,同事发来一篇讲述特化版本的 ...

  6. GraphicsLab Project之再谈Shadow Map

    作者:i_dovelemon 日期:2019-06-07 主题:Shadow Map(SM), Percentage Closer Filtering(PCF), Variance Shadow Ma ...

  7. 用体渲染的方法在Unity中渲染云(18/4/4更新)

    github: https://github.com/yangrc1234/VolumeCloud 更新的内容在底部 最近在知乎上看到一篇文章讲云层的渲染(https://zhuanlan.zhihu ...

  8. libcurl教程

    名称 libcurl 的编程教程 目标 本文档介绍使用libcurl编程的一般原则和一些基本方法.本文主要是介绍 c 语言的调用接口,同时也可能很好的适用于其他类 c 语言的接口. 跨平台的可移植代码 ...

  9. cocos2dx libcurl

    转自:http://www.himigame.com/curl-libcurl/878.html 本篇介绍使用libcurl编程的一般原则和一些基本方法.本文主要是介绍 c 语言的调用接口,同时也可能 ...

随机推荐

  1. Springcloud 整合Hystrix 断路器,支持Feign客户端调用

    1,在这篇博文中,已经大致说过了Springcloud服务保护框架 Hystrix在服务隔离,服务降级,以及服务熔断中的使用 https://www.cnblogs.com/pickKnow/p/11 ...

  2. Feign客户端的重构,新建springcloud架构

    1,在上篇博文中,已经实现了feign 客户端来远程调用接口的功能,因为feign 客户端在springcloud 开发过程中是比较常用的方式 https://www.cnblogs.com/pick ...

  3. Python——NumPy库入门

    1.数据的纬度 维度:一组数据的组织形式 1.1 一维数据 一维数据由对等关系的有序或无序数据构成,采用线性方式组织 ,对应列表.数组和集合等概念 列表:数据类型可以不同 ,如 3.1413, 'pi ...

  4. python——os平台编程

    一.os平台编程需求 1.目录文件的操作 对系统目录,文件的操作方法 2.程序的定时执行 3.可执行程序的转换 python程序向可执行程序的转换 二.目录文件操作 root:当前目录: dirs:当 ...

  5. Flutter 不可错过的学习资源

    老孟导读:今天给大家分享一下我在学习Flutter的过程中整理的资料,这些文章或者开源项目都是精挑细选的,希望可以帮助到到家.另外相关资料会在Github一直更新,欢迎大家fork,如果喜欢的话给个小 ...

  6. .NET(C#)实现桌面背景切换(控制台应用程序,windows服务版的未实现成功)

    AdvancedBackgroundJimmy.Program.cs using AdvancedBackground; using Microsoft.Win32; using System; us ...

  7. 1012 The Best Rank (25 分)

    To evaluate the performance of our first year CS majored students, we consider their grades of three ...

  8. IIS 组成

    HTTP.sys      http.sys 侦听来自网络的 HTTP 请求,将它们传递到 IIS 并返回响应. 它是一种可以从命令行停止和启动的服务.      "NET STOP HTT ...

  9. Python 参数使用总结

    Python 中参数的传递非常灵活,不太容易记住理解,特整理如下备忘: 普通参数 即按照函数所需的参数,对应位置传递对应的值,可以对应 Java 中的普通参数 def max(a, b): if a ...

  10. Centos6升级内核方法

    docker需要内核在3.0以上,如果centos6上需要安装docker的话需要先将内核进行升级 工具/原料   Centos6.5_x64 方法/步骤     操作系统为centos6.5,内核为 ...