ホームページ > ウェブフロントエンド > jsチュートリアル > Three.js の最適化: テクニック

Three.js の最適化: テクニック

Linda Hamilton
リリース: 2024-12-03 00:32:12
オリジナル
570 人が閲覧しました

コードは芸術になり得ます。巧妙な構文、エレガントなデータ構造、洗練されたインタラクションのいずれであっても、プログラマーだけが認識できる美しさがあります。それはそれで問題ありません。

しかし、コードは視覚的に素晴らしいもの、誰もが認めるものを作成することもできます。ここで、Three.js のようなツールが威力を発揮します。ただし、Three.js は、特にさまざまな計算能力を持つデバイスからアクセスされる動的な Web ページで使用する場合、重くなる可能性があります。

あなたが私と同じように、サイトに複数の Three.js シーンを追加する場合 (didof.dev で行うように)、最適化が必要になります。ここでは、パフォーマンスを維持するための 3 つの実践的なテクニックを紹介します。

元々は私のブログに投稿されました。

必要な場合にのみシーンをロードする

シーンが表示されていない場合は、シーンをロードしないでください。これは、重いグラフィック コンポーネントに当てはまります。これに最適なツールは IntersectionObserver で、要素がビューポートに入ったことを検出します。 SvelteKit でそれを処理する方法は次のとおりです:

<script lang="ts">
    import { browser } from '$app/environment';
    import { onMount } from 'svelte';

    let ref: HTMLDivElement;
    let download = $state(false);
    if (browser)
        onMount(() => {
            const observer = new IntersectionObserver(([entry]) => {
                if (entry.isIntersecting) {
                    download = true;

                    // we need this once only
                    observer.disconnect();
                }
            });
            // ref has been bound by Svelte since we are in onMount
            observer.observe(ref);

            return () => observer.disconnect();
        });
</script>

