VRM - humanoid 3d avatar format for VR

VRM仕様

glTF-2.0のバイナリ形式glbをベースにした、VR向けモデルフォーマットです。

拡張子

.vrmを使います。gltfのバイナリ形式.glbと互換性があります。

保存できる要素

人間型モデル一体分の情報を保存します。

Texture

  • image1
  • sampler1

Material(json.extensions.VRM.materialProperties)

json.extensions.VRM.materialProperties[0] = {};
json.extensions.VRM.materialProperties[0].name = "Alicia_body"; # 名前
json.extensions.VRM.materialProperties[0].shader = "VRM/UnlitTexture"; # Shader名
json.extensions.VRM.materialProperties[0].renderQueue = 2000;
json.extensions.VRM.materialProperties[0].floatProperties = {}; # float型変数
json.extensions.VRM.materialProperties[0].keywordMap = {}; # キーワード型変数
json.extensions.VRM.materialProperties[0].tagMap = {}; # bool型変数
json.extensions.VRM.materialProperties[0].tagMap.RenderType = "Opaque";
json.extensions.VRM.materialProperties[0].textureProperties = {}; # テクスチャ参照変数
json.extensions.VRM.materialProperties[0].textureProperties._MainTex = 0;
json.extensions.VRM.materialProperties[0].vectorProperties = {}; # vec4型変数

Shader名とパラメータのディクショナリをペアにした汎用の構造で保存します。 現状、Unityに必要な項目を保存しています。 Shaderの詳細は、VRMが提供するシェーダーを参照してください。

Materialの規約

  • マテリアル名はモデル内でユニークである必要があります

Mesh

以下の頂点属性を記録します。

  • attribute1
    • position(vec3)
    • normal(vec3)
    • uv(vec2)
    • tangent(vec4)2

スキニング情報。4ボーンまでのスキニングを格納します。

  • bone weight(bone_index(int) + bone_weight(float)) x 4

モーフ情報。

  • morph target1
    • position(相対位置)
    • normal(vec3)
    • tangent(vec3)

Node

ボーン構造とメッシュを保持するノードをひとまとめにしたシーンです。

  • node1
    • name
    • position(vec3)
    • rotation(quaternion)
    • scale(vec3)

保存する値に対する規約

GLTF2の規約

GLTF2の規約に準拠します。 特に重要な項目です。

  • メートル単位
  • 右手Y-UP座標系3

VRMの規約

人間型モデルに特化して互換性を高めるために、以下の制約を課します。

  • モデルは原点に位置する
  • モデルは-Z向き3
  • モデルのヒエラルキーはY-UP4
  • モデルのメッシュはY-UP4
  • モデルのヒエラルキーはT-Pose
  • モデルのメッシュはT-Pose
  • ボーンに回転を入れない
  • ボーンにスケールを入れない
  • ヘッドボーンは真正面を向いている5

VRMが提供するシェーダー

セル調のキャラクタモデルの運用を想定して以下のシェーダーを用意しています。

Unlit系

ライティング・シェーディングせずにテクスチャ色をそのまま表示します。 半透明の扱い別に4種類を用意しています。

  • UnlitTexture(不透明)
  • UnlitCutout(透明度が閾値以下の部分を透明とする)
  • UnlitTransparent(アルファブレンド。ZWriteしない)6
  • UnlitTransparentZWrite(アルファブレンド。ZWriteする)7

MToon

セルシェーディング、輪郭線に対応したシェーダー。 Unlitよりもきめ細かく設定できます。

モデルのボーンマッピング(json.extensions.VRM.humanoid)

Meshスキニングに使われているボーンと人体の骨格の対応を規格化することで、モーションキャプチャ等のリアルタイムモーションの流し込みができるようにします。

json.extensions.VRM.humanoid = {};
json.extensions.VRM.humanoid.armStretch = 0.05;
json.extensions.VRM.humanoid.feetSpacing = 0;
json.extensions.VRM.humanoid.hasTranslationDoF = false;
json.extensions.VRM.humanoid.legStretch = 0.05;
json.extensions.VRM.humanoid.lowerArmTwist = 0.5;
json.extensions.VRM.humanoid.lowerLegTwist = 0.5;
json.extensions.VRM.humanoid.upperArmTwist = 0.5;
json.extensions.VRM.humanoid.upperLegTwist = 0.5;
json.extensions.VRM.humanoid.humanBones = [];
json.extensions.VRM.humanoid.humanBones[0] = {};
json.extensions.VRM.humanoid.humanBones[0].bone = "hips"; # 定義ボーン名
json.extensions.VRM.humanoid.humanBones[0].node = 3;
json.extensions.VRM.humanoid.humanBones[0].useDefaultValues = true;

