1
2

シンプルでありながら強力な方法。Three.js使用によるカスタムシェーダーの実装。

Last updated at Posted at 2024-09-16

Three.jsは、WebGLを簡単に扱うためのJavaScriptライブラリであり、シェーダーを用いた視覚効果を簡潔に実装することが可能です。本システムでは、シンプルなカスタムシェーダーを用いて、球体の表面にリアルタイムで変化するテクスチャを適用します。

スクリーンショット 2024-09-16 194745.png

スクリーンショット 2024-09-16 194814.png

Three.jsを使用した動的パーリンノイズシェーダーの実装

概要
本稿では、Three.jsを使用して動的に変化するパーリンノイズシェーダーを実装する方法について説明します。Three.jsは、WebGLの抽象化ライブラリであり、視覚的なエフェクトを簡単に実装できるため、シェーダーのカスタマイズも容易です。本実装では、球体の表面にリアルタイムで変化するテクスチャを適用し、視覚的に興味深い効果を得ることを目的としています。

頂点シェーダー(Vertex Shader)

頂点シェーダーは、各頂点の位置をクリップ空間に変換し、フラグメントシェーダーに位置データを渡す役割を担います。以下のコードは、頂点シェーダーの実装例です。


varying vec3 vPosition;

void main() {
    vPosition = position;
    gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}

varying vec3 vPosition;: この変数はフラグメントシェーダーで使用するために頂点の位置を保存します。
vPosition = position;: 各頂点の位置をvPositionに保存します。
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);: 頂点の位置をクリップ空間に変換し、最終的な位置を設定します。

このコードでは、頂点の位置をvPositionとしてフラグメントシェーダーに渡し、後の処理に利用します。

フラグメントシェーダー(Fragment Shader)

フラグメントシェーダーは、パーリンノイズを生成し、これを用いて動的な色変化を適用します。簡易的なノイズ生成アルゴリズムを用いることで、リアルタイムに変化するテクスチャを実現しています。以下のコードは、フラグメントシェーダーの実装例です。


uniform float uTime;
varying vec3 vPosition;

float random(vec3 p) {
    return fract(sin(dot(p, vec3(12.9898, 78.233, 45.543))) * 43758.5453);
}

float noise(vec3 p) {
    vec3 i = floor(p);
    vec3 f = fract(p);
    float n = dot(i, vec3(1.0, 57.0, 113.0));
    return mix(mix(mix(random(i + vec3(0.0, 0.0, 0.0)), random(i + vec3(1.0, 0.0, 0.0)), f.x),
                   mix(random(i + vec3(0.0, 1.0, 0.0)), random(i + vec3(1.0, 1.0, 0.0)), f.x), f.y),
               mix(mix(random(i + vec3(0.0, 0.0, 1.0)), random(i + vec3(1.0, 0.0, 1.0)), f.x),
                   mix(random(i + vec3(0.0, 1.0, 1.0)), random(i + vec3(1.0, 1.0, 1.0)), f.x), f.y), f.z);
}

void main() {
    float n = noise(vPosition * 5.0 + uTime);
    vec3 color = vec3(0.2, 0.4, 0.6) * n;
    gl_FragColor = vec4(color, 1.0);
}

uniform float uTime;: 時間(uTime)の値をシェーダーに渡し、ノイズの動きを制御します。
varying vec3 vPosition;: 頂点シェーダーから受け取った頂点の位置を使用します。
float random(vec3 p): 簡単なランダム関数を定義します。
float noise(vec3 p): 簡単なノイズ生成関数です。パーリンノイズの簡易版で、vPositionとuTimeを使って色の変化を計算します。
gl_FragColor = vec4(color, 1.0);: ノイズに基づいて計算した色を最終的なフラグメント(ピクセル)の色として設定します。

このコードでは、random関数を用いて基本的なノイズを生成し、noise関数を用いてスムーズなノイズを作成します。uTimeは時間を示すユニフォームで、ノイズの動的な変化を実現します。

結論
本稿で示したThree.jsによるパーリンノイズシェーダーの実装は、シンプルでありながら強力な方法で、リアルタイムで動的に変化するテクスチャを実現します。Three.jsを用いることで、複雑なWebGLの処理を簡素化し、視覚的に魅力的なエフェクトを簡単に実装することができます。

<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Three.js Custom Shader Example</title>
    <style>
        body { margin: 0; }
        canvas { display: block; }
    </style>
