【零基礎】充分理解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那樣,每次運營的時候,讓它繪製出不一樣的隨機網格顏色和隨機迷宮形狀嗎?

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