備忘録 blog

Docker/Machine Learning/Linux

three.jsでテキストの向きをカメラに追従させる

Three.js で三次元の可視化を実装するときに、カメラ移動を実装して、様々な角度からその三次元データを見られるようにしたいと考えることがある。このときに必要なマウスドラッグやマウスホイールによるカメラ移動は、OrbitControls.js を使うと苦も無く実装できる。 このとき、Three.js で三次元空間上にテキストを配置してあったとする。その状態で視点を変えると、デフォルトの設定ではテキストの向きが配置時の向きのまま固定されてしまっているので、テキストに対して垂直方向に視点を変えると、そのテキストが読めなくなってしまう。 そのため、カメラの位置に追従し、テキストが常にユーザー側に向いている実装に変更したいと考えた。このとき、試したことや調べたことをまとめる。

うまくいった方法

↑のような実装をしたい時の検索ワードは、face camera, face user, look at camera などのキーワードを用いると良いというのが、調べながら分かってきた。

text.quaternion.copy( camera.quaternion );

オブジェクトの向きは、three.jsなどの各種3Dツールでは、クォータニオンによって定義されている。この↑のコードをオブジェクトの定義時ではなくて、render 時に書くことで、カメラの回転角と、回転させたいオブジェクトの回転角を同期させることができるようになった。

試してうまくいかなかったこと

検索して最初に出てくる方法は、lookAtを使う方法である。これは、 OrbitControls.js を使ったときは利用できないので、うまくいかない。

text.lookAt( camera );

また奥の手として、テキストを画像として貼り付けてしまえば、カメラの向きに画像の向きが固定されるということを利用して、いったんテキストをSVGに吐いてから*1、それをSpriteとして貼り付けるということも考えたが、これもうまく動かなかった。

$ gem install text2svg
    var map = THREE.ImageUtils.loadTexture( "fonts/text.svg" );
    var material = new THREE.SpriteMaterial( { map: map, color: 0xffffff, fog: true } );
    var titleX = new THREE.Sprite( material );
    titleX.position.x = xScale(vpts.xMin) - 6,
    titleX.position.y = 2;
    scatterPlot.add( titleX );

    var geometry2 = new THREE.Geometry();
    var vertex2 = new THREE.Vector3(xScale(vpts.xMin) - 6, 2, 0);

    geometry2.vertices.push( vertex2 );

    var material2 = new THREE.PointCloudMaterial( { size: 100, sizeAttenuation: false, map: map, alphaTest: 0.5, transparent: true } );

    var titleX = new THREE.PointCloud( geometry2, material2 );

    scatterPlot.add( titleX );

いまのtextはTHREE.Textureで作っていたが、THREE.Spriteで作るようにすると、THREE.Spriteはカメラに対して常に正面を向くオブジェクトを生成することができるので、こちらの方が良かったかもしれない。

ics.media

参考文献

stackoverflow.com

stackoverflow.com

stackoverflow.com

*1:この方法だと、静的なテキストしか配置できないことになってしまうという欠点があるが。