</head>
<body>
    <!-- Three.jsを使用するためのスクリプトを読み込む -->
    <script src="https://cdn.jsdelivr.net/npm/three@0.153.0/build/three.min.js"></script>

    <script>
        // シーン、カメラ、レンダラーを初期化
        var scene = new THREE.Scene();
        var camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
        var renderer = new THREE.WebGLRenderer();
        renderer.setSize(window.innerWidth, window.innerHeight);
        document.body.appendChild(renderer.domElement);

        // パーリンノイズを生成するカスタムシェーダー
        var vertexShader = `
            varying vec3 vPosition;
            void main() {
                vPosition = position;
                gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
            }
        `;

        var fragmentShader = `
            uniform float uTime;
            varying vec3 vPosition;

            float random(vec3 p) {
                return fract(sin(dot(p, vec3(12.9898, 78.233, 45.543))) * 43758.5453);
            }

            float noise(vec3 p) {
                vec3 i = floor(p);
                vec3 f = fract(p);
                float n = dot(i, vec3(1.0, 57.0, 113.0));
                return mix(mix(mix(random(i + vec3(0.0, 0.0, 0.0)), random(i + vec3(1.0, 0.0, 0.0)), f.x),
                               mix(random(i + vec3(0.0, 1.0, 0.0)), random(i + vec3(1.0, 1.0, 0.0)), f.x), f.y),
                           mix(mix(random(i + vec3(0.0, 0.0, 1.0)), random(i + vec3(1.0, 0.0, 1.0)), f.x),
                               mix(random(i + vec3(0.0, 1.0, 1.0)), random(i + vec3(1.0, 1.0, 1.0)), f.x), f.y), f.z);
            }

            void main() {
                float n = noise(vPosition * 5.0 + uTime); // ノイズを計算
                vec3 color = vec3(0.2, 0.4, 0.6) * n; // ノイズに基づいた色を設定
                gl_FragColor = vec4(color, 1.0); // 最終的な色を設定
            }
        `;

        // シェーダーを作成
        var shaderMaterial = new THREE.ShaderMaterial({
            uniforms: {
                uTime: { type: 'f', value: 0.0 }
            },
            vertexShader: vertexShader,
            fragmentShader: fragmentShader
        });

        // 球体ジオメトリとマテリアルを作成
        var geometry = new THREE.SphereGeometry(1, 32, 32);
        var sphere = new THREE.Mesh(geometry, shaderMaterial);
        scene.add(sphere);

        camera.position.z = 5;

        // アニメーションループ
        function animate() {
            requestAnimationFrame(animate);

            // 時間の経過に応じてuTimeを更新
            shaderMaterial.uniforms.uTime.value += 0.01;

            // 球体を回転させる
            sphere.rotation.x += 0.01;
            sphere.rotation.y += 0.01;

            renderer.render(scene, camera);
        }

        animate();

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

特定のアルゴリズムをシェーダー内で直接実装する記述方法。

Three.jsとシェーダーを使ってパーリンノイズを使用し、表面に動的な変化を加えるシェーダーを実装。

Three.jsとカスタムシェーダーを使った実装は、以下のようなアプローチも可能です:

スクリーンショット 2024-09-16 194757.png

  1. カスタムシェーダーの使用:
    Vertex Shader と Fragment Shader を定義して、特定のビジュアルエフェクトを作成する。
    パーリンノイズの生成や動的な変化など、特定のアルゴリズムをシェーダー内で直接実装する。
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Perlin Noise Shader with Three.js</title>
    <style>
        body { margin: 0; }
        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 vec3 vPosition;
        void main() {
            vPosition = position;
            gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
        }
    </script>
    <script id="fragmentShader" type="x-shader/x-fragment">
        uniform float uTime;
        varying vec3 vPosition;

        // パーリンノイズ関数(簡易版)
        float random(vec3 p) {
            return fract(sin(dot(p, vec3(12.9898, 78.233, 45.543))) * 43758.5453);
        }

        float noise(vec3 p) {
            vec3 i = floor(p);
            vec3 f = fract(p);
            float n = dot(i, vec3(1.0, 57.0, 113.0));
            return mix(mix(mix(random(i + vec3(0.0, 0.0, 0.0)), random(i + vec3(1.0, 0.0, 0.0)), f.x),
                           mix(random(i + vec3(0.0, 1.0, 0.0)), random(i + vec3(1.0, 1.0, 0.0)), f.x), f.y),
                       mix(mix(random(i + vec3(0.0, 0.0, 1.0)), random(i + vec3(1.0, 0.0, 1.0)), f.x),
                           mix(random(i + vec3(0.0, 1.0, 1.0)), random(i + vec3(1.0, 1.0, 1.0)), f.x), f.y), f.z);
        }

        void main() {
            float n = noise(vPosition + uTime);
            vec3 color = vec3(0.2, 0.4, 0.6) * n; // ノイズを色に適用
            gl_FragColor = vec4(color, 1.0);
        }
    </script>
    <script>
        // シーン、カメラ、レンダラーの初期化
        const scene = new THREE.Scene();
        const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
        const renderer = new THREE.WebGLRenderer();
        renderer.setSize(window.innerWidth, window.innerHeight);
        document.body.appendChild(renderer.domElement);

        // 球体ジオメトリの作成
        const geometry = new THREE.SphereGeometry(1, 64, 64);

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

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

        // カメラの位置
        camera.position.z = 3;

        // アニメーションループ
        function animate(time) {
            requestAnimationFrame(animate);
            material.uniforms.uTime.value = time * 0.001; // uTimeを更新
            renderer.render(scene, camera);
        }
        animate();
    </script>
</body>
</html>

1
2
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
1
2