シリーズ
- Svelte で 3D 表示を行うライブラリ Threlte #1: パッケージの作成とライブラリ概要
- Svelte で 3D 表示を行うライブラリ Threlte #2: エクストラパッケージを使用する
- Svelte で 3D 表示を行うライブラリ Threlte #3: glTF シーンを扱う
- Svelte で 3D 表示を行うライブラリ Threlte #4: シーンコントロールツールセット Theatre.js for Threlte
- Svelte で3D表示を行うライブラリ Threlte #5: XR への対応
glTF について
glTF は Khronos Group Inc. が提唱しているファイルフォーマット、シーンディスクリプタ。
最初からストリーミング機構などをサポートしているため、Web 利用などと相性が良く、近年普及が進んできている。
昨年の SIGGRAPH 2023 では glTF の今後のフォーマットに関してのセッションやフォーラムも数多くあった。
参加者には、Khronos Group の人に加え、Meta、Adobe、NVIDIA、Autodesk、Epic Games などが参加しており、今後は、XR や 3D 広告での利用をもっと広めていく狙いもあるとみられる。
- The State of 3D Asset Interoperability using USD and glTF
- Geometry, Textures, and Workflow - Optimizing glTF
- glTF Complex Scenes & Interactivity
- Exploring the Artistic Frontier: Unleashing Creativity in 3D Models with glTF and PBR
- Let's Get Moving: Adding Physics to glTF
- glTF: Transforming 3D Asset Delivery for Real-Time Graphics
- Khronos is holding our fourth invite-only glTF Ecosystem Forum at this year’s SIGGRAPH in Los Angeles, California.
glTF のデータ構造
データ構造を理解していると、コード上でのアクセスもしやすい。
なので、一応のデータ構造に関してもここで扱っておく。
{
animations: AnimationClip[],
asset: {generator: string, version: number},
cameras: Camera[],
materials: 'Materials'[],
nodes: (Group | Mesh | SkkinedMesh)[],
parser: GLTFParser,
scenes: Group[]
userData: {}
}
- animations
- アニメーション(クリップ)情報が保存されている
- asset
- glTF ファイル自体の情報
- cameras
- カメラ情報が保存されている
- materials
- マテリアル情報が保存されている
- nodes
- メッシュやスキンメッシュ(アニメーションする際に必要なメッシュタイプ)、内包シーン情報が保存される
- parser
- GLTF のパース情報。JSON 形式もこちら経由で取得できる。
- scenes
- シーン情報を保存
- userData
- ユーザー固有のデータの保存領域
glTF の種類
glTF には、大きく分けて、
- Ascii タイプ
- 拡張子は .gltf
- テキストで構成されているファイルタイプ。一般的なテキストエディタで中身を見ることができる。
- 文字列のパースを含むので、ファイルサイズが大きければ大きいほどロードに時間がかかる。
- Binary タイプ
- 拡張子は .glb
- 内容がバイナリで構成されていてファイルの中身を通常のテキストエディタ等では閲覧できない。
- ファイルサイズが抑えられるのと、ロードの速度が速い。
が存在する。
ファイルによってはテクスチャ等が分かれているので、それら不随ファイルが足りないと、以下の機構で利用するさいはロードエラーを起こすので注意する。
Threlte における glTF
Threlte では、他のファイルフォーマットに比べて最初からサポートがある。
通常のファイルロードに加えて、アニメーションやシーン周りのサポートもあるため、諸々を説明するより先に Threlte での glTF の扱い方を紹介した方がモノにしやすいというのもあり、この段階での glTF の扱い方についてまとめるに至った。
では、まずは簡易ロードの仕方から。
glTF 関連モジュールの使い方
<GLTF>
- glTF ファイルの簡易ロード
<GLTF>
を使用すると、それ単体での glTF ファイルの簡易ロードが可能。
<script lang="ts">
import { T } from '@threlte/core';
+ import { ContactShadows, GLTF, Grid, OrbitControls, Environment } from '@threlte/extras';
</script>
<T.PerspectiveCamera makeDefault position={[5, 2, 13]} fov={15}>
<OrbitControls enableZoom={true} enableDamping target.y={1.5} />
</T.PerspectiveCamera>
<Environment
files={'https://raw.githubusercontent.com/mrdoob/three.js/dev/examples/textures/equirectangular/venice_sunset_1k.hdr'}
isBackground={true}
/>
<Grid
position.y={-0.001}
cellColor="#ffffff"
sectionColor="#ffffff"
sectionThickness={0}
fadeDistance={30.0}
cellSize={2}
/>
<ContactShadows scale={10} blur={2} far={2.5} opacity={0.5} />
<!-- 今回は glTF のサンプルモデルを直接 static 以下に配置して使用 -->
<T.Mesh position={[0, 1.3, 0]}>
+ <GLTF
+ url="https://raw.githubusercontent.com/KhronosGroup/glTF-Sample-Models/main/2.0/DamagedHelmet/glTF/DamagedHelmet.gltf"
+ />
</T.Mesh>
useGltf
- glTF ファイルの操作利用
glTF シーンをロードして、そのシーンをコントロールしながら描画する場合は useGltf
フックでロードして使用する。
Web アプリとして実装する場合はこちらでの利用が多くなると思われる。
useGltf を使用すると、glTF を取り回しができるインスタンスオブジェクトとして扱えるようになる。
<script lang="ts">
import { T } from '@threlte/core'
+ import { Grid, OrbitControls, useGltf, Environment } from '@threlte/extras'
const gltf = useGltf("./models/DamagedHelmet.glb", { useDraco: true })
</script>
{#if $gltf}
+ <T is={$gltf.nodes['node_damagedHelmet_-6514']} />
{/if}
glTF オブジェクトにすると、シーン内の詳細にもアクセスできる様になる。
なので、例えばマテリアルを差し替えるなども可能。
<script lang="ts">
import { T } from '@threlte/core'
import { Grid, OrbitControls, useGltf, Environment } from '@threlte/extras'
+ import { MeshStandardMaterial, Color } from 'three'
const gltf = useGltf("./models/DamagedHelmet.glb", { useDraco: true })
</script>
{#if $gltf}
<T.Mesh
+ geometry={$gltf.nodes['node_damagedHelmet_-6514'].geometry}
+ material={new MeshStandardMaterial({roughness: 0.0,
+ color: new Color("rgb(255, 0, 0)")})}
rotation={[1.5, 0, 0]}
/>
{/if}
useGltfAnimations
- glTF でのアニメーションを再生する
Three.js の場合でもそうだが、大体の 3D フレームワーク同様、メッシュとアニメーションの処理は柔軟に扱えるよう、別々に取り扱うように設計されている。Mesh : Aimation = 1:1 ではないのである。
なので、これらを取り扱う場合には、ワンステップ踏む必要がある。
Threlte では、これを @threlte/extras
の useGltfAnimations
などを使って実現する。
<script lang="ts">
import { T } from '@threlte/core'
+ import { useGltf, useGltfAnimations, GLTF } from '@threlte/extras'
+ const { gltf, actions } = useGltfAnimations()
+ $: $actions['Take 001']?.play()
</script>
<!-- その他シーンセットアップは省略 -->
+<GLTF
+ bind:gltf={$gltf}
+ url="/models/LittlestTokyo.glb"
+ useDraco={true}
+/>
glTf のアニメーションは、一つの glTF ファイルに複数内包されいてる可能性がある。
そのため、アニメーションを再生する際には、アニメーションのトラック名を指定して、再生してあげる必要がある。
こういったところが、アニメーションを扱う上で意図手間かかる所以。
@threlte/gltf
- glTF コンポーネント出力ツール
glTF の内部構造にアクセスする場合は名前解決が必要だったりする。
それを上手く解析して表示するには、3D エディタ的な機能まで必要だったりするが、Threlte では、glTF ファイルを読み込むためのコンパクトな svelte コンポーネントを出力しくれるツールが提供されている。
それが @threlte/gltf
である。
例えば、DamagedHelmet の場合、
npx @threlte/gltf@latest DamagedHelmet.glb
といったコマンドを実行することによって、以下のような Svelte コンポーネントを出力してくれる。
<script>
import { Group } from 'three'
import { T, forwardEventHandlers } from '@threlte/core'
import { useGltf } from '@threlte/extras'
export const ref = new Group()
const gltf = useGltf('/DamagedHelmet.glb')
const component = forwardEventHandlers()
</script>
<T is={ref} dispose={false} {...$$restProps} bind:this={$component}>
{#await gltf}
<slot name="fallback" />
{:then gltf}
<T.Mesh
geometry={gltf.nodes['node_damagedHelmet_-6514'].geometry}
material={gltf.materials.Material_MR}
rotation={[Math.PI / 2, 0, 0]}
/>
{:catch error}
<slot name="error" {error} />
{/await}
<slot {ref} />
</T>
あとは、これをコンポーネントとして再利用するだけ。
<script lang="ts">
import Model from '$lib/models/DamagedHelmet.svelte';
</script>
<!-- シーン設定は省略 -->
<Model />
読み込むファイルが決まっていたり、コンパクトなローダーで収めたり、対象ハックする参考としてこれを利用するという手がある。
glTF を扱う上での付随知識
DRACO ローダーの使用
glTF は、際してコンパクトに取り回しが行える様に圧縮がかけられている事がままある。
その圧縮形式は今のところ DRACO が頻繁に使われている(現状は、実質一択)。
その DRACO 解凍 を行いつつロードしてくれる仕組みが DRACO ローダー として用意されている。
この Draco 圧縮がかかったものを、ローダーを通さずにロードしようとするとエラーを起こす。
Error in asyncWritable: THREE.GLTFLoader: No DRACOLoader instance provided.
そのため、エラー回避も含めてローダーを使用してロードしておく。
以下の様に複数やり方があるので、状況に応じて選択する。
<script lang="ts">
import { userGltf } from "@threlte/extras"
const gltf = useGltf('/model.glb', { useDraco: true })
</script>
<GLTF
url="/models/LittlestTokyo.glb"
useDraco={true}
/>
また、true
を渡すとライブラリ標準のものがロードされてくるが、ローダーを直接指定することも可能。
特別、ローダーを変更したい場合に指定すると良い。
<GLTF
url="/models/LittlestTokyo.glb"
useDraco="https://www.gstatic.com/draco/v1/decoders/"
/>
もう一つとしては Meshpot が存在する。
ただ今のところ僕は使っているところを見たことがないので、要調査。
使い方としては、DRACO と同じで、useMeshpot
を指定する。
<GLTF
url="/models/LittlestTokyo.glb"
useMeshpot={true}
/>
次のシリーズを読む
Svelte で 3D 表示を行うライブラリ Threlte #4: シーンコントロールツールセット Theatre.js for Threlte