iT邦幫忙

第 11 屆 iThome 鐵人賽

DAY 21
1
Modern Web

寫給工程師的 WebGL 學習心得系列 第 21

[WebGL - Day21] Shadertoy + PixiJS,電視雜訊效果實作

  • 分享至 

  • xImage
  •  

系列文來到三部分,開始寫些什麼了!


這次寫的是類似電視雜訊的效果:
Noise
分為兩個部分:

  • Shadertoy 電視線效果 (效果參考自 The Art of Code - Value Noise Explained!)
  • 將效果搬移到 PixiJS

在 Shadertoy 上寫 fragment shader

電視雜訊的效果分作兩個部分,xy

  • 先做其中的 x
float c = fract(sin(uv.x*2304.)*3057.);

fract sin x
驚人,我知道 sin() 會回傳 -1~1
怎麼加上 fract() 跟乘上神奇數字就變成...類似條碼的效果了?


fract:

fract — compute the fractional part of the argument
Declaration
genType fract( genType x);
Description
fract returns the fractional part of x. This is calculated as x - floor(x).

負數取餘數:

被除數負數時,除出來的結果是多少?

JavaScript

>  1 % 3
<. 1
>  -1 % 3
<. -1
>  -2 % 3
<. -2

JavaScript 裡, 被除數 小於 0 時,餘數負數

Shader
The Book of Shaders: fract
The Book of Shaders: fract
從圖上看出,寫 Shader 時, fract() 取餘數的結果是正數

sin() 的結果是 -1 ~ 1 ,用 fract 取餘數的結果就是 0 ~ 1

註: 負數取餘數的實作取決於程式語言,並非所有語言的結果都相同


神奇數字,沒特別要多大,但是要夠大

why?

如果直接取 sin() 的餘數
由於 uv.x 的範圍是 0.~1.
sin(uv.x) 的結果會是 sin(0.) ~ sin(1.)
大約是 0~0.8414709848078965

float c = fract(sin(uv.x));

fract sin x*1
輸出會從約 84% 白色的漸層

  • sin(uv.x*2.):

sin(uv.x*2.) 的結果會是 sin(0.) ~ sin(2.),負數取餘數是正數

float c = fract(sin(uv.x*2.));

fract sin x*2

  • sin(uv.x*6.):

sin(uv.x*6.) 的結果會是 sin(0.) ~ sin(6.)

float c = fract(sin(uv.x*6.));

簡諧運動加上負數取餘數的結果,畫面有點特別
fract sin x*6


加上 y 的效果

float c = fract(sin(uv.x*2304.+uv.y*181.)*3057.);

fract sin
真的只是加上 uv.y ,就完成了

uv.x 跟 uv.y 如果乘的是一樣的數字,
結果會很規律,不是想要的效果
fract sin

數學真是太神奇了...

如同電視雜訊,一直沙沙沙的效果?
把 Shadertoy 內建變數 iTime 乘進輸出公式

float c = fract(sin(uv.x*2304.+uv.y*181.)*3057.*iTime);

Noise
完成!

看起來好像很吃效能的效果,意外的有些單純


搬到 PixiJS 上

前幾天的文章有提到在 PixiJS 裡寫濾鏡的方法,
將剛剛完成的 fragment shader 放進 PixiJS 濾鏡 裡:

JavaScript

const shaderFrag = `
precision highp float;

varying vec2 vTextureCoord;

uniform vec4 inputSize;
uniform vec4 outputFrame;
uniform float time;

void main() {
  float c = fract(sin(vTextureCoord.x*2304.+vTextureCoord.y*181.)*3057.*time);
  gl_FragColor = vec4(c) * 0.7;
}
`;

const filter = new PIXI.Filter(null, shaderFrag, {
    time: 0,
});
filterContainer.filters = [filter];

Noise

  • 沒有 ShadertoyiTime 變數,自己寫 uniform
const filter = new PIXI.Filter(null, shaderFrag, {
    time: 0,
});
filterContainer.filters = [filter];


let time = 0;
app.ticker.add((delta) => {
    time ++;
    filter.uniforms.time = (time);
});

完成!


這是目前我實作 PixiJS 濾鏡的方法:

  • 先在 Shadertoy 上寫,確認 Fragment Shader 寫法、
  • 整理出透過 JavaScript 傳入的 uniform 變數
  • PixiJS 完成效果

上一篇
[WebGL - Day20] 在 three.js 裡寫 Shader
下一篇
[WebGL - Day22] PixiJS 裡實作圓形淡入效果濾鏡
系列文
寫給工程師的 WebGL 學習心得30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言