#24 pixiv/three-vrm + TypeScriptのテンプレートを作る https://gyazo.com/8c0d08252ed239800b8ea45ba0a75ff8
基本情報
VRMをブラウザで動かすアプリを作るときに使っているテンプレートを少し修正して公開します。
機能としてはローカルのVRMファイルを読み込んで表示するところまで作ります。
構成
パッケージ
インストール
code:sh
npm i three @pixiv/three-vrm
npm i -D webpack webpack-cli typescript ts-loader webpack-dev-server
webpack-dev-serverを使っています。
code:package.json
{
...
"scripts": {
"build": "webpack",
"dev": "webpack serve"
},
...
}
差分はファイル・ディレクトリ名くらいです。
ソースコード
HTML
code:index.html
...
<div id='ui'>
<input type='file' accept='.vrm' id='inputVRM'>
</div>
<div id='viewer'></div>
...
アバターを描画用のcanvasを配置するところと、その上に重ねて操作用のbuttonやinput要素等を配置しています。
今回は他の要素は特にないので描画領域をウィンドウいっぱいに広げています。
code
code:index.ts
import { Avatar } from './Avatar';
import { Viewer } from './Viewer';
// ...
Avatar
主にVRMを人型アバターとして扱うクラスです。
よくポーズ関係の機能を追加して使っています。
今回はVRMの読み込み部分のみを実装しています。
code:Avatar.ts
import * as THREE from 'three';
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader';
import { VRM } from '@pixiv/three-vrm';
export class Avatar {
private _scene: THREE.Scene;
private _vrm: VRM;
constructor(scene: THREE.Scene) {
this._scene = scene;
this._vrm = null;
}
// VRMの読み込み
public async loadVRM(url: string) {
if (this._vrm) {
this._scene.remove(this._vrm.scene);
this._vrm.dispose();
}
const loader = new GLTFLoader();
const gltf = await loader.loadAsync(url);
const vrm = await VRM.from(gltf);
this._scene.add(vrm.scene);
this._vrm = vrm;
}
}
Viewer
主にthree.jsのSceneに必要なものを用意します。
カメラ操作にはOrbitControlsを使用しています。
ウィンドウサイズを変更した時のためにリサイズ用の処理を実装しています。
code:Viewer.ts
import * as THREE from 'three'
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls'
export class Viewer {
private _canvas: HTMLCanvasElement;
private _renderer: THREE.WebGLRenderer;
private _scene: THREE.Scene;
private _camera: THREE.PerspectiveCamera;
private _controls: OrbitControls;
constructor(parentElement: HTMLElement) {
//レンダラーの設定
// カメラ
// カメラコントローラー
// ライト
// 軸・グリット表示
}
public get scene(): THREE.Scene {
return this._scene;
}
public update() {
this._renderer.render(this._scene, this._camera);
}
public onResize() {
const parentElement = this._canvas.parentElement;
this._renderer.setPixelRatio(window.devicePixelRatio);
this._renderer.setSize(parentElement.clientWidth, parentElement.clientHeight);
this._camera.aspect = parentElement.clientWidth / parentElement.clientHeight;
this._camera.updateProjectionMatrix();
}
}
要素を取得して初期化
code:index.ts
import { Avatar } from './Avatar';
import { Viewer } from './Viewer';
window.addEventListener('DOMContentLoaded', () => {
const viewerElement = document.getElementById('viewer');
const viewer = new Viewer(viewerElement);
const avatar = new Avatar(viewer.scene);
//デフォルトモデル読み込み
avatar.loadVRM('./three-vrm-girl.vrm');
// ローカルのVRMの読み込み
const inputVRM = document.getElementById('inputVRM');
inputVRM.addEventListener('change', event => {
const target = event.target as HTMLInputElement;
const files = target.files;
if (!files) return;
if (!file) return;
const blob = new Blob(file, { type: 'application/octet-stream' }); const url = URL.createObjectURL(blob);
avatar.loadVRM(url)
});
window.addEventListener('resize', () => {
viewer.onResize();
});
//フレーム更新
const update = () => {
requestAnimationFrame(update);
viewer.update();
}
update();
});
実行
code:sh
npm run dev
ホットリロードを有効にするとデフォルトアバターが毎回読み込まれて面倒かもしれません。
https://gyazo.com/8c0d08252ed239800b8ea45ba0a75ff8
左上のファイルを選択からローカルのVRMファイルを読み込んでみます。
https://gyazo.com/aaf82853e3b709798690e0f19b75580f
ちゃんと読み込まれて表示されていればOKです。
あとはお好みで色々と改造してみてください。
シェーダーを適用してみたり
身長等を表示させたり
参考