作者: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. 数据库(sqlserver 2005)优化排查之路

    查找问题过程是痛苦的,解决完问题是快乐! 兄弟帮助一个公司开发了一个旅游网站(asp.net+sqlsever2005),一直还算稳定,但是最近网站却慢的可以,让人头疼.登录服务器,进入任务管理器,发 ...

  2. RPC框架实现(一) Protobuf的rpc实现

    概述 RPC框架是云端服务基础框架之一,负责云端服务模块之间的项目调用,类似于本地的函数调用一样方便.常见的RPC框架配带的功能有: 编解码协议.比如protobuf.thrift等等. 服务发现.指 ...

  3. JVM中内存分配策略及堆和栈的比较

    最近愈发对JVM底层的运行 原理产生了兴趣,遂查阅相关资料以备忘. 内存分配策略 根据编译原理的观点,程序运行时的内存分配,有三种策略,分别为静态的.堆式的.栈式的. 静态存储分配指的是在编译时就能确 ...

  4. 7.Maven命令

    在eclipse中运行maven 一.首先要对pom.xml文件右键→Run As→Maven build 二.输入Maven命令 三.常见的Maven命令有: [1]clean 清理 [2]comp ...

  5. centos7中提升用户权限

    提升用户权限我看网上资源有两种方法,一种是修改/etc/sudoers/文件将新增的用户权限提升为和root一样的权限,这种方法不知道怎么回事我没用应用成功,这里我介绍第二种方法,第二种方法是更改/e ...

  6. Spring中的设计模式:工厂方法模式

    导读 工厂方法模式是所有设计模式中比较常用的一种模式,但是真正能搞懂用好的少之又少,Spring底层大量的使用该设计模式来进行封装,以致开发者阅读源代码的时候晕头转向. 文章首发于微信公众号[码猿技术 ...

  7. CodeForces 506B/505D Mr. Kitayuta's Technology

    Portal:http://codeforces.com/problemset/problem/506/B http://codeforces.com/problemset/problem/505/D ...

  8. 【docker linux】linux系统镜像转化为docker镜像

    概述 使用docker安装linux的同学都知道,你在docker提供的仓库安装linux系统,你就会体验到最精简的.最纯净的linux系统,当然,他会精简到你连ifconfig命令都需要自己配置,恰 ...

  9. 使用Azure Rest API获得Access Token介绍

    背景 本文主要介绍如何获取如何获取Azure Rest API的访问token,所采用的是v2.0版本的Microsoft标识平台,关于1.0和2.0的区别可以参考 https://docs.azur ...

  10. API网关--Kong的实践

    1. 什么是Kong 目前互联网后台架构一般是采用微服务,或者类似微服务的形式,应用的请求通常需要访问多个后台系统.如果让每一个后台系统都实现鉴权.限流.负载均衡.审计等基础功能是不合适的,通用的做法 ...