見出し画像

Vue 3 と Three.js で箱を表示

Vue3 + Three.js|NAYUTA株式会社 シリーズ記事の第1弾です。
https://github.com/NAYUTA-tech/tutorial-three-vue にソースコードも公開しています。

今回作るもの

Vue.js 3 (3.2.36) と Three.js (0.141.0 ) で、Three.jsのGetting Startedの最初のページと同じものを作ります!

Three.jsのチュートリアルでおなじみのこいつを、Vue上で動かします

VueとThree.jsの組み合わせは、ググっても情報が古くて動かなかったり、妙に遅かったりしましたが、少なくとも2022年6月現在は、この記事の通りで動くはずです!

前提

  • Windows

  • WSL2 + Ubuntu

  • Node.js

    • WSL内にインストールしてない場合や、古いバージョンを使っている場合は、ここ 見て18.xあたりをインストールしましょう。たった2行のコマンドでインストールできます!

インストール

まずはVueから。
ここはほぼ Quick Start | Vue.js のままです。

npm init vue@latest

実行すると以下のようにいろいろ質問されます。

ここではプロジェクト名を"hello-three-vue"とした以外は、全てデフォルトのままにしています。

あとは言われるがままにコマンドを実行して…

cd hello-three-vue
npm install
npm run dev

表示されたURLを開くと…

You did it!

ここまでで Vue のインストールは完了です。

ターミナルではViteというツールの開発用サーバーが動いていると思いますが、一旦Ctrl+Cで終了させてしまい、"You did it!"と表示されているタブも閉じてしまいましょう。


続いてThree.jsをインストールします。
ここもインストール自体は Installation – three.js docs に書いてあるままです。

npm install three

今回は最低限なのでこれだけです。

以上でインストールは完了です!

箱を表示

さて、ここからが本番です。

App.vue を下記のように書き換えてください。

<script>
import * as THREE from 'three'

export default {
  data() {
    /**
     * non-reactiveなデータ
     * * https://stackoverflow.com/a/54907413
     */
    this.scene = null
    this.camera = null
    this.renderer = null
    this.cube = null

    /**
     * reactiveなデータ
     * * ここでThree.jsのデータを定義すると、重くなるか動かなくなるので注意
     *   * https://stackoverflow.com/a/65732553
     */
    return {}
  },

  mounted() {
    this.init()
    this.addBox()
    this.animate()
  },

  methods: {
    init() {
      this.scene = new THREE.Scene()
      this.camera = new THREE.PerspectiveCamera(
        75, // FOV
        window.innerWidth / window.innerHeight, // aspect ratio
        0.1, // near
        1000 // far
      )
      this.renderer = new THREE.WebGLRenderer({ canvas: this.$refs.canvasRef })

      // レンダリング解像度
      this.renderer.setSize(
        window.innerWidth,
        window.innerHeight,
        false
      )

      this.camera.position.z = 5
    },

    addBox() {
      const geometry = new THREE.BoxGeometry()
      const material = new THREE.MeshBasicMaterial({ color: 0x00ff00 })
      this.cube = new THREE.Mesh(geometry, material)
      this.scene.add(this.cube)
    },

    animate() {
      requestAnimationFrame(this.animate)

      this.cube.rotation.x += 0.01
      this.cube.rotation.y += 0.01

      this.renderer.render(this.scene, this.camera)
    }
  }
}
</script>

<template>
  <canvas ref="canvasRef" class="fullscreen"></canvas>
</template>

<style scoped>
.fullscreen {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
}
</style>

後は再度開発用サーバーを下記のコマンドで起動して、

npm run dev

表示されたURLをブラウザで開いたら、、、

冒頭の画像と同じものが表示されましたでしょうか?

完成です!

注意点

以上で完成ですが、いくつか注意点がありますので、ここで解説します。

data(){} の書き方

Vue 3 で Options API を使う場合、公式ドキュメントの記載にしたがうと、下記のような書き方になってしまいます。

data() {
    return {
        this.scene = null
        this.camera = null
        this.renderer = null
        this.cube = null
    }
},

しかし、このreturn{}内で定義された変数は、Vue3では自動で便利機能モリモリのReactive Stateとして定義されてしまい、Three.jsとは相性が悪いです。
Three.js関連の変数は、ソースコード内のコメントの通り、return{}の外で定義しましょう。

エラー: "THREE.WebGLRenderer: A WebGL context could not be created"

たまに、Chromeのデベロッパーコンソールに上記のようなエラーが出てきます。これはあなたのせいでもThree.jsのせいでもないことが多いです。
OSやドライバーの問題である可能性がほとんどで、"WebGL context"と出てきたらPC再起動等を試してみましょう。
詳細は以下を見てください。
https://github.com/mrdoob/three.js/issues/4927