みかづきブログ・カスタム

基本的にはちょちょいのほいです。

Three.jsで表示したMeshにログを表示する 📈

f:id:kimizuka:20220107193822g:plain

Three.jsでVRコンテンツを作る際、console.logで出力した値を確認するのが面倒なので、かつて作成した動的にテキストを出力できるテクスチャを切り出して、ログの出力に特化させたものをつくりました。

blog.kimizuka.org

こちらの記事内に出てくるDEMOでもちゃっかり活躍してます。

blog.kimizuka.org

DEMO

https://kimizuka.org/mock/debug
※ 毎フレームDate.nowの値を出力しています

ソースコード

import * as THREE from '~/scripts/build/three.module';

export default function Debug(width = 600, height = 1200) {
    const canvas = document.createElement('canvas');
    const ctx = canvas.getContext('2d');
    const fontSize = 24;
    const padding = 12;

    canvas.width = width;
    canvas.height = height;

    const geometry = new THREE.PlaneGeometry(canvas.width / 100, canvas.height / 100);
    const texture = new THREE.CanvasTexture(canvas);
    const material = new THREE.MeshBasicMaterial({
      map: texture
    });
    const mesh = new THREE.Mesh(geometry, material);

    mesh.log = log;
    mesh.log('');

    function log(param) {
      if (!ctx) {
        return;
      }

      ctx.fillStyle = '#282828';
      ctx.fillRect(0, 0, canvas.width, canvas.height);
      ctx.fillStyle = '#f8f8f8';
      ctx.font = `${ fontSize }px sans-serif`;

      if (typeof param !== 'string') {
        param = JSON.stringify(param, null, '  ');
      }

      param.split('\n').forEach((param, i) => {
        ctx.fillText(param, padding, padding + fontSize * (i + 1));
      })

      material.map.needsUpdate = true;
    }

    return mesh;
  }
}

ざっくり書くとこんな感じで、

  1. THREE.Meshのインスタンスにlogというメソッドを生やして返す
  2. logは受け取った引数がテキストであればそのまま、オブジェクトであればJSON.stringifyしてテキストにする
  3. 受け取ったテキストをテクスチャに設定してあるcanvasのコンテクストに書き込む
  4. 書き込みが終わったらmaterial.map.needsUpdateを実行する

という流れを作りました。

使い方(抜粋)

renderer.setAnimationLoop(() => {
  debug.log(String(Date.now()));
  renderer.render(scene, camera);
});

const debug = new Debug(300, 300);

debug.position.set(0, 0, -12);
scene.add(debug);

という感じで使います。

本当はTypeScriptで開発しているのですが、強引に型を解決しているのが恥ずかしいので、ブログ用のソースはJavaScriptで書き直しました。