Vuejs上でThree.jsを使って3Dモデルの表示をする必要があったので、その際の実装方法を紹介します。
目標
今回は以下のようなものを作ります。
具体的な話をすると以下のようなことをしています。
- Threejsで空間とCubeを描画
- Cubeはクォータニオンのデータを使って回転
- 空間はマウスのクリックとドラックで視点移動(トラックボールコントロール)
その部分は各自実装してください。
インストール
まずは、Threejs系のモジュールのインストールが必要です。
ここでは、Threejs本体とトラックボールコントロールのモジュールをインストールします。
(トラックボールコントロールについてはThreejsのモジュール内のexampleに入っていて利用する事もできるのですが、ちょっと面倒なのですでにあるモジュールを利用します)
npm install --save vue-threejs
npm install --save three-trackballcontrols
実装
とりあえずソースコードです。
<template>
<div id="cube" ref="stage"></div>
</template>
<script>
import VueThreejs from 'vue-threejs'
import * as THREE from 'three'
import TrackballControls from 'three-trackballcontrols'
export default {
name: 'Cube',
props: {
qua: Array
},
components: {
VueThreejs,
TrackballControls
},
data () {
// === scene ===
const scene = new THREE.Scene()
// === renderer ===
const renderer = new THREE.WebGLRenderer()
// === camera ===
const camera = new THREE.PerspectiveCamera(
30,
1,
0.1,
100
)
camera.position.z = 5
// === light ===
const light = new THREE.HemisphereLight(0xFFFFFF, 0xFFFFFF, 2.0, (0, 1, 0))
// === Grid ===
const grid = new THREE.GridHelper(6, 10, 0x888888, 0x888888)
grid.position.y = -0.6
// === model ===
const geometry = new THREE.BoxGeometry(1, 0.5, 1.25)
const material = [
new THREE.MeshStandardMaterial({ color: 0xFFFFFF }),
new THREE.MeshStandardMaterial({ color: 0xF4D06F }),
new THREE.MeshStandardMaterial({ color: 0xFF8811 }),
new THREE.MeshStandardMaterial({ color: 0x9DD9D2 }),
new THREE.MeshStandardMaterial({ color: 0xFFA8F0 }),
new THREE.MeshStandardMaterial({ color: 0x392F5A })
]
const cube = new THREE.Mesh(geometry, material)
return {
scene: scene,
renderer: renderer,
camera: camera,
light: light,
cube: cube,
grid: grid,
trackball: null
}
},
created () {
// === sceneにmodel,light, cameraを追加 ===
this.scene.add(this.camera)
this.scene.add(this.light)
this.scene.add(this.cube)
this.scene.add(this.grid)
},
mounted () {
this.$refs.stage.appendChild(this.renderer.domElement)
var stage = document.getElementById('cube')
this.renderer.setSize(stage.offsetWidth / 1.25, 500)
this.camera.aspect = (stage.offsetWidth / 1.25) / 500
this.camera.updateProjectionMatrix()
// === TrackBall ===
this.trackball = new TrackballControls(this.camera, this.renderer.domElement)
this.trackball.noZoom = true
this.trackball.noPan = true
this.trackball.rotateSpeed = 1.0
this.animate()
},
methods: {
animate () {
requestAnimationFrame(this.animate)
this.quaternion()
this.renderer.render(this.scene, this.camera)
this.trackball.update()
},
quaternion () {
if (typeof this.qua[0] === 'undefined') {
return
}
var q = new THREE.Quaternion(this.qua[2], this.qua[3], this.qua[1], this.qua[0])
this.cube.quaternion.copy(q)
}
},
watch: {
'qua': function () {
this.quaternion()
this.renderer.render(this.scene, this.camera)
}
}
}
</script>
データ(クォータニオン)を与える部分を変更すれば親コンポーネントとしても利用できます
解説
かなり部分的で大雑把な解説をします。
具体的にメソッドの引数の内容等が知りたい方は、以下の公式サイトを参照してください。
参考 Threejsthreejs.org
Dataセクション
基本的には変数をまとめて宣言するのですが、3Dモデルや画面、カメラなどの初期設定をしています。
やってることは、コンストで宣言して変数に設定した項目を格納して他のメソッド内から使えるようにしている感じです。
data () {
// === scene ===
const scene = new THREE.Scene()
// === renderer ===
const renderer = new THREE.WebGLRenderer()
// === camera ===
const camera = new THREE.PerspectiveCamera(
30,
1,
0.1,
100
)
camera.position.z = 5
// === light ===
const light = new THREE.HemisphereLight(0xFFFFFF, 0xFFFFFF, 2.0, (0, 1, 0))
// === Grid ===
const grid = new THREE.GridHelper(6, 10, 0x888888, 0x888888)
grid.position.y = -0.6
// === model ===
const geometry = new THREE.BoxGeometry(1, 0.5, 1.25)
const material = [
new THREE.MeshStandardMaterial({ color: 0xFFFFFF }),
new THREE.MeshStandardMaterial({ color: 0xF4D06F }),
new THREE.MeshStandardMaterial({ color: 0xFF8811 }),
new THREE.MeshStandardMaterial({ color: 0x9DD9D2 }),
new THREE.MeshStandardMaterial({ color: 0xFFA8F0 }),
new THREE.MeshStandardMaterial({ color: 0x392F5A })
]
const cube = new THREE.Mesh(geometry, material)
return {
scene: scene,
renderer: renderer,
camera: camera,
light: light,
cube: cube,
grid: grid,
trackball: null
}
},
Createセクション
このセクションはインスタンスが作成された瞬間に発火します。
Dataセクションで定義されたsceneにカメラなどの要素を追加していきます。
created () {
// === sceneにmodel,light, cameraを追加 ===
this.scene.add(this.camera)
this.scene.add(this.light)
this.scene.add(this.cube)
this.scene.add(this.grid)
},
Mountedセクション
このセクションはインスタンスがマウントされたら発火します。
DOM要素にこれ以降アクセス可能となるので、ここで画面サイズを取得してブラウザの大きさにThreejsのレンダリングサイズそれに対応したカメラサイズを再定義しています。
またここでトラックボールコントロールについて定義しています。
mounted () {
this.$refs.stage.appendChild(this.renderer.domElement)
var stage = document.getElementById('cube')
this.renderer.setSize(stage.offsetWidth / 1.25, 500)
this.camera.aspect = (stage.offsetWidth / 1.25) / 500
this.camera.updateProjectionMatrix()
// === TrackBall ===
this.trackball = new TrackballControls(this.camera, this.renderer.domElement)
this.trackball.noZoom = true
this.trackball.noPan = true
this.trackball.rotateSpeed = 1.0
this.animate()
},
Methodsセクション
animateでクォータニオンを利用してモデルの回転を描画します。
quaternionでは、クォータニオンの持つ4つの変数を設定します(ここでは順番がバラバラですが、適宜変更してください)
methods: {
animate () {
requestAnimationFrame(this.animate)
this.quaternion()
this.renderer.render(this.scene, this.camera)
this.trackball.update()
},
quaternion () {
if (typeof this.qua[0] === 'undefined') {
return
}
var q = new THREE.Quaternion(this.qua[2], this.qua[3], this.qua[1], this.qua[0])
this.cube.quaternion.copy(q)
}
},
Watchセクション
Threejsには直接関係ないのですが、この仕様だと親コンポーネントからpropsでデータを取得しているので、このWatchでデータ変更を監視しています。
またデータの更新を確認後メソッドを呼び出すことで再描画しています。
watch: {
'qua': function () {
this.quaternion()
this.renderer.render(this.scene, this.camera)
}
}
まとめ
以前はVuejsの中でThreejsを使うのにちょっと面倒くささを感じていたのですが、優秀なモジュールも多数あるので、意外とさらっとかけました。
React等を含めSPAがかなり書きやすいのでとてもうれしく思います。
コメントを残す