0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

three.js Line2 を動的に更新する方法(直線の終点だけ)

Posted at

image.png

three.jsで太い線を描画したいのですが、THREE.MeshLineはメンテされていないらしい。three.js exampleのlines_fatで使われているLine2で太い線が描画できるようです。直線だと動的に更新もできるそうなので、簡単に試してみました。

参考

コード

index.html
<!DOCTYPE html>
<html lang="en">

<head>
  <title>three.js: How to dynamically update Line2</title>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <style>
    body {
      margin: 0;
      overflow: hidden;
    }
  </style>
</head>

<body>
  <script type="importmap">
    {
      "imports": {
        "three": "https://unpkg.com/three@0.164.1/build/three.module.js",
        "three/addons/misc/Timer": "https://unpkg.com/three@0.164.1/examples/jsm/misc/Timer.js",
        "three/addons/controls/OrbitControls": "https://unpkg.com/three@0.164.1/examples/jsm/controls/OrbitControls.js",
        "three/addons/lines/Line2":  "https://unpkg.com/three@0.164.1/examples/jsm/lines/Line2.js",
        "three/addons/lines/LineMaterial":  "https://unpkg.com/three@0.164.1/examples/jsm/lines/LineMaterial.js",
        "three/addons/lines/LineGeometry":  "https://unpkg.com/three@0.164.1/examples/jsm/lines/LineGeometry.js"
      }
    }
  </script>
  <script type="module">
    import * as THREE from 'three';
    import { Timer } from 'three/addons/misc/Timer';
    import { OrbitControls } from 'three/addons/controls/OrbitControls';
    import { Line2 } from 'three/addons/lines/Line2';
    import { LineMaterial } from 'three/addons/lines/LineMaterial';
    import { LineGeometry } from 'three/addons/lines/LineGeometry';

    const renderer = new THREE.WebGLRenderer();
    document.body.appendChild(renderer.domElement);
    const scene = new THREE.Scene();
    // グリッド表示
    scene.add(new THREE.GridHelper(60, 6));
    // カメラ
    const camera = new THREE.PerspectiveCamera(
      40,
      window.innerWidth / window.innerHeight,
      1,
      1000
    );
    camera.position.set(0, 200, 0);
    const controls = new OrbitControls(camera, renderer.domElement);
    controls.enableDamping = true;
    controls.minDistance = 10;
    controls.maxDistance = 500;

    // Line2: https://threejs.org/examples/#webgl_lines_fat
    // How to update Line2 dynamically?: https://discourse.threejs.org/t/how-to-update-line2-dynamically/37913
    const line = new class {
      constructor() {
        this.geometry = new LineGeometry();
        this.geometry.setPositions([0, 30, -30, 0, 0, 0]);
        this.material = new LineMaterial({
          color: 0xff0000,
          linewidth: 20,
        });
        scene.add(new Line2(this.geometry, this.material));
      }
      // Line2を動的に更新
      update(date) {
        // 現在の秒数からs終点を変更
        const theta = (date.getSeconds() + date.getMilliseconds() / 1000) / 30 * Math.PI;
        this.geometry.attributes.instanceEnd.setX(0, Math.sin(theta) * 30);
        this.geometry.attributes.instanceEnd.setZ(0, Math.cos(theta) * -30);
        this.geometry.attributes.instanceEnd.needsUpdate = true
      }
      // ラインの解像度: ラインの幅の基準になる
      setResolution(width, height) {
        this.material.resolution.set(width, height);
      }
    }();

    const timer = new Timer();
    const ms = Date.now();
    function animate(timestamp) {
      requestAnimationFrame(animate);
      timer.update(timestamp);

      line.update(new Date(ms + timer.getElapsed() * 1000));

      controls.update();
      renderer.render(scene, camera);
    }
    animate();

    function onWindowResize() {
      const width = window.innerWidth;
      const height = window.innerHeight;
      renderer.setPixelRatio(window.devicePixelRatio);
      renderer.setSize(width, height);

      line.setResolution(width, height);

      camera.aspect = width / height;
      camera.updateProjectionMatrix();
    }
    onWindowResize();
    window.addEventListener('resize', onWindowResize);
  </script>
</body>

</html>

Line2

Line2の部分は以下のところです。setPositionsで2点のxyzを指定しています。
線の幅は、20としました。この20は、materialのresolutionで指定する幅や高さから計算されますのでmaterial.resolution.setも設定が必要でした。line_fatのサンプルでは、animateの中でresolutionを設定していましたが、このコードでは、resizeの時に設定しています。

Line2の設定部分
        this.geometry = new LineGeometry();
        this.geometry.setPositions([0, 30, -30, 0, 0, 0]);
        this.material = new LineMaterial({
          color: 0xff0000,
          linewidth: 20,
        });
        scene.add(new Line2(this.geometry, this.material));
Line2の解像度設定
        this.material.resolution.set(width, height);

Line2の動的更新は、instanceStartinstanceEndに位置を設定し、needsUpdate を呼ぶことでできました。
このコードでは、XZ面に終点が円を描くように動きます。

Line2の動的更新部分
        const theta = (date.getSeconds() + date.getMilliseconds() / 1000) / 30 * Math.PI;
        this.geometry.attributes.instanceEnd.setX(0, Math.sin(theta) * 30);
        this.geometry.attributes.instanceEnd.setZ(0, Math.cos(theta) * -30);
        this.geometry.attributes.instanceEnd.needsUpdate = true

おわりに

直線の終点だけ動かしています。以下のサンプルのようにもっといろいろ変化できるのかと思うのですが、そこまで理解ができませんでした。ちょっと力不足

0
0
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
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?