定義しているボーン

  • hips
  • spine
  • chest
  • upperChest8
  • neck
  • head
  • Left/Right eye8

  • Left/Right upperArm

  • Left/Right lowerArm

  • Left/Right hand

  • Left/Right upperLeg

  • Left/Right lowerLeg

  • Left/Right foot

  • Left/Right toe8

  • Left/Right thumb proximal, intermediate, distal8

  • Left/Right index proximal, intermediate, distal8

  • Left/Right middle proximal, intermediate, distal8

  • Left/Right ring proximal, intermediate, distal8

  • Left/Right little proximal, intermediate, distal8

モデル情報(json.extensions.VRM.meta)

json.extensions.VRM.meta = {};
json.extensions.VRM.meta.title = "Alicia Solid";
json.extensions.VRM.meta.author = "DWANGO Co., Ltd.";
json.extensions.VRM.meta.contactInformation = "http://3d.nicovideo.jp/alicia/";
json.extensions.VRM.meta.reference = "";
json.extensions.VRM.meta.texture = 6;
json.extensions.VRM.meta.version = "";

json.extensions.VRM.meta.allowedUserName = "Everyone";
json.extensions.VRM.meta.violentUssageName = "Disallow";
json.extensions.VRM.meta.sexualUssageName = "Disallow";
json.extensions.VRM.meta.commercialUssageName = "Allow";
json.extensions.VRM.meta.otherPermissionUrl = "http://3d.nicovideo.jp/alicia/rule.html";

json.extensions.VRM.meta.licenseName = "Other";
json.extensions.VRM.meta.otherLicenseUrl = "http://3d.nicovideo.jp/alicia/rule.html";

情報

タイトル(Title)

アバターモデルの名前を設定します

作者(Author)

モデルの作者の名前を記述します

連絡先(Contact Information)

モデルの作者への連絡先を記述します

参照(Reference)

何か「親作品」に相当するものがある場合は参照URLなどを記述します

サムネイル(Thumbnail)

アバターモデルのサムネイルを登録します。2048x2048程度の解像度テクスチャを推奨します。meta情報内ではtexture番号を指定します。

バージョン

モデルの作成バージョンです。

使用許諾・ライセンス情報

License

アバターの人格に関する許諾範囲

Personation / Charaterization Permission

アバターに人格を与えることの許諾範囲(json.extensions.VRM.meta.allowedUserName)

A person who can perform with this avatar

  • アバターを操作することはアバター作者にのみ許される(Only Author)
  • 明確に許可された人限定(Explictly Licensed Person)
  • 全員に許可(Everyone)
このアバターを用いて暴力表現を演じることの許可(json.extensions.VRM.meta.violentUssageName)

Violent acts using this avatar

  • 不許可(Disallow)
  • 許可(Allow)
このアバターを用いて性的表現を演じることの許可(json.extensions.VRM.meta.sexualUssageName)

Sexuality acts using this avatar

  • 不許可(Disallow)
  • 許可(Allow)
商用利用の許可(json.extensions.VRM.meta.commercialUssageName)

For commercial use

  • 不許可(Disallow)
  • 許可(Allow)
その他のライセンス条件(json.extensions.VRM.meta.otherPermissionUrl)

Other License Url

上記許諾条件以外のライセンス条件がある場合はそのライセンス文書へのURLを記述

再配布・改変に関する許諾範囲

Redistribution / Modifications License

ライセンスタイプ(json.extensions.VRM.meta.licenseName)

License Type

その他ライセンス条件(json.extensions.VRM.meta.otherLicenseUrl)

Other License Url

上記許諾条件以外のライセンス条件がある場合はそのライセンス文書へのURLを記述

モーフ設定(json.extensions.VRM.blendShapeMaster)

BlendShapeをグループ化するBlendShapeGroupの配列を設定します。

