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の動的更新は、instanceStart
やinstanceEnd
に位置を設定し、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
おわりに
直線の終点だけ動かしています。以下のサンプルのようにもっといろいろ変化できるのかと思うのですが、そこまで理解ができませんでした。ちょっと力不足