three.js + three-vrm で、VRoid 製アバターの服と髪の色をゲーム内で変える機能を作った。白い T シャツは狙い通り色が変わるのに、短パンは何色を指定しても真っ黒になる。
原因は MToon マテリアルの色の掛け算の仕組みにある。同じ色替えコードでも、元テクスチャの明暗で挙動が真逆になる。
ハマった順に原因と直し方を書く。
three.js + three-vrm で、VRoid 製アバターの服と髪の色をゲーム内で変える機能を作った。白い T シャツは狙い通り色が変わるのに、短パンは何色を指定しても真っ黒になる。
原因: MToon の色は「掛け算」
MToon の litFactor (基本色) は baseColorTexture に乗算される。
- 白い布のテクスチャ × 指定色 = 指定色が出る
- 暗い布のテクスチャ × 指定色 = 黒く潰れる
さらに陰側の shadeMultiplyTexture も暗い布だと、陰が全部黒に落ちる。つまり同じ色替えコードでも、元テクスチャの明暗で結果が真逆になる。白 T シャツで動いたからと全マテリアルに一般化したのが敗因だった。
直し方: 暗い布だけテクスチャを外す (flatten)
暗い布のマテリアルに限って、map と shadeMultiplyTexture を null にして litFactor の平面色を主役にする:
function tintBaked(mat, color, flatten) {
mat.color.set(color);
if (flatten) {
mat.map = null;
mat.shadeMultiplyTexture = null;
}
}
布の織り目は消えるが、色は正しく出る。元に戻せるように、外したテクスチャは復元用に控えておく。
ついでに踏んだ罠 2 つ
1. Blender を通した GLB はマテリアル名にサフィックスが付く。 同名マテリアルが統合できない時に tint が tint.005 になる。色替え対象を name === 'tint' の完全一致で探していると .005 だけ塗り残る。正規表現 (/^tint\.\d+$/) で拾う。
2. VRoid 書き出し VRM から顔のスライダー値は読めない。 Face の morph は 57 個あるが表情用で、体型・顔つきのパラメータは書き出し時に頂点へ焼かれていて値が残っていない。顔パーツをいじりたい場合はマテリアル名で mesh を特定して実行時に頂点を動かすしかない。
まとめ
- MToon の色替えは乗算。暗いテクスチャは何色を掛けても黒
- 暗い布は map / shadeMultiplyTexture を外して litFactor を主役に
- 1 つのマテリアルで動いた色替えを全マテリアルに一般化しない
- マテリアル名の完全一致比較は Blender の .00N サフィックスで漏れる
コメント (0)
まだコメントなし。