【GLSL】glslifyのノイズ(noise)の使い方とデモ

WebGL & Three.js

WebGL(GLSL)でよく使うglslifyのノイズ(noise)の使い方を紹介します。
デモもあるので確認してみてください。

【GLSL】glslifyのノイズ(noise)のデモ







【GLSL】glslifyのノイズ(noise)の使い方

glslifyをインストール

まずはglslifyをインストールします。
glslifyのGithub

npm install -D glslify

そしてpackage.jsonに以下のように入っていればOKです。

  "dependencies": {
    ...
    "glslify": "^7.1.1",
    ...
    "three": "^0.152.0"
  },

2Dと3Dを読み込む

// 2Dと3Dのシンプレックスノイズ関数をインポート
#pragma glslify: noise2 = require(glsl-noise/simplex/2d);
#pragma glslify: noise3 = require(glsl-noise/simplex/3d);

そして、やりたいことに合わせて2Dもしくは3Dを以下のようにして読み込みます。
X軸とY軸のみを扱う場合は、2dの方を使います。
X軸とY軸に加えて、時間などの経過で模様を変化させたいときは3dを使います。

ノイズを生成する

あとは、以下のようにするだけでノイズを生成できます。

  float n = noise2(vUv);

基本のコードについて知りたい場合は、【WebGL&Three.js入門】Three.jsの一番シンプルなサンプルコードのチュートリアルの解説にて詳しく解説しているので参考にしてみてください。
今回はglslifyのノイズ(noise)に関係しているところを解説します。

フラグメントシェーダのコード

ノイズ関数を使用して動的なビジュアルエフェクトを生成するフラグメントシェーダーのコードです。

// 浮動小数点数の精度を中程度に設定
precision mediump float;

// 2Dと3Dのシンプレックスノイズ関数をインポート
#pragma glslify: noise2 = require(glsl-noise/simplex/2d);
#pragma glslify: noise3 = require(glsl-noise/simplex/3d);

// テクスチャ座標をバーテックスシェーダーから受け取る
varying vec2 vUv;

// ユニフォーム変数として時間を受け取る
uniform float uTick;

void main() {
  // ノイズのスケールを定義
  const float SIZE = 10.0;

  // ノイズの動きのスピードを定義
  const float SPEED = 0.02;

  // 通常の2Dノイズを生成
  float n = noise2(vUv);

  // スケールを大きくして細かいノイズを生成
  n = noise2(vUv * SIZE);

  // 時間経過と共に細かくなるノイズを生成
  n = noise2(vUv * uTick * SPEED);

  // 時間経過と共に移動するノイズを生成
  n = noise2(vUv * SIZE - uTick * SPEED);

  // X軸方向に移動するノイズを生成
  n = noise2(vec2(vUv.x * SIZE + uTick * SPEED, vUv.y));

  // 波のように揺れるノイズを生成
  n = noise2(vec2(vUv.x * 80.0 - sin(vUv.y + uTick / 80.0), vUv.y * SIZE - sin(vUv.x + uTick / 80.0)));

  // 3Dノイズを使って時間経過と共に模様が変化するエフェクトを生成
  n = noise3(vec3(vUv * SIZE, uTick * SPEED));

  // 横縞模様の3Dノイズを生成
  n = noise3(vec3(vUv.x * SIZE * 2.0, vUv.y * SIZE * 7.0, uTick * SPEED));

  // 最終的なフラグメントの色を設定(青緑のグラデーション)
  gl_FragColor = vec4(0.0, n, n, 1.);
}

vUv変数はテクスチャ座標を表し、uTick変数は時間を表しています。
ノイズ関数の出力値nは、最終的に色の青緑成分に使用され、動的なビジュアルパターンを生成しています。
上の場合、ノイズ関数の出力値nをgl_FragColorで緑と青の部分にのみいれて青緑色になるようにしています。

それぞれのケースでみてみましょう。

通常の2Dノイズを生成

float n = noise2(vUv);

上のようなシンプルなコードで生成できます。
通常のノイズは割と大きいです。

スケールを大きくして細かいノイズを生成

    const float SIZE = 10.0;
    n = noise2(vUv * SIZE);

ノイズのサイズを細かくしたい場合、UV座標に掛け算してあげると粒子が細かくなります。
10をかけると上のようになり、100をかけると下のようになります。

