Perlin Noise 及其应用
Perlin Noise 可以用来表现自然界中无法用简单形状来表达的物体的形态,比如火焰、烟雾、表面纹路等。要生成 Perlin Noise 可以使用工具离线生成,也可以使用代码运行时生成。最简单常用的离线生成工具就是 Photoshop 了,新建画布,然后直接选择云彩滤镜即可。而这里要介绍的是使用代码生成 Perlin Noise,使用代码运行时生成有几点好处。首先就是 Perlin Noise 纹理不会占用空间,如果大量使用到了 Perlin Noise Texture,这就不失为一个减少包大小的好方法,比如说地形的高度图是通过 Perlin Noise 生成的,同时又有很多张地图。还有个好处就是,运行时生成的是可以根据需要产生不同的变化的,而工具离线生成的就做不到。
以下提到的生成方法以及代码都是来自于http://devmag.org.za/2009/04/25/perlin-noise/,在此基础上加上我加入了自己的理解。
下面就来介绍下使用代码生成 Perlin Noise 的几个步骤:
生成一张随机杂点的纹理

随机杂点纹理是生成 Perlin Noise 的基础,以后所有要用到的数据都是从这张纹理衍生出来的。
private float[,] GenerateWhiteNoise(int width, int height)
{
// 这里的随机数种子很重要
// 如果想要每次生成的 Perlin Noise 都一样,只要设置一个相同的值即可
// 这里使用随机种子,所有每次都会生成不同的 Perlin Noise
Random.seed = Random.Range(int.MinValue, int.MaxValue);
float[,] noise = new float[width, height];
for (int i = 0; i < width; i++)
{
for (int j = 0; j < height; j++)
{
noise[i, j] = Random.value;
}
}
return noise;
}
使用不同的采样率对随机杂点纹理进行采样

上图中每一小块是使用不同的采样率对随机杂点纹理进行采样的结果。采样率越小,获得的细节越少,采样率越大,获得的细节越多。左上角的采样率是最小的,所以细节越少。右下角的采样率最大,所以细节越多。我们将使用这几张图来合成最终的 Perlin Noise。在合成过程中,每一张图的合成比重是不一样的,细节越少的占用更多的合成比重,细节越多的占用更少的合成比重。以上图来举例,也就是说,左上角的那张采样纹理决定了 Perlin Noise 最终的大致形状,以后的所有纹理逐一叠加来提供细节,但是比重越来越小。
// 生成指定采样级别的 Noise
private float[,] GenerateSmoothNoise(float[,] whiteNoise, int octave, int width, int height)
{
float[,] smoothNoise = new float[width, height];
// 采样步长
int samplePeriod = (int)Mathf.Pow(2, octave);
// 采样频率
float sampleFrequency = 1.0f / samplePeriod;
for (int i = 0; i < width; i++)
{
// 最左点位置
int sampler_l = (i / samplePeriod) * samplePeriod;
// 最右点位置
int sampler_r = (sampler_l + samplePeriod) % width;
// 根据实际点与最左最右的距离,计算水平混合系数
float horizontal_blend = (i - sampler_l) * sampleFrequency;
for (int j = 0; j < height; j++)
{
// 最上点位置
int sampler_t = (j / samplePeriod) * samplePeriod;
// 最下点位置
int sampler_d = (sampler_t + samplePeriod) % height;
// 根据实际点与最上最下的距离,计算垂直混合系数
float vertical_blend = (j - sampler_t) * sampleFrequency;
// 左上和右上根据水平混合系数进行插值
float top = Interpolate(whiteNoise[sampler_l, sampler_t], whiteNoise[sampler_r, sampler_t], horizontal_blend);
// 左下和右下根据水平混合系数进行插值
float bottom = Interpolate(whiteNoise[sampler_l, sampler_d], whiteNoise[sampler_r, sampler_d], horizontal_blend);
// 最总数值为 top down 根据垂直混合系数进行插值
smoothNoise[i, j] = Interpolate(top, bottom, vertical_blend);
}
}
return smoothNoise;
}
// 在两个数值间进行插值
private float Interpolate(float x0, float x1, float alpha)
{
return x0 * (1 - alpha) + alpha * x1;
}
合成最终的 Perlin Noise

