どうもです、タドスケです。
【Three.js】Canvas 上で 3D行列計算結果を確認するサンプル | しぬまでワクワクしていたい
どうもです、タドスケです。 最近、仕事で行列計算のコードに触る機会が出てきました。 しかし僕は日頃から公言しているとおり、物理数学が大の苦手なゲームプログラマーで…
こちらの記事で、Three.js を使ってブログ記事上で軽量な 3D コンテンツを扱えることを知りました。
前回は行列計算をやったので、今回はベクトルを使ってみました。
2つのベクトルの内積の値がアニメーション更新されます。
コードは以下です。(ChatGPT を使用しています)
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>ベクトルの回転、内積、なす角と射影の表示</title>
<style>
body { margin: 0; }
#info {
position: absolute;
top: 0;
left: 0;
color: white;
font-family: sans-serif;
padding: 10px;
background-color: rgba(0, 0, 0, 0.5);
}
</style>
</head>
<body>
<div id="info">
<p>内積: 0</p>
<p>なす角: 0°</p>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
<script>
// グローバル変数の設定
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
const renderer = new THREE.WebGLRenderer();
const vectorA = new THREE.Vector3(1, 0, 0);
let vectorB = new THREE.Vector3();
const arrowHelpers = {};
const projectionGeometry = new THREE.BufferGeometry();
const infoElement = document.getElementById('info');
const dotProductText = infoElement.children[0];
const angleText = infoElement.children[1];
// 初期設定を行う関数
function setup() {
camera.position.set(0, 0.3, 2);
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
// 矢印ヘルパーの設定
arrowHelpers.A = new THREE.ArrowHelper(vectorA.normalize(), new THREE.Vector3(), vectorA.length(), 0xff0000);
arrowHelpers.B = new THREE.ArrowHelper(new THREE.Vector3(), new THREE.Vector3(), 1, 0x00ff00);
scene.add(arrowHelpers.A);
scene.add(arrowHelpers.B);
// 射影線の設定
projectionGeometry.setAttribute('position', new THREE.BufferAttribute(new Float32Array(6), 3));
const projectionLineMaterial = new THREE.LineBasicMaterial({ color: 0xffff00 });
const projectionLine = new THREE.Line(projectionGeometry, projectionLineMaterial);
scene.add(projectionLine);
}
// ベクトルBの回転と表示を更新する関数
function updateVectorB() {
const time = Date.now() * 0.002;
vectorB.set(Math.cos(time), Math.sin(time), 0);
arrowHelpers.B.setDirection(vectorB.normalize());
arrowHelpers.B.setLength(vectorB.length());
}
// 内積となす角、射影線の計算と表示を更新する関数
function updateDotProductAndAngle() {
const dotProduct = vectorA.dot(vectorB);
const angleRadians = Math.acos(dotProduct / (vectorA.length() * vectorB.length()));
const angleDegrees = angleRadians * (180 / Math.PI);
dotProductText.textContent = `内積: ${dotProduct.toFixed(2)}`;
angleText.textContent = `なす角: ${angleDegrees.toFixed(2)}°`;
// 射影線の更新
const projection = vectorA.clone().multiplyScalar(dotProduct);
projectionGeometry.attributes.position.setXYZ(0, 0, 0, 0);
projectionGeometry.attributes.position.setXYZ(1, projection.x, projection.y, projection.z);
projectionGeometry.attributes.position.needsUpdate = true;
}
// アニメーションループ
function animate() {
requestAnimationFrame(animate);
updateVectorB();
updateDotProductAndAngle();
renderer.render(scene, camera);
}
// 初期設定の実行とアニメーションの開始
setup();
animate();
</script>
</body>
</html>
ベクトルの内積って、数学の授業では「こう計算するんだよ」としか教わらなかったので、「それが何の役にたつの?」という感想がありました。
ベクトルを単なる数字や式ではなく、このようにイメージでとらえるようにすると、理解が深まる気がしました。
以上!ベクトルの内積の検証でした。
コメント
コメント一覧 (1件)
[…] […]