2020/07/05 10:37
Vue(Nuxt)でBlenderのgltfファイルを複数読み込む時の書き方メモ
Vue(Nuxt)でBlenderのgltfファイルを複数読み込む時の書き方メモ
PHPer(ペチパー)の私がVue(Nuxt)をいじる事になって、日々試行錯誤を繰り返してます。
WebGL(ThreeJS)を使ってBlenderで作ったオブジェクト(gltfファイル)をどうやって読み込ませるのかというメモを残しておきます。
取り合えず表示はされたのでこんな感じかなというのは分かったのですが、あくまで一例です。
想定としては「index.vue」ファイルがあって、その中で「toplogo.vue」という別ファイルを読ませているという形になってます。
※多分以前の記事には書いてますが、VueでThreeJSを使う為に必要なインストールとかは済んでる前提となります。
あと、スタイル(css)なんかは適当にあててください。
【index.vue】
<template>
<div>
<div class="c-top-mainvisual-canvas-area">
<topLogo />
</div>
</div
</template>
そしてこちらがgltfファイルに関する記述を書いてる方のファイル。
「item.gltf」と「logo.gltf」の2つを読ませ、1つはアニメーションさせ、1つは表示のみさせて位置や大きさなんかの指定をカスタムしてる感じです。
【toplogo.vue】
<template>
<div class="c-main-wrapper">
<div id="scene-container" ref="sceneContainer" class="scene-container" />
<div class="c-text-filter" />
</div>
</template>
<script>
import Vue from "vue";
import * as THREE from "three";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls";
import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader";
export default Vue.extend({
name: "TopLogo",
data () {
return {
container: null,
scene: null,
renderer: null,
camera: null,
controls: null,
url: null,
width: null,
height: null,
mouseX: 0,
rot: 0,
targetRot: null,
radian: null,
particle: null,
geometry: null,
directionalLight: null
};
},
created () {
},
mounted () {
this.init();
},
methods: {
init () {
// set container
const container = this.$refs.sceneContainer;
// add camera
const width = container.clientWidth;
const height = container.clientHeight;
if (width < 768) {
this.width = container.clientWidth;
this.height = container.clientHeight;
}
// console.log(width);
const fov = 60; // Field of view
const aspect = width / height;
const near = 0.001; // the near clipping plane
const far = 10000; // the far clipping plane
const camera = new THREE.PerspectiveCamera(fov, aspect, near, far);
camera.position.set(100, 100, 100);
camera.up.set(1, 1, 1);
// camera.lookAt(new THREE.Vector3(0, 0, 0));
// create scene
const scene = new THREE.Scene();
// 平行光源
const directionalLight = new THREE.DirectionalLight(0xFFFFFF, 3);
directionalLight.position.set(1, 1, 1);
// シーンに追加
scene.add(directionalLight);
// add controls
const controls = new OrbitControls(camera, container);
// create renderer
const renderer = new THREE.WebGLRenderer({ alpha: true });
renderer.setPixelRatio(window.devicePixelRatio);
renderer.gammaFactor = 2.2;
renderer.outputEncoding = THREE.sRGBEncoding;
renderer.physicallyCorrectLights = true;
container.appendChild(renderer.domElement);
// set aspect ratio to match the new browser window aspect ratio
camera.aspect = width / height;
camera.updateProjectionMatrix();
renderer.setSize(width, height);
// 星屑を作成 (カメラの動きをわかりやすくするため)
const createStarField = () => {
// 形状データを作成
const geometry = new THREE.Geometry();
for (let i = 0; i < 500; i++) {
geometry.vertices.push(
new THREE.Vector3(
3000 * (Math.random() - 0.5),
3000 * (Math.random() - 0.5),
3000 * (Math.random() - 0.5)
)
);
}
// マテリアルを作成
const material = new THREE.PointsMaterial({
size: 4,
color: 0xFFFFFF
});
// 物体を作成
const particle = new THREE.Points(geometry, material);
scene.add(particle);
};
createStarField();
const clock = new THREE.Clock();
let mixer;
const loader = new GLTFLoader();
const url = "threejs/item.gltf";
loader.load(url, (data) => {
const gltf = data;
const object = gltf.scene;
const animations = gltf.animations;
mixer = new THREE.AnimationMixer(object);
mixer.clipAction(animations[0]).play();
scene.add(object);
});
const urlLogo = "threejs/logo.gltf";
loader.load(urlLogo, (data) => {
const gltf = data;
const object = gltf.scene;
object.scale.set(30, 30, 30); // 縮尺の初期化
object.rotation.set(0, 0, 0); // 角度の初期化
object.position.set(0, 0, -2); // 位置の初期化
scene.add(object);
});
const eventHandler = () => {
this.mouseX = event.pageX;
};
// マウス座標はマウスが動いた時のみ取得できる
document.addEventListener("mousemove", eventHandler);
// レンダリング
const animation = () => {
camera.lookAt(new THREE.Vector3(0, 0, 0));
renderer.gammaOutput = true;
renderer.render(scene, camera);
controls.update();
if (mixer) {
mixer.update(clock.getDelta());
}
this.targetRot = (this.mouseX / container.clientWidth) * 360;
// イージングの公式を用いて滑らかにする
// 値 += (目標値 - 現在の値) * 減速値
this.rot += (this.targetRot - this.rot) * 0.2;
// ラジアンに変換する
this.radian = (this.rot * Math.PI) / 180;
// 角度に応じてカメラの位置を設定
camera.position.x = 8 + 0.5 * Math.sin(this.radian);
camera.position.y = -5 + 0.5 * Math.sin(this.radian);
camera.position.z = 8 + 0.5 * Math.cos(this.radian);
scene.rotation.x += 0.001;
scene.rotation.y += 0.001;
scene.rotation.z -= 0.001;
console.log("x:" + camera.position.x);
console.log("y:" + camera.position.y);
console.log("z:" + camera.position.z);
requestAnimationFrame(animation);
};
animation();
}
}
});
</script>
ちょっと余計なものも書いてるので長いですが、だいたいこんな感じでVueで表示させる事ができましたというメモでした。
最初のうちは「何で表示されないんだ!」という事が多いので、自分用の備忘録として残しておきます。
では現場から以上です!
1826