LoginSignup
3
2

Three.jsをVue3のコンポーネントに〜Blenderで作成したオブジェクトを読み込む〜

Last updated at Posted at 2024-03-24

こんにちは|こんばんは。カエルのアイコンで活動しております @kyamaz :frog: です。

はじめに

本稿は、@0taUtanさん、@enumura1さんや@nannany_storesさんの次の寄稿を参考にThree.jsをVue3のコンポーネントにして、Blenderで作成したオブジェクトを読み込む手順を取り上げます。

Blenderでモデルを作成する

3Dモデルのサンプルとして、@PlusFさんの『BlenderでカーボンナノチューブのCGをつくる』を参考にカーボンナノチューブ(CNT)のモデルを作成します。

Blenderの画面

BlenderのメニューからFile → Export → glTF 2.0 (.glb/gltf)を選択して、任意の場所に glTFファイル へエクスポートします。

Blenderエクスポート

ファイル名を"CNT.glb"とします。

Vue 3 + TypeScript + Vite の環境を整える

拙稿『p5.jsをVue3のコンポーネントにする』と同じ手順で環境を整えます。そしてHome.vueを、次のようにコンポーネントHelloWorldを使うように変更します。

Home.vue
<script lang="ts">
import HelloWorld from './components/HelloWorld.vue'
export default {
  components: {
    HelloWorld
  }
}
</script>

<template>
  <div>
    <HelloWorld />
  </div>
</template>

<style scoped>
</style>

Three.jsをコンポーネントとして活用する

Three.jsのモジュールを使用できるようにインストールします。同時にTypeScriptで型チェックができるように、開発のためのモジュール@types/threeもインストールします。

% npm install three
% npm install -D @types/three

Three.jsをコンポーネントにするには次のように書きます。

HelloWorld.vueのソースコード全体
HelloWorld.vue
<script lang="ts">
import { defineComponent, onMounted, ref } from "vue";
import {
  Color,
  GridHelper,
  PerspectiveCamera,
  PointLight,
  Scene,
  WebGLRenderer
} from "three";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.js";
import { GLTF, GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader.js";

export default defineComponent({
  name: "HelloWorld",
  setup() {
    // 描画するDOMの指定
    const container = ref();
    // Three.js
    const scene = new Scene();
    const camera = new PerspectiveCamera();
    const renderer = new WebGLRenderer();
    const light = new PointLight();
    const loader = new GLTFLoader();
    const controls = new OrbitControls(camera, renderer.domElement);
    // 初期化
    const init = () => {
      if (container.value instanceof HTMLElement) {
        // DOMのサイズを取得
        const { clientWidth, clientHeight } = container.value;
        // 背景のグリッドの追加
        scene.add(new GridHelper(50));
        scene.background = new Color(0xcccccc);
        // ライトの設定
        light.color.setHex(0xffffff);
        light.position.set(10, 10, 0);
        scene.add(light);
        // オブジェクトの追加
        loader.load("assets/cnt.glb", (gltf: GLTF) => {
          const model = gltf.scene;
          model.scale.set(0.25, 0.25, 0.25);
          scene.add(model);
        });
        // カメラの設定
        camera.aspect = clientWidth / clientHeight;
        camera.updateProjectionMatrix();
        camera.position.set(10, 10, 0);
        camera.lookAt(0, 0, 0);
        // rendererの設定
        renderer.setSize(clientWidth, clientHeight);
        renderer.setPixelRatio(clientWidth / clientHeight);
        container.value.appendChild(renderer.domElement);
        // 描画
        animate();
      }
    };
    // 描画
    const animate = () => {
      const frame = () => {
        // 描画
        renderer.render(scene, camera);
        // 画面を更新
        requestAnimationFrame(frame);
      };
      frame();
    };
    // マウント時に初期化して描画
    onMounted(() => {
      init();
    });
    return {
      container,
    };
  },
});    
</script>

<template>
  <div ref="container" class="fixed w-full h-full top-0 left-0"></div>
</template>

<style scoped>
.fixed { position: fixed; }
.w-full { width: 100%; }
.h-full { height: 100%; }
.top-0 { top: 0px; }
.left-0 { left: 0px; }
</style>
HelloWorld.vue(抜粋)
<script lang="ts">
import { defineComponent, onMounted, ref } from "vue";
import {
  Color,
  GridHelper,
  PerspectiveCamera,
  PointLight,
  Scene,
  WebGLRenderer
} from "three";

export default defineComponent({
  name: "HelloWorld",
  setup() {
    // 描画するDOMの指定
    const container = ref();
    // Three.js
    const scene = new Scene();
    const camera = new PerspectiveCamera();
    const renderer = new WebGLRenderer();
    const light = new PointLight();
    // 初期化
    const init = () => {
      if (container.value instanceof HTMLElement) {
        // DOMのサイズを取得
        const { clientWidth, clientHeight } = container.value;
        // :
        // :
        // 描画
        animate();
      }
    };
    // 描画
    const animate = () => {
      const frame = () => {
        // 描画
        renderer.render(scene, camera);
        // 画面を更新
        requestAnimationFrame(frame);
      };
      frame();
    };
    // マウント時に初期化して描画
    onMounted(() => {
      init();
    });
    return {
      container,
    };
  },
});    
</script>

Three.js にて glTFファイル をロードする

上記で作成した"CNT.glb"をプロジェクトフォルダのpublic/assets/に移動しておきます。そして、HelloWorld.vue に次のようにGLTFLoaderを読み込むためのimport文を記載をします。

import { GLTF, GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader.js";

さらに、オブジェクトを追加するところにファイルをロードする処理を記述します。

        // オブジェクトの追加
        loader.load("assets/cnt.glb", (gltf: GLTF) => {
          const model = gltf.scene;
          model.scale.set(0.25, 0.25, 0.25);
          scene.add(model);
        });

これをnpm run devで試してみると、次のようにモデルがブラウザで表示されます。

イメージ1

イメージ2

npm run buildでビルドする際には、OrbitControlsのローカル変数のところでエラーになるため、tsconfig.jsonのパラメータを"noUnusedLocals": falseに書き換えておく必要があります。

おわりに

本稿では、いくつかの記事を参考にして、Three.jsをVue3で扱えるようにした環境で、Blenderで作成したカーボンナノチューブのモデルをブラウザに表示する手順を取り上げました。

本稿の環境

本稿のために使用した環境は以下となります。
macOS: Sonoma 14.4 (chip: Apple M1)
Blender: 4.0.2
anyenv: 1.1.5(homebrewにてインストール)
node: 18.19.0(anyenvのnodenvでインストール)
npm: 10.5.0
@vue/cli 5.0.8
three: 0.162.0
vue: 3.4.21
vue-router: 4.3.0
typescript:5.2.2
vite: 5.2.0

(●)(●) Happy Hacking!
/"" __""\

3
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
2