【零基礎】充分理解WebGL(七)

語言: CN / TW / HK

接上篇 http://juejin.cn/post/7111197443043590174

隨機

前面介紹的一些圖案生成思路都是通過確定的數學函式加上週期重複來實現的,而大自然中有很多現象幾乎是無序的,要模擬這些無序的自然現象,就要用到隨機。

在JavaScript中,Math.random返回0到1之間的隨機數,但對著色器來說沒有現成的隨機函式,只能通過人工來構建隨機函式。

為了說明構造隨機的原理,我們暫時先把WebGL放一放,先用JavaScript,在Canvas2D上繪圖來看一下。

```js const canvas = document.querySelector('canvas'); const width = 400 * window.devicePixelRatio; const height = 400 * window.devicePixelRatio;

canvas.width = width; canvas.height = height;

const random = Math.random;

function drawRandomPixel(ctx) { const [w, h] = [random() * width, random() * height]; ctx.fillRect(w, h, 1, 1); }

const ctx = canvas.getContext('2d'); ctx.fillStyle = 'black';

for(let i = 0; i < 0.25 * width * height; i++) { drawRandomPixel(ctx); } ```

http://code.juejin.cn/pen/7113777145381715975

如上面程式碼所示,我們使用在畫布上打隨機點的方式來繪製一張圖片,通過random函式得到點的座標,然後通過ctx.fillRect將點繪製到畫布上,這樣我們得到了一張黑色噪點的圖案。

這裡我們預設是用JavaScript內建的Math.random函式繪製的,現在我們要嘗試自己實現Math.random函式,我們來看一下:

```js const canvas = document.querySelector('canvas'); const width = 400 * window.devicePixelRatio; const height = 400 * window.devicePixelRatio;

canvas.width = width; canvas.height = height;

function fract(num) { return Math.abs(num % 1); }

let seed = 0; function random() { return fract(Math.sin(seed++) * 1e5); }

function drawRandomPixel(ctx) { const [w, h] = [random() * width, random() * height]; ctx.fillRect(w, h, 1, 1); }

const ctx = canvas.getContext('2d'); ctx.fillStyle = 'black';

for(let i = 0; i < 0.25 * width * height; i++) { drawRandomPixel(ctx); } ```

我們修改了random函式,用:

js let seed = 0; function random() { return fract(Math.sin(seed++) * 1e5); }

代替了Math.random,這裡我們用fract函式取sin函式的小數部分,而把sin函式乘以1e5是為了擷取到小數點後5位之後的部分

這是因為,sin函式顯然不是隨機的,但是它的小數點後N位之後的值,由於浮點數精度原因,呈現出隨機性。

你把這裡的Math.sin換成Math.cos或Math.tan也可以得到類似的結果,但也不是所有的函式都可以,這和函式曲線有關,比如Math.log就不行。

http://code.juejin.cn/pen/7113781115059437604

Shader的常用偽隨機函式

理解了偽隨機原理,我們來看一下shader中的常用偽隨機函式:

glsl highp float random(vec2 co) { highp float a = 12.9898; highp float b = 78.233; highp float c = 43758.5453; highp float dt= dot(co.xy ,vec2(a,b)); highp float sn= mod(dt,3.14); return fract(sin(sn) * c); }

原理上,和我們上面取sin函式小數點後幾位的道理是一樣的,只不過把seed換成二維向量,用的引數值讓隨機分佈更均勻。

這樣,我們就可以用隨機函式來繪圖了。

比如我們可以給菱形網格賦予隨機顏色:

http://code.juejin.cn/pen/7113792361355804708

還有下面這個著名的10 PRINT CHR$(205.5+RND(1)); : GOTO 10迷宮生成器也是通過偽隨機數來實現的:

http://code.juejin.cn/pen/7113802534057148424

最後,留給大家一個問題:

因為random是偽隨機函式,意味著我們每次執行上面的程式碼,繪製出的顏色網格和迷宮形狀是一樣的,我們能像JavaScript的random那樣,每次運營的時候,讓它繪製出不一樣的隨機網格顏色和隨機迷宮形狀嗎?

如果你想到了該怎麼做,可以在碼上掘金裡修改上面的程式碼,實現效果,然後將它分享到評論區。