Three.jsの勉強、最近少し行き詰まり中です。
やってみたい表現はたくさんあるのに、技術が追いつかなくてもどかしい…!
そんな中で今回は、ChatGPTに相談しながら、
「ネオンっぽい色ズレ」
「グリッチっぽい揺れ」
「波のようにビビビっと走る演出」
この3つを組み合わせたアニメーションをつくってみました。
「こういうことをやりたい!」と伝えると、
参考になるコードを出してくれてありがたい。
そのままでは使えないことも多いけど、
ヒントをもらえるだけでも大きな助けになります✨❤️🔥
それでは、今回やってみたことを簡単にまとめていきたいと思います📝
完成したもの
上からビビビっと波を走らせています。
さらに、RBGがずれて少しネオンっぽい感じに🩷💚
揺れる幅や速さは細かく調整できそうです◎
使用した技術・環境
私はviteで環境構築したものを使っています。
コードを変更したときの反映スピードが速くて◎
シェーダーはGLSLという言語を使うのですが、
JavaScriptにベタがきすると少々読みづらい…
そこでvite-plugin-glsl をnpmでインストールして使ってみました。
そうするとvertex.glslとfragment.glslというファイルを別で作って、
それをJavaScriptで読み込むことができます。
これだけで使うことができます👇✨!
import glsl from 'vite-plugin-glsl'
export default defineConfig({
plugins: [
glsl()
]
})
公式のURLも貼っておきますね◎
https://www.npmjs.com/package/vite-plugin-glsl
方法
まず簡単な流れを書きますと、
①Three.jsのPlaneGeometryを準備
②ShaderMaterialで画像を貼り付け
③シェーダーで画像をゆらゆら
という感じです。
TextureLoaderで画像を読み込み、
読み込み終了してから諸々準備するという流れです。
const textureLoader = new Three.TextureLoader()
textureLoader.load(
'image.jpg',
(texture) => {
//ここでカメラ、シーン、オブジェクトを用意
},
(error) => {
console.log(error)
}
)
始めは頂点シェーダーを調整します。modelPositionのxとzをuTime(経過時間)に合わせて変更しました。
物理なんて覚えていなくて、三角関数が難しい。sin(サイン)とcos(コサイン)とか本当に分からなすぎるのですが、sinを組み合わせるとランダムな感じのゆらゆらが作れるみたい👀‼️
数値は色々調整したら好きなようにできそうです。

// グリッチタイミングに高さ(y)によるディレイを加える
float glitchTime = uTime * 0.003 - (1.0 - modelPosition.y) * 5.0;
// グリッチの強さ(揺らぎ波 × 強弱制御)
float baseWave = (sin(glitchTime) + sin(glitchTime * 3.45) + sin(glitchTime * 8.76)) / 7.0;
出てきた数値をmodelPosition.xとmodelPosition.zにプラスすることで、上から下に向けて波がいってる感じになりました🌊
次はフラグメントシェーダーでネオンっぽい動きをつけます。
// 揺れ量(時間と位置でノイズっぽく)× 強さ
float delayedTime = (uTime - (1.0 - vUv.y) * 1000.0) * 0.001;
float offset1 = (sin(delayedTime) + sin(delayedTime * 3.45) + sin(delayedTime * 8.76)) * 0.01 * waveStrength;
float offset2 = (cos(delayedTime) + cos(delayedTime * 3.45) + cos(delayedTime * 8.76)) * 0.0097 * waveStrength;
float offset3 = (sin(delayedTime) + sin(delayedTime * 3.45)) * 0.0095 * waveStrength;
RGBの3つをそれぞれ少しずつずらしていきます。これもさっきと同じでsin(サイン)とcos(コサイン)を使ってずらしました。
// それぞれUVをずらす(RGBで色分け)
vec2 uvR = vUv + vec2(offset1, 0.0);
vec2 uvG = vUv + vec2(offset2, 0.0);
vec2 uvB = vUv + vec2(offset3, 0.0);
それぞれ、uvをずらします。
// 各チャンネル取得
float r = texture2D(uTexture, uvR).r;
float g = texture2D(uTexture, uvG).g;
float b = texture2D(uTexture, uvB).b;
gl_FragColor = vec4(r, g, b, 1.0);
これをgl_FragColorに入れると、ネオンっぽくなりました。ずれる量とか速さとかは数値を色々いじると調整できます◎
覚えておきたいポイント
◎texture2Dはuvが必要💡
最初うっかりuvを渡し忘れてしまい、真っ白になってしまいました。
// 正しくはこう!
vec3 color = texture2D(uTexture, vUv).rgb;
texture2D() は「どこを表示するか」を uv で指定してあげないと、何も描画されないんですね。Fragment Shaderで画像を表示したい時は、まずここを確認!
◎ノイズ表現はrandom()だけじゃない
random() を使いたかったけど、GLSLにはビルトインの random() 関数がないので、擬似的に自分で関数を書く必要があります。
たとえばこんな感じ👇
float random2D(vec2 st) {
return fract(sin(dot(st, vec2(12.9898, 78.233))) * 43758.5453123);
}
でも、random() だけだと単調になりがち。
そこで sin() や cos() を組み合わせてみたところ、自然で面白いノイズっぽい揺れが作れました!
float wave = sin(uTime + uv.y * 10.0) + cos(uTime * 0.5 + uv.y * 5.0);
こうやって波を混ぜると、「ピリピリ感」や「ざぶーん感」が出てきて楽しいです!
今後やってみたいこと
◎ホバーした時だけこの演出をかける
◎複数画像にこの演出をかけて、スライダーにしてみる
まとめ
Three.jsとGLSLの組み合わせでイメージ通りのものができると嬉しいですね。
これからも色々な表現を試していきたいです。