json.extensions.VRM.blendShapeMaster.blendShapeGroups[1] = {};
json.extensions.VRM.blendShapeMaster.blendShapeGroups[1].binds = [];
json.extensions.VRM.blendShapeMaster.blendShapeGroups[1].binds[0] = {};
json.extensions.VRM.blendShapeMaster.blendShapeGroups[1].binds[0].index = 0;
json.extensions.VRM.blendShapeMaster.blendShapeGroups[1].binds[0].mesh = 3;
json.extensions.VRM.blendShapeMaster.blendShapeGroups[1].binds[0].weight = 100;
json.extensions.VRM.blendShapeMaster.blendShapeGroups[1].materialValues = [];
json.extensions.VRM.blendShapeMaster.blendShapeGroups[1].name = "A"; # 名前
json.extensions.VRM.blendShapeMaster.blendShapeGroups[1].presetName = "a"; # 事前定義名

ブレンドシェイプグループ(json.extensions.VRM.blendShapeMaster.blendShapeGroups)

名前

名前です。事前定義名と同じ(大文字)を推奨します

事前定義名

待機状態の表情

  • Neutral

リップシンク

  • A
  • I
  • U
  • E
  • O

瞬き

  • Blink
  • Blink_L
  • Blink_R

喜怒哀楽

  • Fun
  • Angry
  • Sorrow
  • Joy

視線制御

  • LookUp
  • LookDown
  • LookLeft
  • LookRight

その他

  • Unknown

ブレンドシェイプの名前の識別名

システムからブレンドシェイプを一意に認識する文字列IDを以下のロジックで決定します。

// 疑似コード
function GetID(preset, name)
{
  if (Preset != BlendShapePreset.Unknown)
  {
      return preset.ToString().ToUpper();
  }
  else
  {
      return name.ToUpper();
  }
}
  • ブレンドシェイプIDがユニークになるようにPresetとNameを設定する

一人称設定(json.extensions.VRM.firstPerson)

json.extensions.VRM.firstPerson.firstPersonBone = 36;
json.extensions.VRM.firstPerson.firstPersonBoneOffset = {};
json.extensions.VRM.firstPerson.firstPersonBoneOffset.x = 0;
json.extensions.VRM.firstPerson.firstPersonBoneOffset.y = 0.06;
json.extensions.VRM.firstPerson.firstPersonBoneOffset.z = 0;
json.extensions.VRM.firstPerson.meshAnnotations = [];
json.extensions.VRM.firstPerson.meshAnnotations[0] = {};
json.extensions.VRM.firstPerson.meshAnnotations[0].firstPersonFlag = "Auto";
json.extensions.VRM.firstPerson.meshAnnotations[0].mesh = 0;
json.extensions.VRM.firstPerson.meshAnnotations[1] = {};
json.extensions.VRM.firstPerson.meshAnnotations[1].firstPersonFlag = "Auto";
json.extensions.VRM.firstPerson.meshAnnotations[1].mesh = 1;
json.extensions.VRM.firstPerson.meshAnnotations[2] = {};
json.extensions.VRM.firstPerson.meshAnnotations[2].firstPersonFlag = "Auto";
json.extensions.VRM.firstPerson.meshAnnotations[2].mesh = 2;
json.extensions.VRM.firstPerson.meshAnnotations[3] = {};
json.extensions.VRM.firstPerson.meshAnnotations[3].firstPersonFlag = "Auto";
json.extensions.VRM.firstPerson.meshAnnotations[3].mesh = 3;
json.extensions.VRM.firstPerson.meshAnnotations[4] = {};
json.extensions.VRM.firstPerson.meshAnnotations[4].firstPersonFlag = "Auto";
json.extensions.VRM.firstPerson.meshAnnotations[4].mesh = 4;
json.extensions.VRM.firstPerson.meshAnnotations[5] = {};
json.extensions.VRM.firstPerson.meshAnnotations[5].firstPersonFlag = "Auto";
json.extensions.VRM.firstPerson.meshAnnotations[5].mesh = 5;
json.extensions.VRM.firstPerson.meshAnnotations[6] = {};
json.extensions.VRM.firstPerson.meshAnnotations[6].firstPersonFlag = "Auto";
json.extensions.VRM.firstPerson.meshAnnotations[6].mesh = 6;

一人称視点のアバターを描画する場合、自モデルの頭部の中が見えてしまうという問題が生じます9。 これに対応するために、一人称時の表示状態を指定できます。

firstPersonBone(json.extensions.VRM.firstPerson.firstPersonBone)

一人称時に描画を切り替えるべきボーンを指定します。通常Headです。

firstPersonBoneOffset(json.extensions.VRM.firstPerson.firstPersonBoneOffset)

一人時のヘッドセットの目標位置。 頭ボーンからヘッドセットへのオフセットを加味することを想定している。