時間経過と共に細かくなるノイズを生成

  n = noise2(vUv * uTick * SPEED);

時間経過と共に変化させるには、uTickにスピードをかけて調整してあげます。
ここから詳しい見た目はデモを参照した方がわかりやすいです。

時間経過と共に移動するノイズを生成

  n = noise2(vUv * SIZE + uTick * SPEED);

移動はuTickを足したり引いたりしてあげることで調整することができます。

X軸方向に移動するノイズを生成

  n = noise2(vec2(vUv.x * SIZE - uTick * SPEED, vUv.y));

X軸だけ移動させたい場合は、vUvのX軸だけに上の処理を追加すればOKです。
Y軸でやりたい場合も同様です。
移動の向きを変えたいときは+と-を変えればいいです。

波のように揺れるノイズを生成

  n = noise2(vec2(vUv.x * 80.0 - sin(vUv.y + uTick / 80.0), vUv.y * SIZE - sin(vUv.x + uTick / 80.0)));

波の場合は基本的には上と同じですが、三角関数のsineなどを使います。
sineなどの三角関数については、必要な最低限の知識部分のみ【WebGL】GLSLで使う数学(三角関数、ベクトル)を文系エンジニアのために実務でいるところだけ解説で詳しく解説しているので確認してみてください。

3Dノイズを使って時間経過と共に模様が変化するエフェクトを生成

  n = noise3(vec3(vUv * SIZE, uTick * SPEED));

時間経過と共に模様が変化させたい場合は三次元必要になるので、3Dノイズを使います。
vec3で三次元を指定して、最初の二つはX軸、Y軸、最後の軸は時間としてuTickの値をとれば時間の経過とともに模様が変化するようになります。

横シマ模様の3Dノイズを生成

  n = noise3(vec3(vUv.x * SIZE * 2.0, vUv.y * SIZE * 7.0, uTick * SPEED));

vUvのxとyを分けて記述することで、大きさをx軸とy軸で変えることができます。
あとは基本的に上と同じようにしてスピードや大きさを調整します。
縦シマにしたい場合は、xとyの値を入れ替えてあげる(xに大きい数値をかける、yに小さい数値をかける)だけで実現できます。

水に浮かんでいるような揺れる画像


上が元の画像です。
JavaScriptのマテリアルには以下のような記述をします。

  const material = new THREE.ShaderMaterial({
    uniforms: {
      uTex: { value: await loadTex("/imagepath/noise1.jpg") },
      uTick: { value: 0 },
      uNoise: { value: new THREE.Vector2(15, 52) },
    },
    vertexShader,
    fragmentShader,
  });

フラグメントシェーダーでuniformで値をとりたいので、uNoiseというプロパティを用意して、valueには二次元をとるようにします。
そうすると、valueにxとyに指定した値を持つオブジェクトが生成できます。

precision mediump float;

#pragma glslify: noise2 = require(glsl-noise/simplex/2d);
#pragma glslify: noise3 = require(glsl-noise/simplex/3d);

varying vec2 vUv;
uniform sampler2D uTex;
uniform float uTick;
uniform vec2 uNoise;

void main() {
  const float SPEED = 0.02;
  const float DETAIL_LEVEL = 0.007;
  float noise = noise3(vec3(vUv.x * uNoise.x, vUv.y * uNoise.y, uTick * SPEED));

  vec4 tex = texture(uTex, vUv + noise * DETAIL_LEVEL);

  // gl_FragColor = vec4(noise, noise, noise, 1.);
  gl_FragColor = tex;
}

元の画像と横シマ模様の3Dノイズでみせたようなノイズと合わせることでデモのような見た目を実現できます。
noiseを生成するときに、ノイズの大きさとしてuNoiseをvUvに掛け合わせてあげることでノイズの大きさを調整できます。

  vec4 tex = texture(uTex, vUv + noise * DETAIL_LEVEL);

そして、ノイズの座標分ずらしてあげることで水面や、不思議な感じに揺らすことができます。
上の例ではノイズを足していますが、引いても逆方向に動いていい感じになります。
画像をどれくらいはっきりみせたいかはDETAIL_LEVELという定数を設けていますが、これは見ながら適切な値を見つけていく感じになります。