<div bind:this={ref}>
    {#if download}
        <!-- let SvelteKit handle the code splitting -->
        {#await import('./three-scene.svelte')}
            Loading
        {:then module}
            <module.default />
        {:catch error}
            <div>{error}</div>
        {/await}
    {/if}
</div>
ログイン後にコピー
ログイン後にコピー

視界外のシーンを一時停止する

シーンが表示されない場合は、レンダリングを停止します。ほとんどのチュートリアルは 1 つの全画面シーンに焦点を当てていますが、複数のシーンがあるサイトの場合は、非表示のシーンを一時停止するとリソースが節約されます。

これは、IntersectionObserver を使用してシーンのアニメーション ループを制御するスニペットです:

function tick() {
    const elapsedTime = clock.getElapsedTime();

    // Update your scene (e.g. set uniforms, move/rotate geometries...)

    renderer.render(scene, camera);
}

// Start the rendering
renderer.setAnimationLoop(tick);
ログイン後にコピー
ログイン後にコピー

またしても、私たちの友人である IntersectionObserver が私たちを助けてくれます。

let clock: THREE.Clock;
let renderer: THREE.WebGLRenderer;

if (browser)
    onMount(() => {
        const observer = new IntersectionObserver(([entry]) => {
            if (entry.isIntersecting) {
                clock.start();
                renderer.setAnimationLoop(tick); // resume
            } else {
                clock.stop();
                renderer.setAnimationLoop(null); // pause
            }
        });
        observer.observe(canvas);

        // Scene setup...

        return () => {
            observer.disconnect();

            // Other cleanup...
        };
    });
ログイン後にコピー

ビューポート サイズに合わせてシェーダー ワークロードを調整する

画面が小さいデバイスは、多くの場合、性能が低くなります。シェーダーの計算ワークロードをそれに応じて調整します。たとえば、ビューポートの幅に基づいて、フラクタル シェーダで使用されるオクターブ数を減らします。

ブラウザから...

<script lang="ts">
    import ThreeScene from "./three-scene.svelte";
    import { browser } from '$app/environment';

    const octaves = browser ? (window.innerWidth <= 680 ? 2 : 4) : 1
</script>

<ThreeScene {octaves} />
ログイン後にコピー

...three.js を通じて...

const material = new THREE.ShaderMaterial({
    vertexShader,
    fragmentShader,
    uniforms: {
        uOctaves: new Three.Uniform(octaves) // coming as $prop
    }
});
ログイン後にコピー

...最後にシェーダー内です。

uniform float uOctaves;

for(float i = 0.0; i <= uOctaves; i++)
{
    elevation += simplexNoise2d(warpedPosition * uPositionFrequency * pow(2.0, i)) /  pow(2.0, i + 1.0);
}
ログイン後にコピー

このアプローチは、パフォーマンスとビジュアル品質のバランスを動的にとります。

ブラウザにクリーンアップを任せる

ここが難しいところです。 Three.js はメモリを自動的にクリーンアップしません。ジオメトリ、テクスチャ、マテリアルなどのオブジェクトを手動で追跡して破棄する必要があります。これをスキップすると、移動したり戻ったりするたびにメモリ使用量が増加し、最終的にはブラウザがクラッシュします。

私のホームページで観察したことを共有させてください:

初期メモリ使用量: 22.4MB

Optimizing Three.js: ey Techniques

別のページへのソフト ナビゲーション後: 28.6MB (そのページは静的 HTML であっても)。

Optimizing Three.js: ey Techniques

前後のナビゲーションを繰り返した後: ブラウザがクラッシュするまでメモリ使用量が増加し続けました。

Optimizing Three.js: ey Techniques

なぜですか? Three.js オブジェクトが適切に破棄されていなかったためです。そして、広範な調査にもかかわらず、最新のフレームワークでメモリを完全にクリーンアップする信頼できる方法を見つけることができませんでした。

これが私が見つけた最も簡単な解決策です: Three.js シーンのあるページを離れるときにハードリロードを強制します。ハードリロードにより、ブラウザは次のことが可能になります。

  1. 新しいページコンテキストを作成します。
  2. 古いページでガベージ コレクションを実行します (クリーンアップはブラウザーに任せます)。

SvelteKit では、data-sveltekit-reload を使用するとこれが簡単です。シーンのあるページに対して有効にするだけです:

ホームページのserver.page.ts

<script lang="ts">
    import { browser } from '$app/environment';
    import { onMount } from 'svelte';

    let ref: HTMLDivElement;
    let download = $state(false);
    if (browser)
        onMount(() => {
            const observer = new IntersectionObserver(([entry]) => {
                if (entry.isIntersecting) {
                    download = true;

                    // we need this once only
                    observer.disconnect();
                }
            });
            // ref has been bound by Svelte since we are in onMount
            observer.observe(ref);

            return () => observer.disconnect();
        });
</script>

<div bind:this={ref}>
    {#if download}
        <!-- let SvelteKit handle the code splitting -->
        {#await import('./three-scene.svelte')}
            Loading
        {:then module}
            <module.default />
        {:catch error}
            <div>{error}</div>
        {/await}
    {/if}
</div>
ログイン後にコピー
ログイン後にコピー

ナビゲーション リンクの場合、この値を動的に渡します:

function tick() {
    const elapsedTime = clock.getElapsedTime();

    // Update your scene (e.g. set uniforms, move/rotate geometries...)

    renderer.render(scene, camera);
}

// Start the rendering
renderer.setAnimationLoop(tick);
ログイン後にコピー
ログイン後にコピー

一般的な を使用する場合は、コンポーネントの場合、これを実装する必要があるのは 1 回だけです。

このアプローチは完璧ではありません。特定のページに対するスムーズなクライアント側ルーティングが無効になります。しかし、メモリを抑制し、クラッシュを防ぎます。私にとって、そのトレードオフにはそれだけの価値があります。


最終的な考え

これらの最適化は私にとってはうまくいきましたが、疑問は残ります。最新のフレームワークで Three.js オブジェクトを適切にクリーンアップするにはどうすればよいでしょうか?信頼できる解決策を見つけた場合は、ぜひご連絡ください!

以上がThree.js の最適化: テクニックの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

ソース:dev.to
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
著者別の最新記事
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート