diff --git a/book/SUMMARY.md b/book/SUMMARY.md
index adb1f7a..46e7aa4 100644
--- a/book/SUMMARY.md
+++ b/book/SUMMARY.md
@@ -24,6 +24,8 @@
* [惑星形式のmapを作る](city/01_remove.md)
* [橋を作る](city/02_bridge.md)
* [建造物を作る](city/03_house.md)
+* [gameplay camera](camera/README.md)
+ * [もっと近づける](camera/01_close.md)
* [json blueprint utilities](json/README.md)
* [apiから情報を取得する](json/01_varest.md)
* [pixel streaming](pixel/README.md)
@@ -37,7 +39,12 @@
* [dragon ik plugin](plan/06_dragonik.md)
* [unity](unity/README.md)
* [blender](blender/README.md)
- * [モデルをカスタマイズする](blender/01_model.md)
+ * [素体を作る](blender/01_model.md)
+ * [衣装を着せる](blender/02_costume.md)
+ * [vrmで統合する](blender/03_vrm.md)
+* [web](web/README.md)
+ * [glbをwebに表示する](web/01_three.md)
+ * [vrmをwebに表示する](web/02_three-vrm.md)
* [issue](issue/README.md)
* [ue](issue/ue/README.md)
* [error II-E1001](issue/ue/ue_01.md)
diff --git a/book/blender/01_model.md b/book/blender/01_model.md
index 8079b3a..b9c5c53 100644
--- a/book/blender/01_model.md
+++ b/book/blender/01_model.md
@@ -1,22 +1,50 @@
-# モデルをカスタマイズする
+# 素体を作る
-素体を作ります。色々と探したのですが、なかなか納得するものが見つかりません。できる限り現実に近いものを目指します。
+素体を作ります。できる限り現実に近い再現を目指します。これを`gender system`と呼び、わかりやすくいうとキャラクターの性器システムです。
今回は、Aモデル(vrm)とBモデル(部位)の統合をblenderで行います。統合したものをvrmにexportして、ueでimportします。Bモデルにはシェイプキーを設定しているので、ueではモーフターゲットとして動かせるようにします。
-blenderのexportやueのimportは注意が必要です。`VRM MToon`, `ペアレントで[Body]頂点`, `UVMap`, `シェイプキーをSKkeeperで上書き`が必要でした。
+ueで動かすにはmaterialに`VRM MToon`を使うこと、`UVMap`の名前に設定すること、`vrm0`を使うこと、データ転送で`頂点`を設定することが大切です。今回使用する[addon](/blender/)は、vrmをimport, exportするaddonだけです。
-使用する[addon](/blender/)を確認してください。
+## vroidの設定
-1. まずBモデルをblenderで読み込み、いらないものを削除します。vrmを読み込むaddonが必要です。
-2. materialに`VRM MToon`を設定します。ueでimportする際にcrashします。
-3. dataのUVマップに`UVMap_xxx`となっているときは名前を`UVMap`にします。色がおかしくなります。
-4. 編集モードでBモデルを膨らませます。これは頂点を選択して[G]を押します。
-5. addonの`SKkeeper`を使い、オブジェクトメニューからapply modifyを選択します。追加モデルのmirrorやkeyをまとめます。これをしないとvrmにexportできません。
-6. 次にAモデルの編集です。いくつかの面を削除してBモデルをくっつけても違和感ないようにします。
-7. トランスフォームのペアレントで`[Body]頂点`を選択します。G, [y, z, r]などを駆使して位置調整してください。
+素体はvroidをベースにします。つけている衣装を全部はずします。そして、パンツなどもテクスチャを削除しておくと素体の完成です。
-ペアレントは正しく設定されるときもあればズレることもあります。同じファイルをvrmにexportしても結果がその都度異なります。また、モーフターゲットが正しく反映されないこともあります。
+## materialの設定
-私は`[Armatrue]ボーン(hip)`につけることで設定しました。しかし、ueで読み込んだとき少し下に表示されるので、これ自体をずらした位置で調整し、ueで確認する必要がありました。
+vrmをblenderで読み込みます。
+1. Bモデルのmaterialに`VRM MToon`を設定します。ueでimportする際にcrashします。
+2. dataのUVマップに`UVMap_xxx`となっているときは名前を`UVMap`にします。色がおかしくなります。
+
+## 基本的な編集(移動、削除、位置)
+
+1. Bモデルを正しい位置にセットします。[G], [Z], [R]
+2. Aモデルを適切に切り取ります。[面を選択], [細分化], [四角形に], [面を削除]
+3. シェイプキーを動かしても問題ないかの確認します。
+
+
+
+## bodyに追従させる
+
+このままではBモデルは体に追従しません。追従させるには頂点をアーマチュア(armature)に追加します。
+
+1. Bモデルのモディファイアから[データ転送], [Body(J_Bip_C_Hips)], [頂点], [頂点グループ]を選択し、[データレイヤーを生成], [適用]します。
+2. Bモデルのモディファイアでアーマチュアで[Armature]を追加します。
+3. 女性の場合はボーン(足)を調整します。なお、少し浮くようになってしまうため全体を-Zします。[G], [X], [R]
+
+> シェイプキーをueでも有効にするには、vrm0を使います。また、編集しているときにobjectを分離した場合は元の構成に再統合してください。
+
+
+
+## gender systemについて
+
+例外ない限りキャラクターにはgender systemが搭載されます。アイは原作で性別がないので、例外的にgender systemから除外されています。
+
+最初に実装するキャラは、仮名で`アダブ(男性)`, `イブチェ(女性)`になります。
+
+## issue: ueで読み込むと一部のmaterialが正しく表示されない
+
+この現象はblenderで作った`.vrm`をvrm4uで読み込み、一旦、ueを終了するとその後、materialがおかしくなります。
+
+しかし、`vrm1`の出力は正常なので、それを読み込んで生成されたmaterialを`SK_${name}`にセットしています。
diff --git a/book/blender/02_costume.md b/book/blender/02_costume.md
new file mode 100644
index 0000000..66d74b4
--- /dev/null
+++ b/book/blender/02_costume.md
@@ -0,0 +1,11 @@
+# 衣装を着せる
+
+衣装は体に追従しません。これを追従するようにします。
+
+1. 衣装を選択した状態で、モディファイア -> データ転送、頂点を選択し、頂点グループにチェックを入れる。
+2. データレイヤーのボタンを押して頂点を生成。
+3. 最後にモディファイアのデータ転送を適用。
+
+## 衣装を着せるaddon
+
+blenderに[kiseru](https://pielotopica.booth.pm/items/4854979)をインストールします。
diff --git a/book/blender/03_vrm.md b/book/blender/03_vrm.md
new file mode 100644
index 0000000..4867e51
--- /dev/null
+++ b/book/blender/03_vrm.md
@@ -0,0 +1,55 @@
+# vrmで統合する
+
+`.vrm`を読み込んで、別の`.vrm`に統合する方法を紹介します。最終的にはueでも動くようにします。
+
+編集しているうちにblenderのシェイプキーがueのモーフターゲットにexportできなくなったり、髪の毛のウェイトが消えていたりといったトラブルが多発します。
+
+blenderのvrmはspring boneを設定して髪の毛を動かしています。これはboneに付いているcolliderと連動しています。
+
+髪の毛はboneとも連動していて、armature(bone)を統合したあとにheadとheadをつなげなければなりません。
+
+統合するobjectの動きを本体に追従したい場合は頂点とアーマチュアを正しく設定しなければなりません。データ転送を活用します。
+
+髪の毛が動かない場合、物理シュミレーションが設定されているか確認してください。ueの`kawaiiphysics`でも設定できます。私はueのほうで設定しています。また、髪の毛のウェイトが剥がされていないか確認してください。データ転送後は髪の毛にウェイトが付いているか確認しましょう。
+
+シェイプキー(モーフターゲット)が消えてしまうのは`vrm1`に起因します。blenderで編集する場合は`vrm0`をベースにしてください。また、シェイプキーは元のobject名に再統合しなければ消えてしまいます。
+
+1. vrm0をベースにしているか。
+2. objectを元の状態に再統合できているか。
+3. 髪の毛にウェイトはあるか。
+4. 髪の毛に物理シュミレーションはあるか。
+
+## 具体的な手順
+
+hair(髪の毛)とbody(体)で分けられたvrmを読み込むこととします。これを統合します。
+
+1. まず、boneの統合を行います。boneはarmatureというようです。不要なboneを削除します。そして、完成版となるarmatureを最後に選択して統合します。
+2. 次に必要なboneをつなぎます。boneを編集モードで編集し、`J_Bip_C_Head` -> `head`を選択し、右クリックで親を`オフセット維持`で設定します。
+3. 次にhairを選択し、モディファイアからデータ転送を行います。ソースに`body`, 頂点グループに`J_Bip_C_Head`を選択します。ここが特に重要です。そして、頂点データにチェックを入れ、頂点グループを選択します。最後にデータレイヤーを生成し、適用します。これでhairとbodyがつながった状態になりました。bodyが主体になります。
+
+ウェイトがhair(J_Bip_C_Head)に設定されていることを確認してください。今後、hairのobjectをいじると削除される可能性があります。
+
+## ueで物理シュミレーションを設定する
+
+1. 次にueでhairを動くようにします。ueでvrmを読み込んで、`ABP_Post_${name}`を編集します。`kawaiiphysics`でRoot Boneに`J_Sec_Hair_1_xx`を1-12まで入れて、Capsule Limitsに`chest`, `hip`などを設定します。私は`chest`の値を大きめにして対処しました。
+2. 次に、服が体に入りこまないようにしなければなりません。これも先ほどと同じように設定します。
+3. 設定後は`ABP_Post_${name}`をコピーしておきましょう。このファイルは新しくvrmを読み込むと上書きされるため、最新版を読み込んだあと再設定しやすいように。
+
+blenderから読み込んだmodelを動かしてみた。
+
+
+
+## blendshapeとvmc
+
+vmcは`.vrm`をblenderで開いたときblendshape(ブレンドシェイプ)が設定されていれば大抵動くと思います。
+
+ただし、blenderで編集した際に使えなくなってしまう可能性が高いです。例えば、objectを分離した場合がそうです。最初の構成にobjectを統合すると動くようになります。
+
+## issue: 髪の毛が頭に追従しなくなる事がある
+
+激しく動いたときなど、髪の毛と頭が分離してしまうことがありますが、これはウェイトが正しく塗られていないことが原因です。
+
+armature(bone)を統合したとき、`head`,`neck`, `J_Bip_C_Head`, `J_Bip_C_Neck`に混在してウェイトが塗られていることがあります。
+
+例えば、赤く塗るのは`J_Bip_C_Head`だけにしましょう。`耳`と`後頭部の内部(髪色部分)`が重要です。
+
diff --git a/book/blender/README.md b/book/blender/README.md
index 59d5d72..cafc2dc 100644
--- a/book/blender/README.md
+++ b/book/blender/README.md
@@ -2,8 +2,21 @@
[blender](https://blender.org/)でモデルを編集して、ueで読み込みます。
+- version: `4.2`
+
|addon|body|
|---|---|
|https://github.com/saturday06/VRM-Addon-for-Blender|vrmを読み込む|
-|https://github.com/smokejohn/SKkeeper|modifireをobjectに反映|
+## その他のaddon
+
+結局、vrmを読み込むもの以外は使いませんでした。気になったaddonを載せておきます。
+
+|addon|body|
+|---|---|
+|https://github.com/smokejohn/SKkeeper|modifireをobjectに反映|
+|https://github.com/12funkeys/rigid_bodys_gen|rigを付ける|
+|https://github.com/shteeve3d/blender-wiggle-2|rigを付ける|
+|https://pielotopica.booth.pm/items/4854979|衣装を着せる|
+
+なお、pythonを実行するのは危険も伴いますので、blender addonの実行(インストール)は最低限にしてましょう。
diff --git a/book/camera/01_close.md b/book/camera/01_close.md
new file mode 100644
index 0000000..100ec59
--- /dev/null
+++ b/book/camera/01_close.md
@@ -0,0 +1,7 @@
+# もっと近づける
+
+GASPにはマウスのDown/Upのキーでカメラ操作が設定されているので、それを改造します。今回は`Style:Close`でもっと近づけるようにします。
+
+例えば、`GamePlay Camera`から`Get Initial Variable Table`を`Set Camera Rig Parameters`につなげます。そして、`Close_Strafe`を選択し、`/Content/Blueprints/Camera/CameraAsset_SandboxCharacter`にある`Close_Strafe`のOffsetを公開します。公開するにはnodeを伸ばして変数を作ればokです。
+
+これで`/Content/Blueprints/CBP_SandboxCharacter`から値をいれることができます。ピンを分割して、x軸に`-70.0`を入れます。
diff --git a/book/camera/README.md b/book/camera/README.md
new file mode 100644
index 0000000..b5bc7d1
--- /dev/null
+++ b/book/camera/README.md
@@ -0,0 +1,5 @@
+# gameplay camera
+
+[gameplay camera](https://dev.epicgames.com/documentation/ja-jp/unreal-engine/gameplay-camera-system)
+
+キャラクターブループリント(Character BP)とカメラブループリント(Camera BP)の分離(共通化)を図ります。
diff --git a/book/default/00_issue.md b/book/default/00_issue.md
deleted file mode 100644
index 8b13789..0000000
--- a/book/default/00_issue.md
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/book/default/01_start.md b/book/default/01_start.md
index 8e63846..9d95826 100644
--- a/book/default/01_start.md
+++ b/book/default/01_start.md
@@ -38,18 +38,23 @@ ueを長く使っているとわかることですが、ゲーム制作にはあ
## unityやblenderも使っている
-私はキャラクター(character)を`.vrm`で作っています。
+私はキャラクター(character)を[vroid](https://vroid.com/studio)で作っています。
-`.vrm`の開発は主に[unity](/unity/)が中心です。`.vrm`の仕様を作っているpixivがunityで読み込むためのpluginを開発しているからです。そのためかvrmに対応する多くのツールはunity専用です。
+そして、`.vrm`の開発は主に[unity](/unity/)が中心です。なぜなら`.vrm`の仕様を作っているpixivがunityで読み込むための[plugin](https://github.com/vrm-c/UniVRM)を開発しているからです。そのためかvrmに対応する多くのツールはunity専用です。
もし`.vrm`を扱う場合は、どちらにせよ`unity`を使うことになると思います。私もモデルのカスタマイズなどでunityを使っています。
-モデルのカスタマイズには[blender](/blender)というものもあって、`blender`も使用しています。
+また、モデリングには[blender](/blender)を使用しています。
-つまり、本当にueでゲームを制作すべきかはよく考えなければいけません。ゲーム開発は使用するものが少ないほど安定します。
+つまり、本当にueでゲームを制作すべきかはよく考えなければいけません。ゲーム開発は使用するツールが少ないほど安定します。
結局、unityやblenderを使うことになるなら、unityで開発するのがオススメです。情報量、安定性、ツールの豊富さなどからそう判断します。
+```diff
++ [vroid] --> [vrm] --> [blender] --> [unity]
+- [vroid] --> [vrm] --> [blender] --> [unity] --> [ue]
+```
+
## ueを使う理由
私がueを使う理由は、最初にゲーム開発を始めたとき使ったものがueだったからです。その時の感動は忘れません。
diff --git a/book/default/README.md b/book/default/README.md
index 1dca1f8..6a0c038 100644
--- a/book/default/README.md
+++ b/book/default/README.md
@@ -20,6 +20,15 @@ ue5でゲームを作成するまでの過程をまとめます。
ただし、ストレージの読み書き速度は重要です。ueやprojectを入れるストレージには注意してください。
+## 便利なサイト
+
+|name|body|
+|---|---|
+|[dev.epicgames.com](https://dev.epicgames.com/community/)|開発者コミュニティ|
+|[perplexity.ai](https://www.perplexity.ai/)|検索エンジン|
+|[suno.com](https://suno.com/)|作曲|
+|[vroid.com](https://vroid.com/studio/)|3Dモデル|
+
## 使用するタグ
### youtube
diff --git a/book/img/0007.png b/book/img/0007.png
new file mode 100644
index 0000000..cd3d0db
Binary files /dev/null and b/book/img/0007.png differ
diff --git a/book/img/0008.png b/book/img/0008.png
new file mode 100644
index 0000000..acfd078
Binary files /dev/null and b/book/img/0008.png differ
diff --git a/book/img/0009.png b/book/img/0009.png
new file mode 100644
index 0000000..dcadee0
Binary files /dev/null and b/book/img/0009.png differ
diff --git a/book/img/0010.png b/book/img/0010.png
new file mode 100644
index 0000000..382075f
Binary files /dev/null and b/book/img/0010.png differ
diff --git a/book/img/0011.png b/book/img/0011.png
new file mode 100644
index 0000000..d12de9d
Binary files /dev/null and b/book/img/0011.png differ
diff --git a/book/img/0012.png b/book/img/0012.png
new file mode 100644
index 0000000..897cf35
Binary files /dev/null and b/book/img/0012.png differ
diff --git a/book/img/0013.png b/book/img/0013.png
new file mode 100644
index 0000000..674da8d
Binary files /dev/null and b/book/img/0013.png differ
diff --git a/book/plan/03_ocean.md b/book/plan/03_ocean.md
index a13b22a..464b03b 100644
--- a/book/plan/03_ocean.md
+++ b/book/plan/03_ocean.md
@@ -19,3 +19,10 @@
> WaterVolume: 空間にロードされていないアクタを参照しています。
これは`BP_EarthSizedOcean`, `BP_EarthSizedSphericalMesh`の詳細から`is spatially loaded`のチェックを外します。
+
+## ocean wavesが遅い理由
+
+これは`/Content/OceanWaves/Blueprints/BP_SphericalMesh`の`CreateMeshAsyncIteration`にある`Set Timer for Next Tick by Event`が原因です。
+
+しかし、取り除くと海が消えます。ロードを待つほかありません。
+
diff --git a/book/plan/README.md b/book/plan/README.md
index 5119203..76f2047 100644
--- a/book/plan/README.md
+++ b/book/plan/README.md
@@ -8,8 +8,8 @@
|[logicdriver](https://www.fab.com/ja/listings/a3e2fc1f-2aaa-49a1-bc26-4d5d38f7d82f)|3|20241212|ロジックのシステム|[docs](https://logicdriver.com/docs/pages/prochangelog/)|
|[dragon ik plugin](https://www.fab.com/ja/listings/d3f8d256-d8d9-4d27-91c1-c61e55e984a6)|3|20250122|animをターゲットに追従|[docs](https://dragonik.eternalmonke.com/)|
|[superhero flight animations](https://www.fab.com/ja/listings/41185c19-5191-4153-8293-8cc9901efa95)|2|20241217|空を飛ぶシステム||
-|[clazy runner action pack](https://www.fab.com/ja/listings/3f4f4475-c9d1-46e8-992c-4a8f3aff58ed)|2|20240518|ダッシュのシステム||
-|[rts camera system](https://www.fab.com/ja/listings/d063c9c9-6df3-4eeb-b7f3-797b5507379e)|2|20231123|カメラシステム|[docs](https://docs.google.com/document/d/e/2PACX-1vQevvLmKDkw0Z6okftdwGCTTLYOYFO-71mvyV6co4wN8oMuJFJDQAGXCx4CckKcpd-FQEUz5i8QHeQS/pub)|
+|[clazy runner action pack](https://www.fab.com/ja/listings/3f4f4475-c9d1-46e8-992c-4a8f3aff58ed)|1|20240518|ダッシュのシステム||
+|[rts camera system](https://www.fab.com/ja/listings/d063c9c9-6df3-4eeb-b7f3-797b5507379e)|1|20231123|カメラシステム|[docs](https://docs.google.com/document/d/e/2PACX-1vQevvLmKDkw0Z6okftdwGCTTLYOYFO-71mvyV6co4wN8oMuJFJDQAGXCx4CckKcpd-FQEUz5i8QHeQS/pub)|
|[space frontier stations & ships](https://www.fab.com/listings/79323b40-dc56-43bf-8aea-b13800266329)|1|20241114|宇宙船のシステム|[docs](https://docs.google.com/document/d/1p5MY13cpTlVtqP7sCQpAoE_k5VHklmndZ4cHeBy105Y/)|
|[replicated interaction kit vol 3](https://www.fab.com/ja/listings/3ce13688-fd10-462f-b90d-964c85a090ad)|1|20241115|椅子に座るシステム||
|[ocean waves](https://www.fab.com/ja/listings/bfb5c9f8-4e57-4cbe-8273-c88540965412)|1|20241217|惑星の海を作る|[docs](https://www.youtube.com/playlist?list=PLvRH9sjZUdXfuLpvStHfpKyESQoOq28R7)|
@@ -31,9 +31,7 @@ mapを見ていると買いたくなるのはわかります。しかし、map
- 高いassetを購入しない
-高いものには良いものもあります。が、良くないもの、使えないものもたくさんあります。最初は手を出すべきではありません。安くて良いassetがたくさんあります。高いものより結局そっちを使うことになることのほうが多い。
-
-ある程度わかってきたら購入するのはいいかもしれません。その際は独自性を見るようにします。
+高いものには良いものもあります。が、良くないもの、使えないものもたくさんあります。最初は手を出すべきではありません。安くて良いassetがたくさんあります。高いものより結局そっちを使うことになることのほうが多いです。ある程度わかってきたら購入するのはいいかもしれません。その際は独自性を見るようにしています。
- 長らく更新されているassetを購入する
@@ -45,5 +43,5 @@ assetを購入する際は必ず作者を確認してください。他にどの
- 機能をまとめる
-使うassetは少なければ少ないほど良いです。依存関係は少なくしましょう。`epic games`が提供するdefault(デフォルト)のものだけで作るのが一番ですが、それだけでは時間がかかったり、クオリティが低かったりします。そんなとき有料assetに頼ることになります。しかし、「使うassetを少なく」は有効です。シンプルな設定で使えるもの、たくさんの機能をまとめて使えるものがオススメです。
+使うassetは少なければ少ないほど良いです。依存関係は少なくしましょう。`epic games`が提供するdefault(デフォルト)のものだけで作るのが一番ですが、それだけでは時間がかかったり、クオリティが低かったりします。そんなとき有料assetに頼ることになります。しかし、「使うassetを少なくする」のは有効です。シンプルな設定で使えるもの、たくさんの機能をまとめて使えるものがオススメです。
diff --git a/book/web/01_three.md b/book/web/01_three.md
new file mode 100644
index 0000000..0eafc4d
--- /dev/null
+++ b/book/web/01_three.md
@@ -0,0 +1,213 @@
+# three.js
+
+reactの`tsx`で書きます。
+
+```sh
+$ npx create-react-app galaxy --template typescript
+```
+
+これで準備はできましたが、設定ファイルを見てみます。
+
+> tsconfig.json
+
+```json
+{
+ "compilerOptions": {
+ "target": "es5",
+ "lib": [
+ "dom",
+ "dom.iterable",
+ "esnext"
+ ],
+ "allowJs": true,
+ "skipLibCheck": true,
+ "esModuleInterop": true,
+ "allowSyntheticDefaultImports": true,
+ "strict": true,
+ "forceConsistentCasingInFileNames": true,
+ "noFallthroughCasesInSwitch": true,
+ "module": "esnext",
+ "moduleResolution": "node",
+ "resolveJsonModule": true,
+ "isolatedModules": true,
+ "noEmit": true,
+ "jsx": "react-jsx"
+ },
+ "include": [
+ "src"
+ ]
+}
+```
+
+`es5`, `react-jsx`を使っているようです。
+
+buildなどはwebpackではなく`react-scripts`ですね。
+
+> package.json
+
+```json
+ "scripts": {
+ "start": "react-scripts start",
+ "build": "react-scripts build",
+ "test": "react-scripts test",
+ "eject": "react-scripts eject"
+ }
+```
+
+> src/App.tsx
+
+```ts
+import React from 'react';
+import './App.css';
+import { ThreeFiberGalaxy } from './pages/galaxy';
+
+function App() {
+ return
+}
+
+export default App;
+```
+
+> src/pages/galaxy.tsx
+
+```ts
+// https://gist.github.com/artokun/fb7f0c68a01ba5d9813abb3ccce254c4
+
+import * as THREE from 'three'
+import { Points, useGLTF } from '@react-three/drei'
+import { GLTF } from 'three-stdlib'
+import { useFrame, Canvas } from '@react-three/fiber'
+import { useMemo, useRef } from 'react'
+import { EffectComposer, SelectiveBloom } from '@react-three/postprocessing'
+
+type GLTFResult = GLTF & {
+ nodes: {
+ Object_2: THREE.Mesh
+ }
+ materials: {
+ ['Scene_-_Root']: THREE.PointsMaterial
+ }
+}
+
+export function Galaxy(props: JSX.IntrinsicElements['group']) {
+ const ref = useRef(null!)
+ const galaxyCenterLightRef = useRef(null!)
+ const { nodes } = useGLTF('./models/galaxy.glb') as GLTFResult
+ const [positions, colors] = useMemo(() => {
+ nodes.Object_2.geometry.center()
+ const positions = new Float32Array(
+ nodes.Object_2.geometry.attributes.position.array.buffer
+ )
+ const colors = new Float32Array(positions.length)
+
+ const getDistanceToCenter = (x: number, y: number, z: number) =>
+ Math.sqrt(x * x + y * y + z * z)
+
+ // make colors closer to 0,0,0 be more reddish and colors further away be more blueish
+ const color = new THREE.Color()
+ for (let i = 0; i < positions.length; i += 3) {
+ const x = positions[i]
+ const y = positions[i + 1]
+ const z = positions[i + 2]
+ const distanceToCenter = getDistanceToCenter(x, y, z)
+ const normalizedDistanceToCenter = distanceToCenter / 100
+
+ // make colors closer to 0,0,0 be more reddish and colors further away be more blueish (do not use hsl)
+ // color.setHSL(
+ // (0.15 * (0.21 + Math.cos(distanceToCenter * 0.02))) / 2,
+ // 0.75,
+ // 0.6
+ // )
+ color.setRGB(
+ Math.cos(normalizedDistanceToCenter),
+ THREE.MathUtils.randFloat(0, 0.8),
+ Math.sin(normalizedDistanceToCenter)
+ )
+ color.toArray(colors, i)
+ }
+
+ return [positions, colors]
+ }, [nodes])
+ //const starTexture = useLoader(THREE.TextureLoader, '/star.png')
+
+ // slowly rotate the galaxy
+ useFrame(({ clock }) => {
+ ref.current.rotation.z = clock.getElapsedTime() / 5
+ // zoom in and out
+ // ref.current.scale.setScalar(Math.sin(clock.getElapsedTime() / 2) + 1.5)
+ })
+
+ // make particles glow
+
+ return (
+
+
+
+
+
+
+
+
+
+ )
+}
+
+useGLTF.preload('./models/galaxy.glb')
+```
+
+`App.tsx`で読み込むため以下を追記します。
+
+> src/pages/galaxy.tsx
+
+```ts
+export const ThreeFiberGalaxy = () => {
+ return (
+
+ )
+}
+```
+
+`.glb`は[こちら](https://sketchfab.com/3d-models/need-some-space-d6521362b37b48e3a82bce4911409303)からダウンロードして、`public/models/galaxy.glb`に置いてください。
+
+```sh
+$ npm run start
+```
+
+controlを追加します。
+
+```sh
+import { Points, useGLTF, OrbitControls } from '@react-three/drei'
+
+export const ThreeFiberGalaxy = () => {
+ return (
+
+ )
+}
+```
+
diff --git a/book/web/02_three-vrm.md b/book/web/02_three-vrm.md
new file mode 100644
index 0000000..8dac41d
--- /dev/null
+++ b/book/web/02_three-vrm.md
@@ -0,0 +1,212 @@
+# three-vrm
+
+今回は`react-three-fiber`と`three-vrm-animation`を使います。
+
+react-three-fiberはsceneなどを自動でやってくれて、コードもシンプルになります。
+
+> `react-three-fiber`で書く場合に`.vrma`の動きがおかしくなりました。私の環境では`unity + vrm 1.0`でexportしたものを使うと正常に動きました。
+
+```sh
+$ npx create-react-app vrm-model --template typescript
+$ npm i
+$ npm run start
+```
+
+> src/pages/vrm-model.tsx
+
+```ts
+import * as THREE from 'three'
+import React, { useState, useEffect, useRef } from 'react';
+import { OrbitControls } from '@react-three/drei'
+import { useFrame, Canvas } from '@react-three/fiber';
+import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader';
+import { VRM, VRMUtils, VRMLoaderPlugin } from '@pixiv/three-vrm';
+import { VRMAnimationLoaderPlugin, VRMAnimation, createVRMAnimationClip } from "@pixiv/three-vrm-animation";
+
+interface ModelProps {
+ url: string
+ url_anim: string
+}
+
+const VRMModel: React.FC = ({ url, url_anim }) => {
+
+ const [vrm, setVrm] = useState(null);
+ const mixerRef = useRef(null);
+
+ useEffect(() => {
+ const loader = new GLTFLoader();
+ loader.register((parser) => new VRMLoaderPlugin(parser));
+ loader.register((parser) => new VRMAnimationLoaderPlugin(parser));
+ loader.load(url, (gltf) => {
+ const vrmModel = gltf.userData.vrm as VRM;
+ VRMUtils.removeUnnecessaryJoints(vrmModel.scene);
+ setVrm(vrmModel);
+ const mixer = new THREE.AnimationMixer(vrmModel.scene);
+ mixerRef.current = mixer;
+ loader.load(url_anim, (animGltf) => {
+ const vrmAnimations = animGltf.userData.vrmAnimations as VRMAnimation[];
+ if (vrmAnimations && vrmAnimations.length > 0) {
+ const clip = createVRMAnimationClip(vrmAnimations[0], vrmModel);
+ mixer.clipAction(clip).play();
+ }
+ });
+ });
+ }, [url, url_anim]);
+
+ useFrame((state, delta) => {
+ if (mixerRef.current) mixerRef.current.update(delta);
+ if (vrm) vrm.update(delta);
+ });
+
+ return vrm ? : null;
+};
+
+export const VRMModelCanvas = () => {
+ return (
+
+
+
+
+ )
+}
+export default VRMModelCanvas;
+```
+
+重要な箇所は以下のポイントです。
+
+```html
+
+```
+
+ファイルはこの場所から読み込みますので、ダウンロードする場合は[.vrm](https://vroid.pixiv.help/hc/ja/articles/31627266179865-%E3%82%B5%E3%83%B3%E3%83%97%E3%83%AB%E3%83%A2%E3%83%87%E3%83%AB%E3%81%AE%E5%88%A9%E7%94%A8%E6%96%B9%E6%B3%95%E3%81%AB%E3%81%A4%E3%81%84%E3%81%A6%E7%9F%A5%E3%82%8A%E3%81%9F%E3%81%84), [.vrma](https://booth.pm/ja/items/5512385)をここに置きます。
+
+> src/App.tsx
+
+```ts
+import React from 'react'
+import VRMModelCanvas from './pages/vrm_model'
+
+const App = () => {
+ return (
+
+ )
+}
+
+export default App;
+```
+
+> package.json
+
+```json
+{
+ "name": "vrm-model",
+ "version": "0.1.0",
+ "private": true,
+ "dependencies": {
+ "@pixiv/three-vrm": "^3.0.0",
+ "@pixiv/three-vrm-animation": "^3.0.0",
+ "@react-three/drei": "^9.109.2",
+ "@react-three/fiber": "^8.16.8",
+ "@react-three/postprocessing": "^2.16.2",
+ "@testing-library/jest-dom": "^5.17.0",
+ "@testing-library/react": "^13.4.0",
+ "@testing-library/user-event": "^13.5.0",
+ "@types/jest": "^27.5.2",
+ "@types/node": "^16.18.104",
+ "@types/react": "^18.3.3",
+ "@types/react-dom": "^18.3.0",
+ "@types/three": "^0.167.1",
+ "react": "^18.3.1",
+ "react-dom": "^18.3.1",
+ "react-scripts": "5.0.1",
+ "three": "^0.167.1",
+ "three-stdlib": "^2.30.5",
+ "typescript": "^4.9.5",
+ "web-vitals": "^2.1.4"
+ },
+ "scripts": {
+ "start": "react-scripts start",
+ "build": "react-scripts build",
+ "test": "react-scripts test",
+ "eject": "react-scripts eject"
+ },
+ "eslintConfig": {
+ "extends": [
+ "react-app",
+ "react-app/jest"
+ ]
+ },
+ "browserslist": {
+ "production": [
+ ">0.2%",
+ "not dead",
+ "not op_mini all"
+ ],
+ "development": [
+ "last 1 chrome version",
+ "last 1 firefox version",
+ "last 1 safari version"
+ ]
+ }
+}
+```
+
+> tsconfig.json
+
+```json
+{
+ "compilerOptions": {
+ "target": "es5",
+ "lib": [
+ "dom",
+ "dom.iterable",
+ "esnext"
+ ],
+ "allowJs": true,
+ "skipLibCheck": true,
+ "esModuleInterop": true,
+ "allowSyntheticDefaultImports": true,
+ "strict": true,
+ "forceConsistentCasingInFileNames": true,
+ "noFallthroughCasesInSwitch": true,
+ "module": "esnext",
+ "moduleResolution": "node",
+ "resolveJsonModule": true,
+ "isolatedModules": true,
+ "noEmit": true,
+ "jsx": "react-jsx"
+ },
+ "include": [
+ "src"
+ ]
+}
+```
+
diff --git a/book/web/README.md b/book/web/README.md
new file mode 100644
index 0000000..eb6adb6
--- /dev/null
+++ b/book/web/README.md
@@ -0,0 +1,13 @@
+# web
+
+webで動くゲームを開発する技術を紹介します。
+
+3Dモデルには様々な形式があります。ここでは`.glb`, `.vrm`を扱います。
+
+これらをwebに表示することができます。例えば、[tyrano builder](https://b.tyrano.jp/)と組み合わせノベルゲームを作成できます。
+
+|url|body|
+|---|---|
+|https://github.com/mrdoob/three.js/|3Dモデルをwebに表示|
+|https://github.com/pixiv/three-vrm/|vrmをthree.jsで扱う|
+