どうも!かけちまるです!
Three.jsでマウスの動きに合わせてライトを追従させる方法を解説します。
WebGL座標は、window座標とx
,y
軸の原点が違うので座標変換する必要があるのです。
この記事では、
がわかります。
次のコードを元に追従するライトを加えていきます。
ディレクトリ構成index.html css/ -style.css js/ -main.js
index.html<!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <link rel="stylesheet" type="text/css" href="./css/style.css"> <script async src="https://unpkg.com/es-module-shims@1.5.8/dist/es-module-shims.js"></script> <script type="importmap"> { "imports": { "three": "https://unpkg.com/three@0.142.0/build/three.module.js" } } </script> <script src="./js/main.js" type="module"></script> </head> <body></body> </html>
style.css* { margin: 0; padding: 0; box-sizing: border-box; }
main.jsimport * as THREE from 'three'; /** * シーン・カメラ・レンダラーを用意する */ const fov = 50; const fovRad = (fov / 2) * (Math.PI / 180); // 視野角をラジアンに変換 let distance = (window.innerHeight / 2) / Math.tan(fovRad); // カメラ距離を求める const scene = new THREE.Scene(); const camera = new THREE.PerspectiveCamera( fov, window.innerWidth / window.innerHeight, 0.1, 1000 ); camera.position.z = distance; const renderer = new THREE.WebGLRenderer(); renderer.setSize(window.innerWidth, window.innerHeight); renderer.setPixelRatio(window.devicePixelRatio); document.body.appendChild(renderer.domElement); /** * ジオメトリ+マテリアル=メッシュを用意する */ const torusKnotGeometry = new THREE.TorusKnotGeometry( 100, 30, 100, 16 ); const lambertMaterial = new THREE.MeshLambertMaterial({ color: 0x3da8e6, }); const torusKnotMesh = new THREE.Mesh(torusKnotGeometry, lambertMaterial); scene.add(torusKnotMesh); /** * ライトを追加する */ let pointLight = new THREE.PointLight(0xffffff, 1, 0, 1.0); pointLight.position.set(0, 0, 200); scene.add(pointLight); // アニメーション const clock = new THREE.Clock(); function animate() { renderer.render(scene, camera); let getElapsedTime = clock.getElapsedTime(); torusKnotMesh.position.y += Math.sin(getElapsedTime * 1.5); requestAnimationFrame(animate); } animate(); // ブラウザのリサイズに対応させる window.addEventListener('resize', onWindowResize); function onWindowResize() { renderer.setSize(window.innerWidth, window.innerHeight); camera.aspect = window.innerWidth / window.innerHeight; distance = (window.innerHeight / 2) / Math.tan(fovRad); camera.position.z = distance; camera.updateProjectionMatrix(); }
ここまで次のようになっていればオッケーです。
ここまで何をやっているかよくわからない人はこちらの記事が参考になると思います。
5〜7行目はwindowサイズとカメラの画角を合わせメッシュ位置の指定などをpx
で指定できるようにカメラの距離を求めています。
詳細は次の記事で解説しています。
マウスにライトを追従させるにはmousemove
イベントを使います。
ですが、その前にWebGLとwindow座標を合わせる必要があります。
この記事では、window座標をWebGL座標に合わせる方向で考えます。
Three.jsを何度か触ったことがある人なら知っていることだと思いますが、
次の画像のようにWebGL座標では中央を原点としてx
,y
が決まります。
一方、window座標では左上を原点としてx
,y
が決まりますね。
なので、window座標の原点を中心に持ってくる必要があるのです。
window座標の原点を中心に持ってくるには、
で求めることができます。
具体的な数値に当てはめると次の画像のようになります。
マウスを追従するには12行目以降を追加します。
window座標をWebGL座標に変換します。(18、19行目)
変換した座標の値をライトの位置として指定します。(22、23行目)
マウスが動くたびにonMouseMove
関数を実行します。(13行目)
main.js・・・ // ブラウザのリサイズに対応させる window.addEventListener('resize', onWindowResize); function onWindowResize() { renderer.setSize(window.innerWidth, window.innerHeight); camera.aspect = window.innerWidth / window.innerHeight; distance = (window.innerHeight / 2) / Math.tan(fovRad); camera.position.z = distance; camera.updateProjectionMatrix(); } // ライトをマウスに追従させる window.addEventListener('mousemove', (event) => { onMouseMove(event.clientX, event.clientY); }); function onMouseMove(x, y) { const mouseX = x - (window.innerWidth / 2); // 原点を中心に持ってくる const mouseY = -y + (window.innerHeight / 2); // 軸を反転して原点を中心に持ってくる // ライトのxy座標をマウス位置にする pointLight.position.x = mouseX; pointLight.position.y = mouseY; }
こんな感じになったらOKです。
マウスホバーでのアニメーションは使える場面が多いと思いますのでマスターしておくと応用が効くのではないかと思います。
おわり
フィードバックを送信
記事についてのフィードバックはTwitterかお問い合わせフォームから受け付けております。