meshAnnotations(json.extensions.VRM.firstPerson.meshAnnotations)

各メッシュに対して一人称時とそれ以外で表示・非表示を切り替えることができます。 以下の設定があります。

  • Auto: firstPersonBoneとその子孫に対するボーンWeightを持つポリゴンを自動で非表示にする10
  • FirstPersonOnly: 一人称のみ表示
  • ThirdPersonOnly: 三人称のみ表示(頭など一人称時に非表示にするメッシュに指定する)
  • Both: 特に表示切替をしない

視線設定

ターゲットの方向を向くようにキャラクタの視線を制御します。

json.extensions.VRM.firstPerson.lookAtTypeName = "Bone";
json.extensions.VRM.firstPerson.lookAtHorizontalInner = {};
json.extensions.VRM.firstPerson.lookAtHorizontalOuter = {};
json.extensions.VRM.firstPerson.lookAtVerticalDown = {};
json.extensions.VRM.firstPerson.lookAtVerticalUp = {};

目線タイプ(json.extensions.VRM.firstPerson.lookAtTypeName)

  • Bone: ボーンにより目線を操作します。
  • BlendShape: BlendShapeにより目線を操作します。BlendShapePreset.LookUp, LookDown, LookLeft, LookRightを使います。

角度調整

頭と目標物の角度差を目ボーンに適用する場合の角度を調整します。

json.extensions.VRM.firstPerson.lookAtHorizontalInner
json.extensions.VRM.firstPerson.lookAtHorizontalOuter
json.extensions.VRM.firstPerson.lookAtVerticalDown
json.extensions.VRM.firstPerson.lookAtVerticalUp

揺れモノ設定(json.extensions.VRM.secondaryAnimation)

尻尾や髪の毛などのひも状のオブジェクトの自動アニメーションの設定です。

揺れるボーン(secondaryAnimation.boneGroups)

json.extensions.VRM.secondaryAnimation.boneGroups[0] = {};
json.extensions.VRM.secondaryAnimation.boneGroups[0].bones = [];
json.extensions.VRM.secondaryAnimation.boneGroups[0].bones[0] = 41;
json.extensions.VRM.secondaryAnimation.boneGroups[0].bones[1] = 49;
json.extensions.VRM.secondaryAnimation.boneGroups[0].bones[2] = 57;
json.extensions.VRM.secondaryAnimation.boneGroups[0].bones[3] = 59;
json.extensions.VRM.secondaryAnimation.boneGroups[0].bones[4] = 61;
json.extensions.VRM.secondaryAnimation.boneGroups[0].center = -1;
json.extensions.VRM.secondaryAnimation.boneGroups[0].colliderGroups = [];
json.extensions.VRM.secondaryAnimation.boneGroups[0].colliderGroups[0] = 2;
json.extensions.VRM.secondaryAnimation.boneGroups[0].colliderGroups[1] = 1;
json.extensions.VRM.secondaryAnimation.boneGroups[0].colliderGroups[2] = 0;
json.extensions.VRM.secondaryAnimation.boneGroups[0].colliderGroups[3] = 3;
json.extensions.VRM.secondaryAnimation.boneGroups[0].colliderGroups[4] = 5;
json.extensions.VRM.secondaryAnimation.boneGroups[0].colliderGroups[5] = 4;
json.extensions.VRM.secondaryAnimation.boneGroups[0].colliderGroups[6] = 6;
json.extensions.VRM.secondaryAnimation.boneGroups[0].comment = "";
json.extensions.VRM.secondaryAnimation.boneGroups[0].dragForce = 0.4;
json.extensions.VRM.secondaryAnimation.boneGroups[0].gravityDir = {};
json.extensions.VRM.secondaryAnimation.boneGroups[0].gravityDir.x = 0;
json.extensions.VRM.secondaryAnimation.boneGroups[0].gravityDir.y = -1;
json.extensions.VRM.secondaryAnimation.boneGroups[0].gravityDir.z = 0;
json.extensions.VRM.secondaryAnimation.boneGroups[0].gravityPower = 0;
json.extensions.VRM.secondaryAnimation.boneGroups[0].hitRadius = 0.01;
json.extensions.VRM.secondaryAnimation.boneGroups[0].stiffiness = 0.65;

揺れるボーンの根元のボーン(json.extensions.VRM.secondaryAnimation.boneGroups[0].bones)

揺れモノの根元のボーンのノードインデックスを指定します。

