3d-paper-material について
3D シーン向けの紙マテリアルを作るグラフィックススキルです。繊維の高さ、裂けた輪郭、厚みの層、裏側からの光の透過を組み合わせます。複数の紙種、マクロ表示、生成手順をデモで示します。
主な機能
- 01繊維と紙の形成テクスチャをベイク
- 02毛羽立つ繊維を含む裂けマスクを描画
- 03オフセットした層で厚みを表現
- 04紙の構造に応じて裏光の透過を計算
- 05紙種の切り替えとマクロ表示
活用シーン
- 013D シーンで和紙やコットン紙を描画
- 02裂けた繊維の縁を持つ紙モデルを制作
- 03透過光や繊維に沿うインクのにじみを表現
- 04クローズアップで紙の繊維を確認
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ mengto/ skills --skill 3d-paper-materialインストール後、Claude Code で/3d-paper-materialを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
demo.htmlPROMPT.mdagents/openai.yaml詳細
3D 紙素材
紙とは、破れた輪郭で切り取られた繊維のフィールドです。漉き込まれた繊維と地合いの濃淡を表すハイトマップが表面を作り、引き抜かれた繊維を含むアルファマスクが縁を作り、オフセットした複製が厚みを与え、裏側からの光は各ピクセルの地合いの薄さに応じて透過します。どれか一つでも欠ければ、シートは白い平面になります。
紙が燃える、または炎によって現れる場合は paper-cinder-transition を使用してください。このスキルが扱うのは、燃えていないシートです。シーンに吊るすか、敷いて使うシートです。ページをめくる本には、このスキルでは提供されない曲げリグが必要です。
動作する実装は demo.html を参照してください。onBeforeCompile でパッチを適用した MeshPhysicalMaterial、半精度ターゲットに一度だけ焼き込むマップ、Canvas 2D で描画するマスクを使用します。その NOISE GLSL、地合い、楮繊維と樹皮斑点の式、裏側からの光の透過、tearLine とフレイのループは、Kibori のワークショップページ(kibori.html、washiMat、footTear)から変更せずに移植されています。
パイプライン
- 繊維を焼き込む(GPU、一度だけ)。 全画面パスで、
R height, G formation, B fibre, A fleckを半精度ターゲットに書き込みます。高さは0.5 + fibre*0.34 + (tooth-0.5)*0.1 - fleck*0.2 - crease*0.55です。ターゲットではsamples: 0を維持し、ミップマップを有効にします。 - 格子をゆがめる。 繊維の座標を 0.014 unit だけドメインワープし、
(gnoise(q*2.1)-0.5)*1.5rad 回転させます。2.7x の周波数で、より細かく目立たない繊維のセットを追加します(最大 0.55)。 - マスクを描画する(Canvas 2D、原紙の変更時)。
Rは破れ目の内側の薄い帯、Gはフレイ繊維を含むシルエット、Bは低アルファのにじみハローを伴うインク、Aは横にずらして描いた影でぼかしたシルエット(縁の古さ)です。getImageDataで読み戻し、行を反転し、アルファをデータとしてDataTextureに書き込みます。 - シートの形を整える。 168 x 240 の平面に、吊り下げたときの反り、山折りのしわ、ピンのくぼみ、長さを維持する円筒状の巻き(半径 0.23、角度
2.6*curl)を付けます。法線方向にthick/3、2thick/3、thickずらして、複製を 3 つ追加します。 - シェーディングする。 地合いと繊維からアルベドを作り、その上にインクを重ね、エイジマスクで焦げ色とフォクシングを加えます。高さにはワールド単位のバンプを適用します(注意点を参照)。アルファはカットアウトではなく、カバレッジです。
- 光を透過させる。
emissive += L * trとし、tr = 0.74 + (0.5-form)*0.85 + (0.5-lowfbm)*0.45 - fibre*0.22 - fleck*0.45に原紙のゲインを掛け、薄い帯の分を加え、インクの分を差し引きます。Lは背後にある庭です。スカイ、日光の斑点、ミップバイアス 1.4 でサンプリングした黒い竹の幹を含みます。 - マクロ。 焦点位置を中心とする 0.5 unit のウィンドウで、2 つ目の高さターゲットとマスクを再度焼き込み、ウィンドウの端でベースにブレンドします。小さなテクスチャをタイル状に使ってはいけません。
採用した定数
| 和紙 | コットンラグ | クラフト紙 | |
|---|---|---|---|
| 色合い | #f0e8d2 | #f0eee6 | #a88f70 |
| 繊維格子 A / B(単位あたり) | 1.9x54 / 44x2.3 | 15x17 / 16x14 | 7x26 / 26x7 |
| 繊維の凹凸、バンプの高さ(unit) | 0.55, 0.0017 | 0.32, 0.0024 | 0.8, 0.0034 |
| 地合いの周波数、振幅 | 230, 0.09 | 430, 0.17 | 300, 0.20 |
| 漉き目の本数 /unit、振幅、チェーン | 110, 0.28, 0 | 150, 0.55, 0.5 | なし |
| 破れの振幅(T,R,B,L)、フレイのスケール | 64,72,60,76; x12 | 3,58,3,54; x6 | 96,3,4,3; x8 |
| 厚さ(unit)、透過率 | 0.0045, 1.0 | 0.0085, 0.42 | 0.011, 0.16 |
| ラフネス、光沢 | 0.93, 0.4 | 0.9, 0.25 | 0.96, 0.10 |
スケール:1 unit = 134.8 mm(A4 は 1.55 x 2.2)。ベースの焼き込みは 990 px/unit = 7.3 px/mm、マクロウィンドウは 0.5 unit に対して 1536 px = 22.8 px/mm です。破れのサンプル数は 3400 per unit です。カメラの視野角は 28 度、マクロ時の距離は 0.46-0.50 です。
それぞれの失敗を防ぐルール
- 輪郭は、揺らぎのある四角形ではなく、独立した4本の裂け目で切り出します。 単一の正弦波やノイズの縁はCSSの波のように見えます。
tearLineはまっすぐ進み、曲がり、再び進みます。切り口の振幅は3~4、裂けた側は60~100にします。 - 繊維を縁からはみ出すように引き出し、部分的な被覆として描画します。 アルファの縁がくっきりしていると、ハサミで切ったように見えます。繊維は幅0.3~0.7 px、アルファ値30~75%にします。暗い壁では、淡い毛のように見えます。
- 前面のレイヤーの後ろに、オフセットをずらした複製を3枚重ね、紙の色の0.8~0.94倍の色にします。 四角形が1枚だけでは厚みがなく、横から見ても何も見えません。オフセットは0.0045~0.011 unitにします。それ以上だと段ボールのように見えます。
- 繊維の格子にゆがみと回転を加えます。 直交する2組の隆起は、特に斜めから光を当てると、引っかき傷のあるガラスや格子模様のように見えます。
- 透過率は、アルベドと同じフィールドから計算します。 一定の発光値では、カードの中にランプがあるように見えます。薄い形成部分は光らせ、繊維の束とインクは光を遮るようにします。
- 縁の外側では、トースト帯のアルファ値をゼロまでフェードさせます。 ほつれをトーストすると、暗い壁を背景に、引き出された繊維が茶黒く見えます。
- インクのしきい値より下にハローをにじませ、繊維と形成部分を通じてインクを広げます。 ぼやけたハローだけでは、ピンぼけしたインクに見えます。
smoothstep(0.62,0.85, halo + fibre*0.4 + (0.5-form)*0.5)は、インクを繊維に沿って吸い上げます。この項はsmoothstep(0.03,0.16, halo)でガードします。そうしないと、インクから遠い繊維が黒くなります。 - 樹皮の斑点を丸くします。 Kiboriのセルハッシュはマクロ表示で四角くなるため、楕円
smoothstep(0.5,0.12,length((fract(q*cell)-0.5)*vec2(1,1.9)))を乗算します。 - マクロ表示用にウィンドウを用意します。 7 px/mmのベースマップは22 px/mmで霧状になります。タイル状の512 pxの紙テクスチャでは継ぎ目が見えます。
よくある問題とその症状
- 縁の繊維が消える、または縁の外側に真っ黒なフリンジが現れます。
alphaTestは0.5未満をすべて切り捨てます。alphaToCoverage: true、alphaTestを0、アンチエイリアスをオンにします。4つのMSAAサンプルで部分アルファをディザリングします。シャドウ深度マテリアルには専用のalphaMapとalphaTest: 0.5を指定します。 - 紙の横にある大きな黒い毛むくじゃらの形は、バグではありません。 シートが壁に落としている影です。フィルライトか弱い発光で壁を照らし、影が茶色に見えるようにします。
- レリーフが遠くでは強く見え、マクロ表示では消えます。 threeの
perturbNormalArbはサーフェス微分を正規化するため、傾斜はピクセルサイズに依存します。bumpmap_pars_fragmentを正規化しないMikkelsen形式に置き換え、高さをワールド単位で指定します。 - 縁の経年変化やインクのテクスチャが、乗算済みアルファで暗くなります。 アルファ付きのCanvasテクスチャ経由でデータをアップロードしないでください。
getImageDataで4つのキャンバスを結合してDataTextureにします(行を反転し、flipYはfalseにします)。 - Safariで、ぼかした縁の帯が表示されません。
ctx.filterは不安定です。図形を20000 pxずらして描画し、shadowOffsetX = -20000にすると、ぼかしだけが描画されます。 - Half-floatターゲットがブロック状のタイルになります。 そのターゲットではMSAAを使い、
samples: 0にします。 - 丸まった角が何も表示しません。 真正面から見ると、65度の丸まりは短縮されます。距離6でカメラを0.3 unit揺らすか、キーライトを約54度から当てます。
- 全体表示で、繊維の渦が大理石模様のように見えます。 回転フィールドが強すぎます。1.5 radが上限で、1.9では花柄のように見えます。
再生とライフサイクル
デモはゆっくりとしたカメラの揺れを伴ってアイドル状態になります。prefers-reduced-motionでは合成済みの静止画を1枚描画し、コントロール入力があったときだけ再描画します。document.hiddenのときは一時停止し、再開時に時間の基準をリセットします。dtは1/30 sに制限し、デバイスのピクセル比は2を上限とし、サイズはResizeObserverから取得します。逆光とマクロ表示は1 - exp(-dt*k)で滑らかに補間します。コントロールはフォーカスリングとライブステータス行を備えた、本物のラジオボタン、チェックボックス、スライダーです。
コスト(Metal 上のヘッドレス Chrome、DPR 1、1440x900 で測定)
アイドル時のスウェイは約 85 fps です。7 回のドローコールで 32.5 万個の三角形(表面と 3 層で 4 × 8.1 万個)を描画します。標準の切り替えでは約 320 ms で再構築されます(4 つのキャンバスにマスクを適用し、ベイクし、ジオメトリを生成)。マクロウィンドウの表示には約 150 ms かかります。GPU メモリは約 100 MB です(ベースターゲット 40、マクロターゲット 25、マスク 33)。コストがかかるのは、4 つのマスクキャンバスと、数万本のほつれのストロークです。ベイクパスと法線のサンプリングにはコストがかかりません。手軽に調整できるのはベースの密度(PPU_B)です。660 px/unit にするとメモリが半減し、全体表示も鮮明に保てます。
出典
Kibori 木彫のローンチページから抽出しました。そこでは、障子の採光窓、破れたフッターシート、紙のランタンに和紙が使われています。このビルドでは、これらの生成器を 1 枚のシート上で再現し、マクロモードを追加しています。
基準ルーブリックに対する自己評価(0 = 平面、5 = タイル状のノイズテクスチャ、8 = 実際のエッジを持つ方向性のある繊維、10 = 写真とのマクロ比較に合格)
| 項目 | スコア | 理由 |
|---|---|---|
| 繊維の構造 | 8 | 変形し、回転する繊維を 2 つのスケールで表現。奥行き方向に繊維が交差することはありません |
| 耳のエッジ | 8 | 独立した 4 つの裂け目、ほつれた繊維、羽毛状の帯。繊維は影を落としません |
| 厚み | 6 | 段差のある 3 層。極端な斜めから見ると段差が見えますが、断面の繊維はありません |
| 透光性 | 8 | 紙料の分布に基づき、茎と琥珀色の陽光部分を表現。厚みによる散乱のぼかしはありません |
| カールと折り目 | 6 | 巻き上がりは説得力がありますが、全体表示では折り目がかすかに見えます |
| インク | 6 | 繊維へのにじみは狭く、ストロークの端に溜まりません |
| 経年劣化したエッジと影 | 6 | 焼けと斑点は良好ですが、壁に落ちる影は硬い形状です |
| テクセル密度 | 8 | マクロでは 22.8 px/mm、全体表示では 7.3 px/mm |