使用面生成的所有采样纹理来合成最终的 Perlin Noise。
public float[,] GeneratePerlinNoise(float[,] whiteNoise, int octaveCount, int width, int height)
{
// 不同采样级别的 Noise
List<float[,]> smoothNoise = new List<float[,]>();
// 不同采样级别的 Noise 的叠加系数
float persistance = 0.8f;
// 生成不同采样级别的 Noise
for (int i = 0; i < octaveCount; i++)
{
smoothNoise.Add(GenerateSmoothNoise(whiteNoise, i, width, height));
}
// 最终生成的 Perlin Noise
float[,] perlinNoise = new float[width, height];
// 不同采样级别的 Noise 的叠加比重
float amplitude = 1.0f;
// 所有采样级别的 Noise 的叠加总比重
float totalAmplitude = 0.0f;
// 开始混合所有采样级别的 Noise
for (int octave = octaveCount - 1; octave >= 0; octave--)
{
amplitude *= persistance;
totalAmplitude += amplitude;
for (int i = 0; i < width; i++)
{
for (int j = 0; j < height; j++)
{
perlinNoise[i,j] += smoothNoise[octave][i,j] * amplitude;
}
}
}
// 归一化最终的 Perlin Noise
for (int i = 0; i < width; i++)
{
for (int j = 0; j < height; j++)
{
perlinNoise[i,j] /= totalAmplitude;
}
}
return perlinNoise;
}
这里有一点是需要注意的,我们很多时候想要生成的 Perlin Noise 需要是一张循环纹理(可以平铺在表面上,在衔接处不会产生不自然的效果)。原始资料中指出,关键点就在 Mathf.Pow(2, octave) 这里,以及提供的 width 和 height 需要是2的幂次方。其实也可以是 Mathf.Pow(3, octave),width 和 height 是3的幂次方(一般都使用2的幂次方)。其原因就在生成采样纹理时,进行插值的部分。如果每一行(列)的最后一步插值可以正好是下一个 Tile 的第一个点,那么就会生成一个循环的 Perlin Noise。
应用:残蚀效果
| _ | _ |
|---|---|
![]() |
![]() |
不同的 Perlin Noise 会产生不同的残蚀效果,我们可以多生成几张 Perlin Noise 纹理,经过对比来达到想要的效果。也可以把几张 Perlin Noise 纹理进行叠加,产生更丰富的效果。一般来说,直接将 Perlin Noise 纹理叠加到 Diffuse 纹理上是不会产生这么锐利的边缘过度的,所以需要对 Perlin Noise 渐变过度进行放大处理,这个放大操作可以预处理在 Texture 上,也可以在 Shader 中。
应用:动态体积雾
| 原始FogVolume | Perlin Noise FogVolume | Perlin Noise FogVolume GIF |
|---|---|---|
![]() |
![]() |
![]() |
体积雾的实现原理可以看这里,效果就是左图。可以看到雾并没有层次,也无法表现出在空中飘动的效果。使用 Perlin Noise 叠加后,雾就有了层次感(中间图),对 Perlin Noise Texture 进行 uv 动画后,雾也产生了飘动的效果(右图),并且由于我们的 Perlin Noise 是可平铺的,所以 uv 动画在边缘不会产生突变。
Perlin Noise 及其应用的更多相关文章
- 【Ray Tracing The Next Week 超详解】 光线追踪2-4 Perlin noise
Preface 为了得到更好的纹理,很多人采用各种形式的柏林噪声(该命名来自于发明人 Ken Perlin) 柏林噪声是一种比较模糊的白噪声的东西:(引用书中一张图) 柏林噪声是用来生成一些看似杂乱 ...
- python perlin noise
python 利用 noise 生成纹理. # -*- coding: utf-8 -*- """ Created on Mon Apr 23 20:04:41 2018 ...
- 利用perlin noise 生成 wood texture
%%% Perlin Noise %%% Wood_texture clc; clear all; close all; addpath('E:\PhotoShop Algortihm\Image P ...
- OpenCV——Perlin Noise
// define head function #ifndef PS_ALGORITHM_H_INCLUDED #define PS_ALGORITHM_H_INCLUDED #include < ...
- 利用Perlin nosie 完毕(PS 滤镜—— 分成云彩)
%%%% Cloud %%%% 利用perlin noise生成云彩 clc; clear all; close all; addpath('E:\PhotoShop Algortihm\Image ...
- 利用Perlin nosie 完成(PS 滤镜—— 分成云彩)
%%%% Cloud %%%% 利用perlin noise生成云彩 clc; clear all; close all; addpath('E:\PhotoShop Algortihm\Image ...
- GraphicsLab Project 之 Curl Noise
作者:i_dovelemon 日期:2020-04-25 主题:Perlin Noise, Curl Noise, Finite Difference Method 引言 最近在研究流体效果相关的模拟 ...
- convas demo1
1 getContext 语法 Canvas.getContext(contextID) 参数 参数 contextID 指定了您想要在画布上绘制的类型.当前唯一的合法值是 "2d" ...
- 39. Volume Rendering Techniques
Milan Ikits University of Utah Joe Kniss University of Utah Aaron Lefohn University of California, D ...
随机推荐
- EXECUTE 后的事务计数指示 BEGIN 和 COMMIT 语句的数目不匹配
代码中的事务包含了存储过程中的事务.
- 利用maven的filter和profile实现不同环境使用不同的配制
在我们平常的java开发中,会经常使用到很多配制文件(xxx.properties,xxx.xml),而当我们在本地开发(dev),测试环境测试(test),线上生产使用(product)时,需要不停 ...
- Func系列1:安装配置
简介 Func是由红帽子公司以Fedora平台构建的统一网络控制器,是为解决集群管理.监控问题而设计开发的系统管理框架.它是一个能有效简化多服务多服务器系统管理工作的工具,它易于学习.使用和扩展,功能 ...
- 一款灵活好用的日历控件Kalendae
Kalendae是一款纯js不依赖任何js库的日历控件,可以轻松实现显示月份数量,当前选中多个日期,并可以按照周等你想要的格式去定制选中项. 下载地址:GitHub/Kalendae 第一步:Kale ...
- mongodb的读写分离
转自:http://blog.csdn.net/sd0902/article/details/21538621 mongodb的读写分离使用Replica Sets来实现 对于replica set ...
- abstract 与 interface
接口和抽象类: 最本质的区别:抽象类是一个不完全的类,是对对象的抽象,而接口是一种行为规范. a. interface中不能有字段,abstract class则可以:b. interface可以被多 ...
- 一个原生的JavaScript拖动方法
代码: 1 function drag(t,p){ 2 3 var point = p || null, 4 target = t || null, 5 resultX = 0, 6 resultY ...
- 用CRT connect MongoDB 使用Backspace无效
这是个很蛋疼的小问题... 使用./mongo 10.1.235.62:27017 连接上后 打错了无法删除!? 这是在逗我,那就修改CRT个设置,点击选项,会话选项,仿真,把终端改成Linux就行了 ...
- NeHe OpenGL教程 第三十二课:拾取游戏
转自[翻译]NeHe OpenGL 教程 前言 声明,此 NeHe OpenGL教程系列文章由51博客yarin翻译(2010-08-19),本博客为转载并稍加整理与修改.对NeHe的OpenGL管线 ...
- redis 数据持久化
1.快照(snapshots) 缺省情况情况下,Redis把数据快照存放在磁盘上的二进制文件中,文件名为dump.rdb.你可以配置Redis的持久化策略,例如数据集中每N秒钟有超过M次更新,就将数据 ...