揺れるボーンと衝突する当たり判定(json.extensions.VRM.secondaryAnimation.boneGroups[0].colliderGroups)

揺れモノに対する衝突判定グループのインデックスを指定します。

パラメーター

center(json.extensions.VRM.secondaryAnimation.boneGroups[0].center)

world原点以外の、揺れモノの基準点を設定できます。 ワープで移動するUIを実装した場合に、ワープ移動で揺れモノを揺らしたくない場合にワープで移動する親ノードを指定できます。

dragForce(json.extensions.VRM.secondaryAnimation.boneGroups[0].dragForce)

自動アニメーションの抵抗(減速)です。

garvityDir(json.extensions.VRM.secondaryAnimation.boneGroups[0].gravityDir)

重力の方向です。(0, -1, 0)にすると重力に、(1, 0, 0)にすると風のように作用します。

gravityPower(json.extensions.VRM.secondaryAnimation.boneGroups[0].gravityPower)

重力の強さです。

hitRadius(json.extensions.VRM.secondaryAnimation.boneGroups[0].hitRadius)

Colliderとの当たり判定の半径です

stiffiness(json.extensions.VRM.secondaryAnimation.boneGroups[0].stiffiness)

揺れモノの復元力(初期姿勢に戻る力)です

揺れモノ当たり判定設定(json.extensions.VRM.secondaryAnimation.colliderGroups)

揺れモノと衝突する球を設定します。

json.extensions.VRM.secondaryAnimation.colliderGroups = [];
json.extensions.VRM.secondaryAnimation.colliderGroups[0] = {};
json.extensions.VRM.secondaryAnimation.colliderGroups[0].colliders = [];
json.extensions.VRM.secondaryAnimation.colliderGroups[0].colliders[0] = {};
json.extensions.VRM.secondaryAnimation.colliderGroups[0].colliders[0].offset = {};
json.extensions.VRM.secondaryAnimation.colliderGroups[0].colliders[0].offset.x = 0;
json.extensions.VRM.secondaryAnimation.colliderGroups[0].colliders[0].offset.y = 0.05;
json.extensions.VRM.secondaryAnimation.colliderGroups[0].colliders[0].offset.z = 0;
json.extensions.VRM.secondaryAnimation.colliderGroups[0].colliders[0].radius = 0.09;
json.extensions.VRM.secondaryAnimation.colliderGroups[0].colliders[1] = {};
json.extensions.VRM.secondaryAnimation.colliderGroups[0].colliders[1].offset = {};
json.extensions.VRM.secondaryAnimation.colliderGroups[0].colliders[1].offset.x = 0;
json.extensions.VRM.secondaryAnimation.colliderGroups[0].colliders[1].offset.y = 0;
json.extensions.VRM.secondaryAnimation.colliderGroups[0].colliders[1].offset.z = 0;
json.extensions.VRM.secondaryAnimation.colliderGroups[0].colliders[1].radius = 0.09;
json.extensions.VRM.secondaryAnimation.colliderGroups[0].node = 34;

ノード(json.extensions.VRM.secondaryAnimation.colliderGroups[0].node)

当たり判定を設置するノードです。

ローカル座標(json.extensions.VRM.secondaryAnimation.colliderGroups[0].colliders[1].offset)

当たり判定のノードからのローカル座標です。

半径(json.extensions.VRM.secondaryAnimation.colliderGroups[0].colliders[1].radius)

当たり判定の半径です。


  1. gltf2として保存します。 [return]
  2. wには1か-1が入ります。 [return]
  3. OpenGL座標系。+Xが右、+Yが上、+Zが手前です。 [return]
  4. Blenderや3ds MaxなどのZ-UPのモデラーに由来するモデルでヒエラルキーの途中でx軸-90度の回転を入れてZ-UPが入れ子になっている場合があります。 [return]
  5. 視線制御はT-Pose時のヘッドの向きを基準に目標物の方向を計算します。 [return]
  6. 煙や頬の色など実体の無いオブジェクト向けです。 [return]
  7. 半透明の衣装や、髪の先が半透明など実体のあるオブジェクト向けです。 [return]
  8. 無くても問題ありません。 [return]
  9. バックフェースカリングや近平面である程度対処できますが、口の中が作りこんであるモデルの歯茎が意図せずに見えてしまうなど不十分な場合があります。 [return]
  10. 実行時に自動で非表示部分を削除したモデルを生成します。 [return]
VRM - humanoid 3d avatar format for VR