噪声函数

上周在研究噪声函数,作为一个纯新手,过程可谓艰辛,一个一维噪声足足啃了我两整天的时间,才悟出一点眉目,说多了都是泪,特写此文献给同我一样的小白,希望可以能够为你们节约一些宝贵的时间。

本人图形学基础为零,若有错误之处请勿喷,欢迎指正。

本文编程基于AS3。


相关文章:

[1] 随机数生成算法 :http://blog.sina.com.cn/s/blog_4abaa7fc01009tgj.html


什么是噪声:

以最简单的白噪声举例,白噪声就是一大堆随机数,所以一维白噪声就是一个一维的随机数数组,二维三维同理。由于AS3的随机数无法指定种子,所以我参考网上的资料自己写了一个随机函数,系数似乎是出自C++?忘了抱歉。。。

默认种子为:1

package 
{
	/**
	 * ...
	 * @author Christon
	 */
	public class Random 
	{
		private static var r:uint = 1;
		
		private static const A:uint = 1103515245;
		private static const C:uint = 12345;
		private static const M:uint = 32767;
		
		public static function seed(value:Number):void
		{
			Random.r = value;
		}
		
		public static function random():Number
		{
			r = (r * A + C) / M;
			
			return (r % M) / M;
		}
	}
}
我们可以用一个二维白噪声图检验这个算法的好坏,代码就免了,学了二维噪声就会写了


噪声很均匀,但如果我们把 C 换成100再试试:


一条黑乎乎的斜线表明了它是个失败的随机。接下来的学习,从一维噪声开始。


一维噪声

首先利用随机函数生成一个一维噪声数组

var width:int = 500;
var whiteNoise:Array = [];
for (var i:int = 0; i < width; i ++)
{
	whiteNoise[i] = Random.random();
}

然后在舞台上将它画出来,在此小白们应当先百度一下正弦波的波长,震幅和频率各是什么鬼,因为绘画噪声曲线时将会用到这些知识。
简单地说,若振幅为0,则一维噪声将是一条直线,振幅越大,曲线波动越大。波长则决定两个波峰或波谷之间的距离,波长=1/频率,补充完后就继续吧。
现在假设噪声波长0.1,振幅100,则
var amplitude:int = 100;
var frequency:int = 10;
由于加入了波长,故现在两个噪声值之间座标其实应当变长10了而不是原来的1,只有这样才能绘制曲线,咱先使用最简单的线性插值函数来画线好了
function linearInterpolate(a:Number, b:Number, x:Number):Number
{
	return a * (1 - x) + b * x;
}
var bmd:BitmapData = new BitmapData(whiteNoise.length * frequency, amplitude);
var d0:int = 0;
for (var i:int = 0; i < whiteNoise.length - 1; i ++)
{
	//获取相邻的两个噪声值
	var a:int = whiteNoise[i] * amplitude;
	var b:int = whiteNoise[i + 1] * amplitude;
	for (var j:int = 0; j < frequency; j ++ )
	{
		//求导
		var d:int = this.linearInterpolate(a, b, j / frequency);
		bmd.setPixel(i * frequency + j, d, ColorTools.fromRGB(0, 0, 0));
	}
}
this.bitmapData = bmd;
果如下图所示:

不太理想,作下补间吧,然后再将频率改为20。
//...
//求导
//...
//补间
if (d0 != d)
{
	var k:int = d0;
	if (d0 < d)
	{
		for (; k < d; k ++ )
		{
			bmd.setPixel(i * frequency + j, k, ColorTools.fromRGB(0, 0, 0));
		}
	}
	else
	{
		for (; k > d; k -- )
		{
			bmd.setPixel(i * frequency + j, k, ColorTools.fromRGB(0, 0, 0));
		}
	}
	d0 = d;
}
//...
这次效果好多了

当然啦,线性插值的效果是很不理想的,现在我们把线性插值改成余弦插值:
function cosineInterpolate(a:Number, b:Number, x:Number):Number
{
	var ft:Number = x * Math.PI;
	var f:Number = (1 - Math.cos(ft)) * 0.5;
	return a * (1 - f) + b * f;
}
然后再将频率改成30,然后把随机数的种子改为6,这次真的好看多了,嘻嘻

是不是有一种眺望远山的赶脚?很惭愧我想说就上面这些东西,我整整逐磨了两天,为啥呢?经过深刻的反思,总结出的原因是自己忽略了振幅和插值函数的用处,高级语言全是graphics有木有,对于完全不了解插值函数的用处的人,折腾来折腾去都出不来流线型的曲线,所以移植了两天网上的代码,各种失败,我画出来的曲线都是这样的,最终只能放弃网上的代码自己折腾,终于大功告成,不知道有没有和我一样掉在这个坑里的,/捂脸,同时吐槽下那么多帖子各种号称的完整代码里就为啥没有画线的过程呢你说

感觉可以告一段落了,休整一下来日再战。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章