プログラムでデザインする布(シミュレーションなし)
出典:room-at-dawn(MIT)、src/scene/parts/bed.js の布団、毛布、枕。ここではそれを再利用可能なライブラリに分割し、段階的に確認できるサンプルも付けています。
コアアイデア:布は「シミュレート」するのではなく「デザイン」する。
平面上で「各点の布がサポートよりどれだけ高いか」を定義し、その平面をサポートにラップします。
利点は、各ステップが制御可能で再現可能(同じシードで毎回同じ)、0.5秒未満で構築でき、他のオブジェクト(本、スマホ、別の毛布)も同じ高さ関数を直接参照して布の表面にフィットさせられることです。
代償として布は静的です:キャラクターに押されたりしません(インタラクティブが必要な場合は references/engines.md を参照)。
ファイル
| パス | 内容 |
|---|---|
scripts/cloth.js | ライブラリ(ES module、依存はthreeのみ):RNG/ノイズ、Field、塊、折り目ファミリー、しわ、サポートドレープ、角めくり、メッシュ、UV 平坦化、しわテクスチャ、プリント、コットンマテリアル、exportableMesh(GLB エクスポート前の変換) |
example/index.html + main.js | ベッドにかけられた整っていない布団、?stage=1..5 で各エフェクトを段階的に追加 |
scripts/curtain.js | カーテン:curtainPanel(伸長しない断面積分)、auditPanel、linenTextures(リネン平織り)、curtainMaterial(縫い目、ステッチ、収納折り目、逆光透過、微振動)、curtainRing |
example/curtain.html | 窓の前のカーテンペア、?cam=(front、angle、top、hem、stitch、above)、?tied=1、?fullness=、?loops=、?tex=checker |
example/preview-*.jpg | サンプルの完成図と各段階図(preview-curtain.jpg はカーテン) |
references/shape.md | 高さ場デザイン:塊、長い折り目、ピーク削減、しわ、平坦領域 |
references/drape-curl.md | サポート、傾倒、平面のねじれ、角めくり、縫い目、布の上に物を置く、2層目の布の重ね |
references/uv-material.md | UV 平坦化、細かいしわの法線マップ+空洞マップ、プリント、マテリアル |
references/verification.md | 確認すべきデータ、カメラアングル、よくある問題の対処法 |
references/curtain.md | カーテン:断面積分と幅の求解、θ(s) の構成、マテリアル、パラメータ、検証、遭遇した問題 |
references/engines.md | Godot/Unreal への移植:GLB エクスポート、GDScript/C++ への書き換え、インタラクティブ布の連携方法 |
サンプルの実行
cd <スキルフォルダ> && python3 -m http.server 5420 --bind 127.0.0.1
# ブラウザで http://127.0.0.1:5420/example/ を開く
(<スキルフォルダ> はこのスキル読み込み時に表示される Base directory;プラグインとしてインストールした場合は ~/.claude/plugins/cache/ 以下にあります)
URL パラメータ:
stage=1..5:塊、長い折り目追加、ピーク削減追加、しわ追加、角めくりと傾倒追加。cam=wide|close|top|side|left|foot:カメラプリセット。tex=checker:10cm 市松模様に切り替え、UV 伸びをチェック。relax=0:UV 平坦化をオフ、比較用。ui=0:データパネルを非表示、スクリーンショット時に使用。
完了後、window.__cloth に検証データがセットされ、body[data-ready="1"] はスクリーンショット可能を示します。画面上の「Download GLB」ボタンで布を他のエンジンにエクスポートできます。
フロー
| ステップ | 処理内容 | 理由 | ライブラリ関数 |
|---|---|---|---|
| 1 | 平面座標で輪郭(四角形)と「縫い目までの距離」を定義 | 縫い目、しわのフェードアウト、角めくりに必要 | quadEdge |
| 2 | 塊:手動配置の5~10個の楕円、それぞれ高さと角度を持ち、smooth-maxで結合 | 大まかな形状が「布団の状態」を決める。ノイズでは表現できない | makeLobes lobesAt |
| 3 | 長い折り目:2~3組の方向性のあるbillowノイズ、領域に応じて方向を変える | 布団の折り目は引っ張られる方向に沿う | foldFamily |
| 4 | 垂れ下がる部分に縦方向の波;全体にdomain warp;soft capで高さ制限 | 垂布は平らではない;定規で描いたような直線を避ける;一番高い塊を誇張しすぎない | softCap |
| 5 | ピーク削減:各点の高さは(2回ぼかした)周囲より約1.2cm以下に抑え、谷は鋭く保つ | 中綿入りの布には尖ったピークがない;これで「山」から「布団」になる | Field.erodePeaks |
| 6 | しわを一本ずつ描画:クランプ点から扇状に放射、その他はテンションフィールドに沿って分散、平坦領域と縫い目付近で減衰 | ノイズでしわを作ると大理石の模様のようになる。「テント形断面」の線分が必要 | CreaseSet crease |
| 7 | 縫い目:平らな帯、ステッチの溝、縮み | エッジは実在の幾何であり、近づいても信頼できる必要がある | サンプル hemElev |
| 8 | サポートにラップ:平坦領域+角丸+垂直下方向 | 一枚の平面でベッド上とベッドサイドを同時に表現できる | boxSupport |
| 9 | 高い折り目を片側に倒す:倒す距離はその場所の高さに依存 | 膨らみは片側が急で片側が緩やかになり、対称な饅頭のようにはならない | サンプル place |
| 10 | 角めくり:折り線を超えた部分をまず円筒に沿って巻き上げ、次に布団の上に平らに置く;押さえられた領域は平坦領域に設定 | ストーリー性のあるディテールを追加 | curl |
| 11 | 3D曲面に従ってUVを平坦化(マルチグリッド、エッジ長制約) | 急な折り目でプリントが伸びないようにする | relaxUV uvStretch |
| 12 | マテリアル:繰り返し可能な細かいしわの法線マップ(alphaに空洞マップ)+プリント+sheen | ジオメトリはcm単位、テクスチャはmm単位 | wrinkleTexture sprigPrintTexture cottonMaterial |
必ず守ること
- 乱数は必ず
RNG(seed)/pn(...,seed)を使い、Math.random()は使用しない。 スクリーンショットの回帰テスト、ベイクドライティング、多人同期はこれに依存する。 - 大きな形状は3次補間でサンプリング(
Field.cubic)、しわはバイリニア(Field.bil)。 大きな形状にバイリニアを使うと、メッシュ上に5mmの格子面が見える。 - しわをノイズで作らない。 ノイズは塊状になる;実際のしわは方向性があり、両端が細くなる線。
- 先にピーク削減、その後しわを追加。 順序が逆だと、しわの稜線も削られてしまう。
- 角めくりの下の平坦領域は、めくられた範囲全体をカバーする必要がある(サンプルでは
QMAXで自動計算)。カバー不足だと、めくられた部分が高い塊の上に重なり、模様が帯状に伸びる(実際に発生、詳細はreferences/verification.md)。 - UV平坦化は「めくる前」の形状 U に対して行う。 めくられた部分の伸びは別途チェックする。
relaxUVはグリッド数が8の倍数であることを要求する。 - 垂布は下のものに衝突してはならない: サンプルではベッドフレームをマットレスより狭くしている。サポート以外の衝突はこの手法の対象外。
- 隣接する2辺が同時に垂れる場合、コーナーにはテーパーまたは三角布が必要になるが、
boxSupportは処理しない。 対向する2辺のみを垂らすこと。
数値の出発点(サンプル実測)
- 布団の平置き厚さ 5cm、塊の高さ ×
LOBE_SCALE(0.3 は普通に寝た跡、1 は大きく盛り上がった状態)、最高 soft cap 23cm。 - 長い折り目の間隔約 12cm、長さ約 60cm、振幅 3–4cm;ピーク削減半径 5 セル(2.5cm)、上限 1.2cm。
- しわの幅 2.4–4cm、高さは幅の約 0.16–0.3 倍、30% は溝;各クランプ点から 4–7 本、他に 200 組を分散配置。
- メッシュ約 7mm(288×192)、約 13 万トライアングル、モデリング時間約 0.45秒(うち UV平坦化約 0.15秒)、貫通 0、UV伸び p5/p95 約 0.92/1.08。
カーテン(吊り下げられた布)
布団の手法は「高さ場をサポートにラップする」ですが、カーテンには適用できません。カーテンでは、各水平断面は長さが固定された線であり、布に沿って進行角度 θ(s) を積分して描画し、二分法で θ の倍率を調整して、その列がその高さでの必要な幅にちょうど一致するようにします。布は伸びないため、UVは直接メートル単位で、テクスチャの変形はありません。詳細は references/curtain.md を参照。
import { curtainPanel, auditPanel, linenTextures, curtainMaterial, curtainRing, CU } from './scripts/curtain.js';
const p = curtainPanel({ x0t: 0.27, x1t: 0.95, x0h: 0.25, x1h: 0.97, zc: 0.12, yTop: 2.29, yHem: 0.02, loops: 7, seed: 4101 });
const lin = linenTextures();
scene.add(new THREE.Mesh(p.geo, curtainMaterial({ map: lin.map, normalMap: lin.normalMap, yTop: 2.29 })));
console.log(auditPanel(p)); // crossings が 0 であること;stretchP95Pct が 1 未満であること
- サンプル実測:1枚約10万トライアングル、0.2秒で構築、伸び p95 は 0.4–0.7 %、交差 0。
- 注意:
depthを締めすぎると、布が 5–15 % 短くなる。スペースが足りない場合はfullnessを下げ、深さを押さえないこと。 - 注意:裾を狭くしすぎると、折り目同士が貫通することがある。しきい値はシードに依存する(サンプルの右パネルでは約 0.67 倍でも交差あり、0.79 倍で合格)。
crossingsを確認し、必要に応じてシードを変更すること。 - デフォルトの
depth[-0.11, 0.16] では布は短くならないが、竿は壁から約 12cm 離す必要がある。