VRM - humanoid 3d avatar format for VR

GLTFについて

glTFはOpenGLの仕様策定をしているKHRONOSによる3Dフォーマットです。2017年にVersion2が策定されました。 VRMはglTF2.0をベースとしたフォーマットとなっています。

glTFはどんな情報を記録できるのか

  • メッシュ(頂点配列、インデックス配列)
    • モーフターゲット
    • スキニング(4weight)
  • テクスチャ
  • マテリアル(PBR)
  • シーン
  • アニメーション1
  • カメラ1
  • 光源1

3Dのシーン全体を記録できます。

  • OpenGLの右手系Y-UP座標
  • メートル単位
  • リトルエンディアン

glTFフォーマット概説

JSON記述部と、画像や頂点配列を記録するバイナリ部の2つの部分からなります。

gltf形式では、URLやパスで参照する方法で外部のバイナリデータにアクセスします。 glb形式ではJSON部とバイナリ部をひとつのファイルにまとめていて、バイト列のオフセットでバイナリデータにアクセスします。 プログラムから扱うには外部ファイルへのアクセスが無いglb形式の方が簡単2です。

glb形式

ヘッダ部 + チャンク部繰り返しという構造になっています。 実質的には、 ヘッダ部 + JSON CHUNk + BINARY CHUNKとなります。

ヘッダ部

長さ 内容
4 ascii “glTF”
4 gltfバージョン int32 2
4 file size int32

チャンク部

長さ 内容
4 chunk size int32
4 chunk type ascii “JSON” or “BIN\x00”
chunk size chunk body バイト列

python3によるパース例

import struct
import json

class Reader:
    def __init__(self, data: bytes)->None:
        self.data = data
        self.pos = 0

    def read_str(self, size):
        result = self.data[self.pos: self.pos + size]
        self.pos += size
        return result.strip()

    def read(self, size):
        result = self.data[self.pos: self.pos + size]
        self.pos += size
        return result

    def read_uint(self):
        result = struct.unpack('I', self.data[self.pos:self.pos + 4])[0]
        self.pos += 4
        return result


def parse_glb(data: bytes):
    reader = Reader(data)
    magic = reader.read_str(4)
    if  magic != b'glTF':
        raise Exception(f'magic not found: #{magic}')

    version = reader.read_uint()
    if version != 2:
        raise Exception(f'version:#{version} is not 2')

    size = reader.read_uint()
    size -= 12

    json_str = None
    body = None
    while size > 0:
        #print(size)

        chunk_size = reader.read_uint()
        size -= 4

        chunk_type = reader.read_str(4)
        size -= 4

        chunk_data = reader.read(chunk_size)
        size -= chunk_size

        if chunk_type == b'BIN\x00':
            body = chunk_data
        elif chunk_type == b'JSON':
            json_str = chunk_data
        else:
            raise Exception(f'unknown chunk_type: {chunk_type}')

    return json.loads(json_str), body


with open('AliciaSolid.vrm', 'rb') as f:
    parsed, body = parse_glb(f.read())

VRMによる拡張情報

JSONのjson['extensions']['VRM']以下に格納されています。


  1. VRMではサポートしていません [return]
  2. VRMではglbを採用しています。 [return]
VRM - humanoid 3d avatar format for VR