ツール本体
UTF-8
Shift-JIS
ツールの使い方
※ まだ正常に読み込めないモデルが多数存在する。読み込みに失敗した場合はタブごと読み込み直したほうが良い。
zip ファイルを渡す方法
文字コードを選ぶ
Zip ファイル内のコンテンツのファイル名が、UTF-8 でエンコードされているのか、Shift-JIS でエンコードされているのか選ぶ。緑色になっているのが現在選択されている文字コードである。
目安としては、Windows で圧縮されたであろう Zip ファイルには Shift-JIS を、最近の Linux 環境などで圧縮された Zip ファイルには、UTF-8 を選択するといった感じである。
データを渡す
モデルとテクスチャをまとめて指定のところで、モデルファイルが入っているzipファイルをそのまま渡す。パスワードロック解除とかには現状はまだ対応していない。(使っているライブラリは対応しているらしいので、後々実装したい。)
展開して直接ファイルを渡す方法
1. 適当な MMD モデルをダウンロードしてくる
2. zip ファイルとかになっていると思うのでとにかく展開する
3. textures フォルダーの内容を、モデルファイルと同じ場所に全部配置し直す
- 配置し直す前
modelFolder
├── model.pmx
├── readme.txt
└── textures
├── 何かしらのテクスチャ.png
├── 何かしらのテクスチャ1.png
├── ...
├── 何かしらのテクスチャ2.bmp
├── 何かしらのテクスチャ3.bmp
├── ...
- 配置し直したあと
modelFolder
├── 何かしらのテクスチャ.png
├── 何かしらのテクスチャ1.png
├── ...
├── 何かしらのテクスチャ2.bmp
├── 何かしらのテクスチャ3.bmp
├── ...
├── model.pmx
└── readme.txt
4. ファイルを指定する
モデルとテクスチャをまとめて指定のところで、モデルファイルとテクスチャのファイルをすべて指定する。すると、自動的にモデルが読み込まれ、Three.js の シーンが立ち上がってうまく行けば、モデルが見えるはずである。ちなみに全てのモデルには対応できないようで、読み込めない場合もあるためご了承ください。
このツールを作ろうとした動機
単に気軽に、MMD モデルを確認したいとき用。pmx や pmdを読み込んで表示し、色々カメラを動かせるところまでを第一マイルストーンとして、できればボーンを自由に動かしたり、vmd を読み込んで動かせたりする実装してみたいとは思う。
細かいこと
ここからしたはグダグダでもいいからとにかく、何をしたかとか、考えてたとか、思ってたかを記録に残したものになっている。
現状
(2025/9/27)
久しぶりに、Toon shader についてなんとかしようと思い、MMDToonShader
を mesh
に割り当ててみようと、three.js のドキュメントを漁ってみたら、最終的に勘違いに気づいた。
MMDToonShader
自体はすでに MMDLoader
内で処理が走っており、.load
メンバ関数を実行すると、本来であれば、Toon shader は動くはずっぽかった。しかし、その実装自体にそもそも問題があるのかもしれないと思い始めめた。具体的には、リソースファイル名についてリネームする部分がコードに含まれていることが気になった。これはまさしく存在しないはずのリソースを参照する動作と関係がありそうだと思い始めた。
ということで、とりあえず現状は、toon shader と存在しないはずのリソースを参照する問題 としてまとめて置くことにした。
また、わざわざすでに一応新規である MMDLoader 候補のリポジトリもあるけど、動く様子もないし、とにかくまともに動かしたいので、自分で three.js r170 をフォークして修正を試みてみることを考えた。現状では、とりあえずフォークして専用のブランチを作って、そこをデフォルトに設定するまではした。
(2025/9/13)
zip ファイルとエントリの文字コード
zip ファイルから直接読み込むときに、Shift-JIS でファイル名がエンコードされた zip ファイルでしか正常動作しなくなっていた。それで、Linux 環境とかで、日本語が UTF-8 でエンコードされていたりしても、zip ファイル内を正しく読み込めるよう、Shift-JIS と UTF-8 で文字コードを手動で選べるようにした。
zip.js 側には、filenameUTF8 : false
という項目が一応あるが、文字コード関連の自動化はうまく行かない気がするので、とりあえず手動で選択できるようにした。
toon shader
モデルが白っぽくなったり、shader でコケてエラーになってしまうなど、問題があることに対して、ちょっと今回は調べた。MeshPhongMaterial あたりを参考に実装してみればいいかという見当は付いた気がする。
blob URL への変換がうまく行ってないように見える件について
実際に調べてみると、変換がうまく行っていないというより、参照しようとしているファイル名のテクスチャなどが配布されたデータ内にそもそも存在しない、という場合があることがわかった。blender 上で動く拡張機能 mmd_tools を使うと問題が出ないようなモデルで、そのような症状が出ていることがわかった。
(2025/8/26)
なんとか zip.js を使って直接ダウンロードしたモデルファイルを zip ファイルのまま読み込ませることができるようにした。多くのモデルファイルの zip ファイル内のコンテンツには、shift-jis が使われており、それでファイル名が文字化けしてしまう問題があり、それが面倒くさかった。
ただ、現状の実装だと、zip ファイルのエントリが、基本的に shift-jis であることを前提としているため、比較的新しい環境や、UTF-8 を標準としている環境で圧縮された zip ファイルとは多分相性問題を起こすと思われるので、そこを修正したい。
(2025/8/13)
とりあえず、ChatGPT-5 もとにかく詰まったら使って、MMDモデルを読み込むところまで持っていくことはできた。ただし使い方にクセのある仕様になってしまった。MMD の表示にはモデルファイルだけでなく、テクスチャなどの周辺情報が必要であるが、そこら辺の読み込み方法に関して苦戦していた。まず、直接ファイルパスのURLを渡す以外で、blob の参照のURLも利用できることを試すまでは、うまくいっていたが、それ以降のテクスチャを読み込むことに関しては詰まった。そこら辺に関しては ChatGPT-5 が Map を使う方法を吐き出したのでそれをそのまま利用した。
正直手軽だとは現状言い難いし、ファイル読み込みにはかなり改善の余地があるものと感じられる。ただ、そのためだけにバックエンドとか用意したくないし悩みものである。なんとかクライアント側の JS で、ZIPファイルだったりをガリガリ処理させられないか考える必要もありそう。
TODOs
- 最低限 pmx や pmd を読み込み、カメラを動かせる
- 全画面モードや高解像度モード。
- zip ファイルなどを受け付けて、モバイルでも利用しやすくする (バグはまだ残ってる)
- zip.js が Shift-JIS との相性問題で、zip ファイル内のファイル名を正しく解釈できず、文字化けする問題を何とかする
->zip.js を適当に fork して Shift-JIS に対応させたり、UTF-8 にファイル名を変換させるか?
->rawFilename プロパティがあるので、それを Shift-JIS エンコードで読み出すか?
-> 最終的に ChatGPT-5-Mini に要件を渡して書かせたコードを使った。TextDecoder を使ったものを採用した。 - zip ファイル内のエントリの名前の文字コードを自動的に識別し、適切にデコードさせるか、ユーザがフォーマットをあらかじめ指定できるようにして、相性問題を起こらないようにする。
- view 自体にモデルファイルをドラックアンドドロップできるようにする
- 複数のモデルが見つかったときにセレクターを表示するようにする
- コードのリファクタリングによる、実装の分離
- パスワード付き zip への対応
- ファイルロードの進捗状況を表すプログレスバーの実装。UX の改善
- blob URLへのMAP変換がうまくいかないバグへの対応
// 例
manager is working
mmdviewer/:516 足.png
mmdviewer/:517 blob:https://www.nyanmo.info/足.png
mmdviewer/:515
- 欠損しているテクスチャ画像などのリソースエラーの表示
- 正しくテクスチャを表示できず、白っぽくなったり、色褪せたみたいな表示になったりする。toon周り未対応の弊害?とにかくもっとまともなマテリアルだのテクスチャだのの実装をする。
- テクスチャなしとありモードの切り替えボタン
- 全体的にもっと明るい感じにする。薄暗い感じの解消
- blender のように、上や横に自動的にカメラを運ぶ機能
- 透視投影、平行投影の切り替え
- blender みたいに等間隔の補助線を表示する
- 地面を表示する。
- 全画面モードも考慮したコントローラー
- 専用フォームでない view へのファイルのドラックアンドドロップに対応する
- いくつかサンプルモーションを用意し、ボタンを押すだけでモデルを動かせるようにする。例えば歩くとか、何か踊らせるとか。
- 自動で カメラやモデルをぐるぐる動かして、放置していたら全体像を眺められるようにする。
- WASD や タッチパネル上のコントローラーを用意してモデルを動かせるようにする。
- 机とか、椅子とか家の一室みたいなのとか、Sample オブジェクトを配置する。
- youtubeでいう シアターモードみたいなの欲しい。タブ内で全画面みたいな。
- モデルなどをうまく読み込めなかったときのエラーハンドリングと通知
- ボーンを動かせる
- vmd を読み込める
- 複数モデル読み込み
調べてわかったこと
MMDLoader
MMDLoader などは、最新バージョンの three.js にはないっぽいので、古いバージョンを参照する必要がありそう。r170くらいで three.js/examples/jsm /loaders/MMDLoader.js の github の差分に以下のような記述があった。
console.warn( 'THREE.MMDAnimationHelper: The module has been deprecated and will be removed with r172. Please migrate to https://github.com/takahirox/three-mmd-loader instead.' );
しかし、URLを辿って、three-mmd-loader を参照してみたところ、“coming soon"となっており、現時点(2025/8/9)ではまだ何もできていないことがわかった。
こんな状態なので、インターネットから消えるとめんどくさいし、とりあえず適当な場所に、MMD の類が three.js からなくならないうちに r170 のリポジトリごとクローンしておくことにした。
zip.js
(8月あたり?)
以下は、FS | @zip.js/zip.js の引用したもの。一応簡単に必要そうな部分の使い方のメモとして。ただどうすればいいかは、まだ調査できていない。
- 圧縮
const TEXT_CONTENT = "Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat."; const FILENAME = "lorem.txt"; const BLOB = new Blob([TEXT_CONTENT], { type: zip.getMimeType(FILENAME) }); let zipFs = new zip.fs.FS(); zipFs.addBlob("lorem.txt", BLOB); const zippedBlob = await zipFs.exportBlob();
- 展開
zipFs = new zip.fs.FS(); await zipFs.importBlob(zippedBlob); const firstEntry = zipFs.children[0]; const unzippedBlob = await firstEntry.getBlob(zip.getMimeType(firstEntry.name));
(2025/9/13) の追記
なんか結局 zip.js の実装については、調べているうちに、実装する前に調べたとおりにはならなかったので最終的にどうしたか書いておこうかなと思って追記することにした。とは言っても、他にもやりたいことはあるので、労力的な問題でここについては、実際のコードを貼り付けるくらいにしようとは思うが。
実際に実装した感想としては、なんか最初予定していたのと、実際に色々右往左往して動くようにしたコードではまるで実装が違う気がするということであった。zip.fs.FS()
とか使ってなくて、zip.BlobReader
やzip.ZipReader
を使ったりしてるし。
あと、特に windows 環境で圧縮されたであろう MMD 関連の zip ファイルについても ファイル名を Shift-JIS でデコードしないといけないのが、地味に引っかかっていたと思う。ここは完全には解決されてなくて、UTF-8 を一時的に犠牲にする形になってるので、後々なんとかしたいかも。
- import
<script type="text/javascript" src="{{ .Site.BaseURL }}libs/zip.js/dist/zip.min.js"></script>
- 読み込み
function readShiftJISTextFromBlob(ShiftJIStextArrayBlob){
// ChatGPT-5-Mini ---------------------------------------------------------
// TextDecoder が shift-jis に対応している場合
if (typeof TextDecoder !== 'undefined') {
const decoder = new TextDecoder('shift-jis');
return decoder.decode(ShiftJIStextArrayBlob);
} else {
throw new Error('TextDecoder が shift-jis に対応していません');
}
// ChatGPT-5-Mini ---------------------------------------------------------
}
async function readZipForModelFile(modelFile,zipName,fileMap){
let zipBlob = getZipFileRef(modelFile, zipName)
let modelFileName = null
console.log(zipBlob)
let zipBlobReader = new zip.BlobReader(zipBlob);
let zipReader = new zip.ZipReader(zipBlobReader)
for(let entry of await zipReader.getEntries()){
let filename = readShiftJISTextFromBlob(entry.rawFilename)
filename = filename.split("/")[filename.split("/").length - 1]
fileMap.set(filename,URL.createObjectURL(new Blob([await entry.arrayBuffer()])))
if(filename.includes("pmx") || filename.includes("pmd")){
modelFileName = filename
}
console.log(entry)
console.log(filename)
}
if(modelFileName == null){
return null
}
let manager = new THREE.LoadingManager()
manager.setURLModifier((url) => {
let filename = url.split("/").pop().split("\\").pop()
console.log("manager is working")
console.log(filename)
console.log(fileMap.get(filename))
console.log(fileMap.get(filename) || url)
return fileMap.get(filename) || url
})
return {
"modelFileName" : modelFileName,
"manager" : manager
}
}
toon shader と存在しないはずのリソースを参照する問題
結局調べたり問題をなんとかしようとした結果toon-shaderの件と、blob-url-への変換がうまく行かない問題について の話題は、MMDLoader
のコードを読み進めているうちに、どうもつながっているっぽいことがわかっったため、統合して扱うことにした。
(2025/9/27) 時点の記録
とりあえず、MMDLoader.js
の内容を読んでいると次の流れがわかった。
.load("modelfilepath")
でMMDLoader クラスを使った読み込みMeshBuilder
のbuild
関数にパースされたデータが渡されるMaterialBuilder
のbuild
関数にパースされたデータが渡されるMaterialBuilder
内で Toon Shader に関する設定や判定が行われる
以下には、気になったコードの部分について、MMDLoader.js
を置いておく。
- MMDLoader class 内部の気になったところ一部引用
load(){ ... // モデルデータがパースされる const data = modelExtension === 'pmd' ? parser.parsePmd( buffer, true ) : parser.parsePmx( buffer, true ); // MeshBuilder class の build 関数にデータが渡される onLoad( builder.build( data, resourcePath, onProgress, onError ) ); ... }
- MeshBuilder class 内部の気になったところ一部引用
class MeshBuilder { constructor( manager ) { this.crossOrigin = 'anonymous'; this.geometryBuilder = new GeometryBuilder(); this.materialBuilder = new MaterialBuilder( manager ); } ... build( data, resourcePath, onProgress, onError ) { const geometry = this.geometryBuilder.build( data ); const material = this.materialBuilder // MaterialBuilder .setCrossOrigin( this.crossOrigin ) .setResourcePath( resourcePath ) .build( data, geometry, onProgress, onError ); const mesh = new SkinnedMesh( geometry, material ); const skeleton = new Skeleton( initBones( mesh ) ); mesh.bind( skeleton ); // console.log( mesh ); // for console debug return mesh; } ... }
- MaterialBuilder class 内部の気になったところ一部引用
class MaterialBuilder { ... build( data, geometry /*, onProgress, onError */ ) { ...// pmd のとき // gradientMap const toonFileName = ( material.toonIndex === - 1 ) ? 'toon00.bmp' : data.toonTextures[ material.toonIndex ].fileName; params.gradientMap = this._loadTexture( toonFileName, textures, { isToonTexture: true, isDefaultToonTexture: this._isDefaultToonTexture( toonFileName ) } ); ...// pmx のとき // gradientMap let toonFileName, isDefaultToon; if ( material.toonIndex === - 1 || material.toonFlag !== 0 ) { toonFileName = 'toon' + ( '0' + ( material.toonIndex + 1 ) ).slice( - 2 ) + '.bmp'; isDefaultToon = true; } else { toonFileName = data.textures[ material.toonIndex ]; isDefaultToon = false; } params.gradientMap = this._loadTexture( toonFileName, textures, { isToonTexture: true, isDefaultToonTexture: isDefaultToon } ); ... materials.push( new MMDToonMaterial( params ) ); ... } ... _isDefaultToonTexture( name ) { if ( name.length !== 10 ) return false; return /toon(10|0[0-9])\.bmp/.test( name ); } ... _loadTexture( filePath, textures, params, onProgress, onError ) { ... if ( params.isDefaultToonTexture === true ) { let index; try { index = parseInt( filePath.match( /toon([0-9]{2})\.bmp$/ )[ 1 ] ); } catch ( e ) { console.warn( 'THREE.MMDLoader: ' + filePath + ' seems like a ' + 'not right default texture path. Using toon00.bmp instead.' ); index = 0; } fullPath = DEFAULT_TOON_TEXTURES[ index ]; } else { fullPath = this.resourcePath + filePath; } ... } ... }
特に私としては、以下のような部分がすごく気になる。blob url への変換がうまく行かない問題についてのエラーで出ていたファイル名と同じようなことが書かれている。
MMDLoader.js
の特に気になる部分
... const toonFileName = ( material.toonIndex === - 1 ) ? 'toon00.bmp' : data.toonTextures[ material.toonIndex ].fileName; if ( material.toonIndex === - 1 || material.toonFlag !== 0 ) { toonFileName = 'toon' + ( '0' + ( material.toonIndex + 1 ) ).slice( - 2 ) + '.bmp'; isDefaultToon = true; } else { toonFileName = data.textures[ material.toonIndex ]; isDefaultToon = false; } ... ... _isDefaultToonTexture( name ) { if ( name.length !== 10 ) return false; return /toon(10|0[0-9])\.bmp/.test( name ); } ... ... try { index = parseInt( filePath.match( /toon([0-9]{2})\.bmp$/ )[ 1 ] ); } catch ( e ) { console.warn( 'THREE.MMDLoader: ' + filePath + ' seems like a ' + 'not right default texture path. Using toon00.bmp instead.' ); index = 0; } ...
- うまくリソースを読み込めなかったときのエラー
GET blob:https://www.nyanmo.info/toon_%E8%B5%A402.bmp net::ERR_FILE_NOT_FOUND // 赤02.png
blob:https://www.nyanmo.info/h.bmp:1
GET blob:https://www.nyanmo.info/h.bmp net::ERR_FILE_NOT_FOUND
blob:https://www.nyanmo.info/%E8%B6%B3.png:1
GET blob:https://www.nyanmo.info/%E8%B6%B3.png net::ERR_FILE_NOT_FOUND // 足.png
blob:https://www.nyanmo.info/%E9%9D%B4.png:1
GET blob:https://www.nyanmo.info/%E9%9D%B4.png net::ERR_FILE_NOT_FOUND // 靴.png
toon shader
(2025/9/13)
そろそろ、toon shader の問題をなんとかしようと思って、適当に下調べし始めた。正直 three.js と MMD の組み合わせだと、調べてもあんまりこの話題が出てくるって感じじゃないと感じたし、実装できる自信があんまりないというのもあるが、それでも試してみようと思ったのが今日という感じ。
- 気になったところ
調べるというより、three.js r170 の中にある、MMDToonShader.js
についてちょっと見てみたという感じである。それで気になったところをピックアップするということをしようと思う。
// リポジトリからの相対パス
// ./examples/jsm/shaders/MMDToonShader.js
/** * MMD Toon Shader * * This shader is extended from MeshPhongMaterial, and merged algorithms with * MeshToonMaterial and MeshMetcapMaterial. * Ideas came from https://github.com/mrdoob/three.js/issues/19609 * * Combining steps: * * Declare matcap uniform. * * Add gradientmap_pars_fragment. * * Use gradient irradiances instead of dotNL irradiance from MeshPhongMaterial. * (Replace lights_phong_pars_fragment with lights_mmd_toon_pars_fragment) * * Add mmd_toon_matcap_fragment. */ // 何かしらのコード export { MMDToonShader };
コメントを見たり、export に注目している限り、MMDToonShader
を MeshPhongMaterial – three.js docs あたりを参考にこねくり回せば良さそうに見える。
とりあえず、MeshPhongMaterial
のサンプルコードとか調べてみてもいいかもしれないと思った。
toon-shader-と存在しないはずのリソースを参照する問題 を参照
(2025/9/13)
toon shader を適切に設定していなときに出るものだと思われるエラーは以下のようなものがあった。
THREE.WebGLProgram: Shader Error 0 - VALIDATE_STATUS false
Material Name: 柄
Material Type: MMDToonMaterial
Program Info Log: Must have a compiled vertex shader attached:
SHADER_INFO_LOG:
ERROR: 0:306: 'MORPHTARGETS_COUNT' : undeclared identifier
ERROR: 0:306: '' : array size must be a constant integer expression
ERROR: 0:311: 'MORPHTARGETS_TEXTURE_STRIDE' : undeclared identifier
ERROR: 0:311: '*' : wrong operand types - no operation '*' exists that takes a left-hand operand of type 'const highp int' and a right operand of type 'const highp float' (or there is no acceptable conversion)
ERROR: 0:562: 'MORPHTARGETS_COUNT' : undeclared identifier
ERROR: 0:562: '<' : wrong operand types - no operation '<' exists that takes a left-hand operand of type 'highp int' and a right operand of type 'const highp float' (or there is no acceptable conversion)
VERTEX
ERROR: 0:306: 'MORPHTARGETS_COUNT' : undeclared identifier
ERROR: 0:306: '' : array size must be a constant integer expression
ERROR: 0:311: 'MORPHTARGETS_TEXTURE_STRIDE' : undeclared identifier
ERROR: 0:311: '*' : wrong operand types - no operation '*' exists that takes a left-hand operand of type 'const highp int' and a right operand of type 'const highp float' (or there is no acceptable conversion)
ERROR: 0:562: 'MORPHTARGETS_COUNT' : undeclared identifier
ERROR: 0:562: '<' : wrong operand types - no operation '<' exists that takes a left-hand operand of type 'highp int' and a right operand of type 'const highp float' (or there is no acceptable conversion)
301: #endif
302: #endif
303: #ifdef USE_MORPHTARGETS
304: #ifndef USE_INSTANCING_MORPH
305: uniform float morphTargetBaseInfluence;
> 306: uniform float morphTargetInfluences[ MORPHTARGETS_COUNT ];
307: #endif
308: uniform sampler2DArray morphTargetsTexture;
309: uniform ivec2 morphTargetsTextureSize;
310: vec4 getMorph( const in int vertexIndex, const in int morphTargetIndex, const in int offset ) {
311: int texelIndex = vertexIndex * MORPHTARGETS_TEXTURE_STRIDE + offset;
312: int y = texelIndex / morphTargetsTextureSize.x;
three.module.js:20346:14
WebGL warning: linkProgram: Must have a compiled vertex shader attached:
SHADER_INFO_LOG:
ERROR: 0:306: 'MORPHTARGETS_COUNT' : undeclared identifier
ERROR: 0:306: '' : array size must be a constant integer expression
ERROR: 0:311: 'MORPHTARGETS_TEXTURE_STRIDE' : undeclared identifier
ERROR: 0:311: '*' : wrong operand types - no operation '*' exists that takes a left-hand operand of type 'const highp int' and a right operand of type 'const highp float' (or there is no acceptable conversion)
ERROR: 0:562: 'MORPHTARGETS_COUNT' : undeclared identifier
ERROR: 0:562: '<' : wrong operand types - no operation '<' exists that takes a left-hand operand of type 'highp int' and a right operand of type 'const highp float' (or there is no acceptable conversion)
WebGL warning: useProgram: Program must be linked successfully. 31
After reporting 32, no further warnings will be reported for this WebGL context.
ちなみに、three.js 側の MMDToonShader.js
にある具体的な実装を見ても、ひたすらシェーダプログラムが書かれていたり、MeshPhongMaterial
の中身を置き換えようとしているところがあるので、上記のシェーダ関連のエラーとのつながりもありそうに見える。以下は、MMDToonShader.js
の実装の一部分を引用したものである。
const mmd_toon_matcap_fragment = /* glsl */` #ifdef USE_MATCAP vec3 viewDir = normalize( vViewPosition ); vec3 x = normalize( vec3( viewDir.z, 0.0, - viewDir.x ) ); vec3 y = cross( viewDir, x ); vec2 uv = vec2( dot( x, normal ), dot( y, normal ) ) * 0.495 + 0.5; // 0.495 to remove artifacts caused by undersized matcap disks vec4 matcapColor = texture2D( matcap, uv ); #ifdef MATCAP_BLENDING_MULTIPLY outgoingLight *= matcapColor.rgb; #elif defined( MATCAP_BLENDING_ADD ) outgoingLight += matcapColor.rgb; #endif #endif `; // 何かしらのコード const MMDToonShader = { name: 'MMDToonShader', defines: { TOON: true, MATCAP: true, MATCAP_BLENDING_ADD: true, }, uniforms: UniformsUtils.merge( [ ShaderLib.toon.uniforms, ShaderLib.phong.uniforms, ShaderLib.matcap.uniforms, ] ), vertexShader: ShaderLib.phong.vertexShader .replace( '#include <envmap_pars_vertex>', '' ) .replace( '#include <envmap_vertex>', '' ), fragmentShader: ShaderLib.phong.fragmentShader .replace( '#include <common>', ` #ifdef USE_MATCAP uniform sampler2D matcap; #endif #include <common> ` ) .replace( '#include <envmap_common_pars_fragment>', ` #include <gradientmap_pars_fragment> ` ) .replace( '#include <envmap_pars_fragment>', '' ) .replace( '#include <lights_phong_pars_fragment>', lights_mmd_toon_pars_fragment ) .replace( '#include <envmap_fragment>', ` ${mmd_toon_matcap_fragment} ` ) };
(2025/9/13)
ShaderMaterial – three.js docs を読んでいると、MMDToonShader.js
と同じ名前の vertexShader や fragmentShader 、uniforms といった key をもった JSON を渡していることがわかった。
難しく考えなくても、とりあえず、ShaderMaterial に対して export された MMDToonShader
を渡してやれば、あとは、マテリアルをなんとかしてメッシュなりに結びつけて反映させれば良さそうな気もしてきた。
const material = new THREE.ShaderMaterial( { uniforms: { time: { value: 1.0 }, resolution: { value: new THREE.Vector2() } }, vertexShader: document.getElementById( 'vertexShader' ).textContent, fragmentShader: document.getElementById( 'fragmentShader' ).textContent } );
toon-shader-と存在しないはずのリソースを参照する問題 を参照
blob url への変換がうまく行かない問題について
(2025/9/13)
モデルが正しく読み込めない問題として、toon shader 以外に、単純に正しくファイルを読み込めず欠損するというのがある。それで、よく出ているエラーが以下のようなものである。
GET blob:https://www.nyanmo.info/toon_%E8%B5%A402.bmp net::ERR_FILE_NOT_FOUND // 赤02.png
blob:https://www.nyanmo.info/h.bmp:1
GET blob:https://www.nyanmo.info/h.bmp net::ERR_FILE_NOT_FOUND
blob:https://www.nyanmo.info/%E8%B6%B3.png:1
GET blob:https://www.nyanmo.info/%E8%B6%B3.png net::ERR_FILE_NOT_FOUND // 足.png
blob:https://www.nyanmo.info/%E9%9D%B4.png:1
GET blob:https://www.nyanmo.info/%E9%9D%B4.png net::ERR_FILE_NOT_FOUND // 靴.png
正しく blob URL に pmx に書かれているパスが変換された場合は以下のようになる。
mmdviewer/:578 manager is working
mmdviewer/:579 前髪.png
mmdviewer/:580 blob:https://www.nyanmo.info/b12f6356-7b37-45fc-9ed6-8021c66aa610
mmdviewer/:581 blob:https://www.nyanmo.info/b12f6356-7b37-45fc-9ed6-8021c66aa610
ぱっと異常動作例と、正常動作例を見比べて思うこととしては、正常動作しているときは日本語が URL のためにエンコードされていないが、異常動作しているときには、%E8%B6%B3.png
(赤02.png) と思いっきりエンコードされているというのがある。
ここから考えられることとしてはそもそもファイルが読み込めてなくて、見つけられなかった結果、http サーバー側に GET しようとしてそういう挙動になっているのか、ファイルは存在し読み込めているが、どこかでファイル名がエンコードされてしまった結果、実際に存在するリソース名と釣り合わずにエラーになっているのか、という感じがする。
もし、ファイルが見つからなくてこのようなエラーになっているなら、適当に埋め合わせ用のテクスチャを勝手にあてがうようにしたり、欠損している分について手動でテクスチャを投下できるようにしてみてもいいかもしれない。
(2025/9/13)
モデルが正しく読み込めない問題で blob URL への変換がうまく行かないと思っていたことがあるが、それは実はちょっと違うかもしれないことが今回わかった。例えば以下の例では大体、3つほどテクスチャファイルが足りないように見える。
GET blob:https://www.nyanmo.info/toon_%E8%B5%A402.bmp net::ERR_FILE_NOT_FOUND // 赤02.png
blob:https://www.nyanmo.info/h.bmp:1
GET blob:https://www.nyanmo.info/h.bmp net::ERR_FILE_NOT_FOUND
blob:https://www.nyanmo.info/%E8%B6%B3.png:1
GET blob:https://www.nyanmo.info/%E8%B6%B3.png net::ERR_FILE_NOT_FOUND // 足.png
blob:https://www.nyanmo.info/%E9%9D%B4.png:1
GET blob:https://www.nyanmo.info/%E9%9D%B4.png net::ERR_FILE_NOT_FOUND // 靴.png
しかし、実際に元々配布されていた zip ファイル内を確認してみると、そもそもそんな名前のテクスチャは存在せず、脚.png
などにまとめて、足らしきものや、靴らしきものも含まれていることがわかった。
blender 上で動く拡張機能である mmd_tools では、そのような症状はなかったので、内部的になにかしないといけないのかな?と思っているが、具体的にどう実装すればいいかは見当が付いていない。とりあえず mmd_tools のコードをこの問題について注目しながら漁って見ようかなと思ったところ。
toon-shader-と存在しないはずのリソースを参照する問題 を参照
zip 投下時に Shift-JIS のファイル名が付いたモデルしか読み込めない状態になっている件について
(2025/9/13)
正直自動で文字コード識別は不具合の温床な気がするので、とにかく手動で選択できるように実装を変えることにした。どちらかを試せば大体は動くだろうということで、UTF-8とShift-JISを選べるようにした。
関連してそうな記事
参考にしたものや使ったもの
-
Three.js – JavaScript 3D Library
https://threejs.org/ (2025年8月9日) -
three CDN by jsDelivr - A free, fast, and reliable Open Source CDN
https://cdn.jsdelivr.net/npm/three@0.177.0/examples/jsm/ (2025年8月9日) -
three.js でMMDを使う #JavaScript - Qiita
https://qiita.com/shoichi1023/items/6cbaefe078c33f600bfe (2025年8月9日) -
ブラウザ3Dゲームを作る作戦その4: MMDデータを読み込んで動かす - 式姫で遊ぶ日記
https://iwanabot.hatenablog.com/entry/2020/05/01/014258 (2025年8月9日) -
GitHub - takahirox/three-mmd-loader
https://github.com/takahirox/three-mmd-loader (2025年8月9日) -
GitHub - mrdoob/three.js at r170
https://github.com/mrdoob/three.js/tree/r170 (2025年8月9日) -
GitHub - mrdoob/three.js: JavaScript 3D Library.
https://github.com/mrdoob/three.js/tree/dev (2025年8月9日) -
Three.jsこと始め
https://koro-koro.com/threejs-no1/ (2025年8月9日) -
ChatGPT
https://chatgpt.com/ (2025年8月13日) -
Fullscreen API - Web APIs | MDN
https://developer.mozilla.org/en-US/docs/Web/API/Fullscreen_API (2025年8月13日) -
Three.jsでの最適なリサイズ処理 - ICS MEDIA
https://ics.media/tutorial-three/renderer_resize/ (2025年8月13日) -
zip.js - JavaScript library to zip and unzip files
https://gildas-lormeau.github.io/zip.js/ (2025年8月13日) -
GitHub - gildas-lormeau/zip.js: JavaScript library to zip and unzip files supporting multi-core compression, compression streams, zip64, split files and encryption.
https://github.com/gildas-lormeau/zip.js/tree/master (2025年8月13日) -
FS | @zip.js/zip.js
https://gildas-lormeau.github.io/zip.js/api/classes/FS.html (2025年8月13日) -
Media Types
https://www.iana.org/assignments/media-types/media-types.xhtml (2025年8月26日) -
Common media types
https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/MIME_types/Common_types (2025年8月26日) -
Blob: text() method
https://developer.mozilla.org/en-US/docs/Web/API/Blob/text (2025年8月26日) -
FileReader
https://developer.mozilla.org/en-US/docs/Web/API/FileReader (2025年8月26日) -
MeshPhongMaterial – three.js docs
https://threejs.org/docs/?q=MeshPhongMaterial#api/en/materials/MeshPhongMaterial (2025年9月13日) -
URL Encode/Decode (For Japanese language)
https://www.avekt.com/en-us/WebApps/UrlEncodeJapanese (2025年9月13日) -
MeshPhongMaterial - Three.js Tutorials
https://sbcode.net/threejs/meshphongmaterial/ (2025年9月13日) -
sugiany/blender_mmd_tools: mmd_tools is a blender addon for importing Models and Motions of MikuMikuDance.
https://github.com/sugiany/blender_mmd_tools (2025年9月13日) -
ShaderMaterial – three.js docs
https://threejs.org/docs/#api/en/materials/ShaderMaterial (2025年9月13日) -
three.js/src/materials/MeshPhongMaterial.js at master · mrdoob/three.js
https://github.com/mrdoob/three.js/blob/master/src/materials/MeshPhongMaterial.js (2025年9月14日)