2
3

WebGLとThree.jsを用いたフラグメントシェーダーによる、高速な並列計算リアルタイムカラフルアニメーション。

Last updated at Posted at 2024-09-17

スクリーンショット 2024-09-18 055453.png

スクリーンショット 2024-09-18 055505.png

WebGLとThree.jsを用いたフラグメントシェーダーによるリアルタイムカラフルアニメーションの生成

要約
本研究では、WebGLとThree.jsを用いてフラグメントシェーダーを実装し、画面全体にわたるリアルタイムのカラフルなアニメーションを生成する手法を提案する。時間変化と座標に基づく色の生成アルゴリズムを組み合わせ、動的かつ視覚的にど派手なエフェクトを実現した。

  1. はじめに
    リアルタイムで動的なビジュアルエフェクトを生成する手法は、ゲームやシミュレーション、インタラクティブアートなど多くの分野で注目されている。特に、GPUを利用したシェーダープログラムは、高速な並列計算により複雑なビジュアルエフェクトの生成を可能とする。

  2. 使用技術
    提案手法は、JavaScriptライブラリであるThree.jsを用いてWebGLを操作し、リアルタイムでアニメーションを描画する。シェーダープログラムは、頂点シェーダーとフラグメントシェーダーの2つのプログラムで構成される。本研究では、フラグメントシェーダーにおいて色の計算を行い、その結果を画面上に表示する。

  3. 実装手法
    3.1 フラグメントシェーダー
    フラグメントシェーダーは、ピクセル単位で実行され、各ピクセルの最終的な色を決定する。本研究では、画面上の座標に基づく色の計算を行うために、シェーダーコード内で変数 vUv を利用して、スクリーン座標を取得している。

3.2 カラフルなエフェクト生成
カラフルなエフェクトを生成するため、時間 u_time と座標 vUv に基づいて sin および cos 関数を組み合わせた。フラグメントシェーダー内で以下の式を用いて、RGB成分を計算する。

r = abs(sin(vUv.x * 10.0 + u_time * 2.0) * cos(vUv.y * 10.0 + u_time * 3.0));
g = abs(sin(vUv.y * 20.0 + u_time * 4.0) * cos(vUv.x * 20.0 + u_time * 2.0));
b = abs(sin((vUv.x + vUv.y) * 15.0 + u_time * 5.0));

これらの計算は、座標と時間に基づいて絶えず変化する色を生成するため、動的かつ派手なエフェクトが画面上に現れる。また、abs 関数を用いることで、RGB値が常に正の値となり、色の変化がスムーズに行われるよう工夫している。

3.3 アニメーションとレンダリング
JavaScript内でのアニメーションは、requestAnimationFrame を利用して毎フレーム更新される。各フレームごとに時間 u_time を更新し、フラグメントシェーダーに渡すことで、リアルタイムでアニメーションが変化する。

  1. 結果
    本実装により、Webブラウザ上でリアルタイムに変化するカラフルなアニメーションを描画することに成功した。座標と時間を組み合わせた色の生成アルゴリズムにより、単純なシェーダーコードでありながら、視覚的に豊かなエフェクトを実現している。また、フルスクリーンで描画できる汎用性を持つ。

  2. 考察
    本研究で使用したシェーダーコードは、sin や cos 関数を組み合わせた単純なものであるが、時間変化を導入することで視覚的にど派手なアニメーションを実現している。さらに、フラグメントシェーダーによる数値計算の結果を直接色として表現することで、グラフィックスプログラミングにおけるアート表現の一端を示した。

  3. 結論
    本研究では、WebGLとThree.jsを用いたフラグメントシェーダーによるリアルタイムアニメーションの生成を提案した。座標と時間に基づくシンプルな数値計算を用いることで、カラフルでダイナミックなビジュアルエフェクトを実現した。本手法はWebベースのインタラクティブなグラフィックスコンテンツの可能性を追求する基盤となる。

シェーダーコードを埋め込んでいることによってとてもシンプルに記述されてます。
ライブラリーの機能を使用することで コンパイルなどの記述も必要なくなっています

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Full-Screen Colorful Shader Animation</title>
    <style>
        body { margin: 0; overflow: hidden; }
        canvas { display: block; }
    </style>
</head>
<body>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
    <script id="vertexShader" type="x-shader/x-vertex">
        varying vec2 vUv;
        void main() {
            vUv = uv;
            gl_Position = vec4(position, 1.0); // 画面全体に描画
        }
    </script>
    <script id="fragmentShader" type="x-shader/x-fragment">
        uniform float u_time;
        varying vec2 vUv;

        void main() {
            // 派手なアニメーションを作るための数値計算
            float r = abs(sin(vUv.x * 10.0 + u_time * 2.0) * cos(vUv.y * 10.0 + u_time * 3.0));
            float g = abs(sin(vUv.y * 20.0 + u_time * 4.0) * cos(vUv.x * 20.0 + u_time * 2.0));
            float b = abs(sin((vUv.x + vUv.y) * 15.0 + u_time * 5.0));

            // 色を合成して派手なカラーアニメーションを作成
            vec3 color = vec3(r, g, b);
            gl_FragColor = vec4(color, 1.0);
        }
    </script>
    <script>
        // シーン、カメラ、レンダラーの初期化
        const scene = new THREE.Scene();
        const camera = new THREE.Camera();
        const renderer = new THREE.WebGLRenderer();
        renderer.setSize(window.innerWidth, window.innerHeight);
        document.body.appendChild(renderer.domElement);

        // 平面ジオメトリの作成(フルスクリーン用)
        const geometry = new THREE.PlaneGeometry(2, 2);

        // シェーダーマテリアルの作成
        const material = new THREE.ShaderMaterial({
            vertexShader: document.getElementById('vertexShader').textContent,
            fragmentShader: document.getElementById('fragmentShader').textContent,
            uniforms: {
                u_time: { value: 0.0 }
            }
        });

        // メッシュの作成
        const plane = new THREE.Mesh(geometry, material);
        scene.add(plane);

        // アニメーションループ
        function animate(time) {
            requestAnimationFrame(animate);
            
            // 時間をシェーダーに渡す
            material.uniforms.u_time.value = time * 0.001;

            renderer.render(scene, camera);
        }

        // ウィンドウのリサイズに対応
        window.addEventListener('resize', () => {
            renderer.setSize(window.innerWidth, window.innerHeight);
        });

        animate();
    </script>
</body>
</html>

2
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
3