《The Book Of Shader》筆記,有增刪。程序員
說到隨機函數,JavaScript 中有 Math.random()
,PHP 中有rand()
,在圖形繪製時,隨機也無處不在。《The Book Of Shader》 經過一個簡單的函數衍化,讓咱們瞭解隨機:express
經過fract()
和sin()
的結合,咱們獲得了一個有必定規律但被打亂的曲線,當咱們把1.0
變成無限大時,再看看效果:app
咱們把上面的公式封裝成rand()
函數:dom
Shader 中的隨機是肯定性隨機(僞隨機),也就是當咱們的輸入值肯定時,輸出值也是肯定的,而 JavaScript 和 PHP 則是非肯定隨機,每次隨機出來的內容是不同的。固然咱們還能夠對隨機增長一些變化:wordpress
rand()*rand()
會讓值更趨近於 0: 函數
更多的隨機研究能夠看這篇文章,你會發現隨機數也是能夠「操做」的: ui
你會發現隨機圖表中,會有兩個地方的隨機分佈不均勻(-1.5707 ~ 1.5707),這是 sin() 最大值和最小值的地方,因此咱們在取值的時候儘可能避免這兩個地方:spa
如今咱們對隨機有了深刻的理解,是時候將它應用到二維,x 軸和 y 軸。爲此咱們須要將一個二維向量轉化爲一維浮點數。這裏有幾種不一樣的方法來實現,但 dot() 函數在這個例子中尤爲有用。它根據兩個向量的方向返回一個 0.0 到 1.0 之間的值。—— refer3d
若是你對下面的vec2(12.23,78.32)))*232348.23)
留有疑問,姑且將其理解爲 magic number,它的效果就跟電視沒有信號時的雪花效果同樣:code
下面對這些隨機數作一些操做:
封裝函數:
// 僞隨機
float random (float n) {
return fract(sin(n)*1000000.);
}
float random (vec2 st) {
return fract(sin(dot(st.xy,vec2(12.9898,78.233)))*43758.5453123);
}
// 散列函數(哈希值)
float hash(float n) {
return fract(sin(n) * 1e4);
}
float hash(vec2 p) {
return fract(1e4 * sin(17.0 * p.x + p.y * 0.1) * (0.1 + abs(sin(p.y * 13.0 + p.x))));
}
複製代碼
噪聲跟隨機有什麼不一樣?
噪音的基礎來自於隨機數,隨機數的特色是每一個點的值都是離散的,相互徹底沒有關係,而噪音則是讓離散的隨機數連續起來。最簡單的連續化處理就是插值,在離散數據中間用函數插值的方法把空隙填滿空間就天然連續了。說到插值,學過數值分析的馬上就能想到七八種插值方法,只要能保持連續性不論是三角函數,正態分佈,仍是樣條曲線均可以使用。—— 不僅是噪音
有了噪音咱們就能夠還原出天然界的真實景象:
如何獲得一個離散的隨機值,能夠經過上面的隨機函數:
接着把這些離散的隨機值經過mix()
線性插值的方式鏈接起來:
經過smoothstep()
函數讓變化更圓滑:
在一些 noise 的應用中你會發現程序員喜歡用他們本身的三次多項式函數(好比下面的例子),而不是用smoothstep()
,結果是同樣的。
經過這種方式獲得了一段 「噪音」。
當咱們把它做爲值,顯示在畫布中,會是什麼樣子呢?能夠看到一維的噪音並無太大的價值:
能夠用直接封裝好的noise()
函數(文章底部會羅列這些函數的聲明):
2D 噪聲在圖形角度才更具有價值,其自變量再也不是水平或垂直的一個值而是二維的值:
當咱們使用已經封裝好後的 2D noise() 函數並傳入座標後,看看效果:
函數封裝:
// 一維(這裏都是基於hash,也能夠改爲基於random
float noise(float x) {
float i = floor(x);
float f = fract(x);
float u = f * f * (3.0 - 2.0 * f);
return mix(hash(i), hash(i + 1.0), u);
}
// 二維
float noise(vec2 x) {
vec2 i = floor(x);
vec2 f = fract(x);
// Four corners in 2D of a tile
float a = hash(i);
float b = hash(i + vec2(1.0, 0.0));
float c = hash(i + vec2(0.0, 1.0));
float d = hash(i + vec2(1.0, 1.0));
// Simple 2D lerp using smoothstep envelope between the values.
// return vec3(mix(mix(a, b, smoothstep(0.0, 1.0, f.x)),
// mix(c, d, smoothstep(0.0, 1.0, f.x)),
// smoothstep(0.0, 1.0, f.y)));
// Same code, with the clamps in smoothstep and common subexpressions
// optimized away.
vec2 u = f * f * (3.0 - 2.0 * f);
return mix(a, b, u.x) + (c - a) * u.y * (1.0 - u.x) + (d - b) * u.x * u.y;
}
// 三維
float noise(vec3 x) {
const vec3 step = vec3(110, 241, 171);
vec3 i = floor(x);
vec3 f = fract(x);
// For performance, compute the base input to a 1D hash from the integer part of the argument and the
// incremental change to the 1D based on the 3D -> 1D wrapping
float n = dot(i, step);
vec3 u = f * f * (3.0 - 2.0 * f);
return mix(mix(mix( hash(n + dot(step, vec3(0, 0, 0))), hash(n + dot(step, vec3(1, 0, 0))), u.x),
mix( hash(n + dot(step, vec3(0, 1, 0))), hash(n + dot(step, vec3(1, 1, 0))), u.x), u.y),
mix(mix( hash(n + dot(step, vec3(0, 0, 1))), hash(n + dot(step, vec3(1, 0, 1))), u.x),
mix( hash(n + dot(step, vec3(0, 1, 1))), hash(n + dot(step, vec3(1, 1, 1))), u.x), u.y), u.z);
}
複製代碼
相關連接: