噪聲函數

上週在研究噪聲函數,作爲一個純新手,過程可謂艱辛,一個一維噪聲足足啃了我兩整天的時間,才悟出一點眉目,說多了都是淚,特寫此文獻給同我一樣的小白,希望可以能夠爲你們節約一些寶貴的時間。

本人圖形學基礎爲零,若有錯誤之處請勿噴,歡迎指正。

本文編程基於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有木有,對於完全不瞭解插值函數的用處的人,折騰來折騰去都出不來流線型的曲線,所以移植了兩天網上的代碼,各種失敗,我畫出來的曲線都是這樣的,最終只能放棄網上的代碼自己折騰,終於大功告成,不知道有沒有和我一樣掉在這個坑裏的,/捂臉,同時吐槽下那麼多帖子各種號稱的完整代碼裏就爲啥沒有畫線的過程呢你說

感覺可以告一段落了,休整一下來日再戰。

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