mcpField
3
3d-wood-lighting-scorecard

3d-wood-lighting-scorecard

github.com/mengtoクリエイティブ・メディア7.1k346 回インストール3 日前に更新
リポジトリ

3d-wood-lighting-scorecard について

Three.js のシーン制作者向けに、木目、窓から差す光、ほこり、最終トーンマッピングを扱う照明・マテリアルスキルです。固定条件で描画を採点し、各レイヤーの効果を調べるスクリプトも含みます。

主な機能

  1. 01プロシージャルな木材マテリアル生成
  2. 02単一窓のキーライトと環境光の設定
  3. 03光の筋とほこりの追加
  4. 04最終パスでの ACES 適用
  5. 05フレーム採点とレイヤーのアブレーション

活用シーン

  1. 01暗い木工作業室のシーン制作
  2. 02木目の照明と影の評価
  3. 03光の筋やほこりの効果比較

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/mengto/skills --skill 3d-wood-lighting-scorecard

インストール後、Claude Code で/3d-wood-lighting-scorecardを入力して読み込まれたか確認します。

同梱ファイル

SKILL.md と一緒にインストールされる参考資料やスクリプト。

demo.html
PROMPT.md
agents/
openai.yaml
references/
last-scorecard.json
metrics.js
rubric.md
scorecard.mjs

詳細

3D 木材ライティングのスコアカード

木材が木材らしく見えるのは、暗い部屋を横切る一方向のキーライトが当たっているときだけです。木目、影の継ぎ目、光芒はすべて同じ窓から照らし、最後に一度だけトーンマッピングし、雰囲気ではなく完成フレーム上でそれぞれを測定します。

検証済みのスタック:Three.js r169(unpkg)、WebGL2、MeshPhysicalMaterial.onBeforeCompile にパッチを当てた GLSL、半精度浮動小数点 MSAA ターゲット、手書きの ACES パス。デモは demo.html、数値は references/ にあります。

適用範囲。このスキルは、照明のある室内ひとつのライティングとマテリアルのレイヤーを対象にします。実際の木や屋根の輪郭で遮られる必要がある太陽光の光芒には、3d-sky-rays(スクリーンスペース遮蔽)を使用してください。ここでの光芒は平面で作成しています。スキャンまたはペイント済みの PBR マップ、テクセル予算、納品には 3d-high-resolution-textures を使用してください。ここでの木材は、共有の 1024 ノイズフィールドひとつからプロシージャルに生成します。暗い金属の明るいリムには 3d-specular-rim-glow を、ガラスにはこの手法ではなくスタジオボックスの手順を使用してください。スコアリングの習慣だけを取り入れる場合は workflow-score-to-target を使用してください。このスキルでは、木材の評価基準と測定ツールを提供します。

Kibori(木彫)の、スクロールに応じて動く WebGL 木工ページから抽出しました。woodMat、buildEnvironment、キーライトのリグ、光芒の平面、ダスト、最終パスを移植し、光芒が必要とする場所に窓を移しています。

構築順序

  1. レンダラー:NoToneMapping、LinearSRGBColorSpace、samples: 4 の HalfFloatType ターゲット、autoClear = false。加算合成の光芒とダストも含め、すべてをこのターゲットに描画します。最後に全画面パスで一度だけトーンマッピングします。
  2. 窓はひとつだけにします。発光する紙、キーライトの方向、光芒の平面、PMREM 環境内の窓カードを同じ線上に配置します。それ以外は配置しません。
  3. キーライトは DirectionalLight 0xfff2df、強度 2.0、4096 シャドウマップ(820 px 未満では 2048)、normalBias 0.0045(2048 の場合は 0.0085)、bias -0.00025、静的な部屋では shadowMap.autoUpdate = false にします。実際の開口部がある実際の壁に影を落とさせます。そうすれば、作業台にできる光の溜まりが窓の形になります。
  4. カメラ側からフィルライト 0xaec7df 0.60-0.76、後方からリムライト 0xffdeb4 0.72、半球ライト 0xb5c5d7 / 0x35281c 0.19 を当てます。これらは影側を保つためだけのもので、見た目を作るものではありません。
  5. 木材:MeshPhysicalMaterial の ior 1.43、specularIntensity 0.72、粗さは clamp(base * W.rough, 0.35, 1) とします。プロシージャルなアルベド/粗さ/バンプは、ひとつの周期ノイズフィールドから一度だけベイクします(1024x1024 RGBA8、64 セル、3 オクターブ、ミップマップあり、リピート)。
  6. 光芒、ダスト、最終パスの順に処理し、その後 references/scorecard.mjs を実行します。

ルールと、それぞれが防ぐ失敗

フラットな光で木目が消えます。 部屋が見えるほど明るい半球光や環境光は、あらゆる細孔を埋めてしまい、板が塗装したように見えます。キーライトを 2.0 に保ち、環境光の成分を低く抑えてください。ここで測定したところ、スタジオの反射をオフにすると影が持ち上がらなくなるため、合計スコアが 0.14 上がりました。マテリアルごとの envMapIntensity は、作業台で 0.25、彫刻した板で 0.45、デフォルトで 0.40 にします。

envMapIntensity が機能せず、調整しても書面上だけのものになります。 three r163 以降では、マテリアル値は material.envMap が設定されている場合にのみ読み取られます。scene.environment だけでは無視されます。木材のマテリアルすべてで mat.envMap = env を設定し、マップを null にするのではなく、強度を使ってレイヤーを切り替えてください(null にすると再コンパイルが発生します)。

存在しない窓が光沢面に映ります。 Kibori の環境マップでは窓を左前に配置していましたが、窓が左後ろにあるシーンでは、存在しない光が反射します。環境マップの窓のペイン群を実際の窓の位置に配置してください(ここでは (-2.3, 4.3, -3.2)、スケール 0.5、12 枚のペインを 1.24x1.38、放射輝度 3.8/3.55/3.05 に設定)。鏡面反射の方向が窓から少し外れるように板を傾けます(ここでは 0.27 rad。鏡面反射の角度と完全に一致すると、板全体が白っぽくなります)。 ボリュメトリック光で黒つぶれし、木目が失われます。 加算平面は背後にあるすべてのものに光を加えます。交差する木材の上面も例外ではありません。各光線をワールド高さ(smoothstep(0.02, 0.34, y))に応じてフェードさせ、ゲインは 0.13(Kibori は 0.12)に保ち、基準ストリップ下の明るさを 35 レベル未満に抑え、黒レベルを確認してください。p1 は 7 未満である必要があります。高さフェードを導入する前は、ゲイン 0.14 で銘板に乳白色のもやがかかり、木目も失われました。

光線の平面が立体と交差する箇所に、くっきりしたくさび形が現れます。 平面がベンチや柱と交差し、明るい線を描きます。Kibori では平面をベンチの後ろに配置していましたが、上記の高さフェードを使えば平面を移動せずに解決できます。また、pow の前に clamp を適用してください。エッジ座標がわずかに負のとき、pow(1.0 - across, 2.2) は NaN になり、加算ブレンディングを通じて NaN が伝播すると黒いくさびが生じます。

ゴッドレイがクリップします。 レンダラーがトーンマッピングを行う場合、または光線パスが ACES の後に加算される場合、光線にトーンマッピングが二重に適用されるか、値が 1.0 に張り付きます。光線を線形 HDR で合算してから、ACES を一度だけ適用してください(a=2.51 b=0.03 c=2.43 d=0.59 e=0.14)。窓紙は toneMapped:false の線形値(1.15, 0.86, 0.52)です。(3.9, 3.35, 2.55)では白くクリップしていました。

加算アルファ。 AdditiveBlending はアルファも加算します。不透明なキャンバスでは見えませんが、シーンをアルファ合成レイヤーに描画することがあれば、blendSrc One, blendDst One, blendSrcAlpha Zero, blendDstAlpha One を指定した CustomBlending を使用してください。

ベンチにシャドウアクネが発生する、または銘板の下に隙間ができます。 アクネは小さな負の bias だけが原因です。マップの解像度に応じた normalBias を使用してください(4096 では 0.0045、2048 では 0.0085)。オブジェクトと影の間の隙間(ピーターパンニング)は、normalBias が高すぎることが原因です。スコアカードでは contactGap として表示されます。各オブジェクトの下に焼き込み式の接地シャドウを追加してください(放射状アルファ 0.78、r = 0.30..1.0 の smoothstep、y 0.0012)。シャドウマップだけでは接地部分が明るく残ります。

単一帯のテクスチャが斜め方向でちらつきます。 微細な帯(1 メートルあたり 96/88 サイクルの道管、230 の繊維、13/115 の放射組織)は、それぞれの座標の fwidth を使ってフェードさせる必要があります。また、バンプは色より先に弱めてください(1 - smoothstep(0.05, 0.18, vesselPx))。そうしないと dFdx(h) によってモアレが発生します。スコアカードの shimmer は、半ピクセル分だけ視点を移動した値です。ここでは 1.1 ですが、3 以上はエイリアシングです。

彫刻のレリーフがノイズになります。 周期が 12 px 未満の溝はバンプでエイリアシングが発生し、明るい環境光に対してちらつきます。ここでの青海波の彫刻は 0.15 m のセルを使い、円ごとに 2 本のリングを配置しています(幅 1440 の場合、約 16 px)。0.062 m では 1x でノイズになり、2x では問題ありませんでした。彫刻のサイズはモデルではなく、ピクセルの予算に合わせてください。

木目にレンガ状の模様が現れます。 板目の上面では、髄線の斑点(rays)がタイル状の格子に見えます。Kibori のデフォルト値 0.8 は柾目の面向けです。銘板では 0、その他では 0.15 にしてください。

暗い壁にバンディングが発生します。 ビネットをかけたグラデーションに 8 ビットの段差が現れます。sRGB エンコード後に ±0.0015 のハッシュディザ((hash - 0.5) * 0.003)を加えると解消できます。スコアカードは同じピクセルが 6 個以上続く箇所を数えます(flatRun 0.019)。

カメラを動かすとメトリクスも変わります。 揺れと時間を固定し(KiboriScore.freeze(true))、同じタスク内でレンダリングしてキャンバスを読み取ってください(preserveDrawingBuffer は使用しません)。固定サイズと DPR でキャプチャし、ヘッドレス Chromium を使用してください。アプリ内ペインは背景を自身で描画し、GPU を共有します。

定数

項目値
カメラ垂直方向 34 度、(0.60, 1.25, 2.55) から (0.05, 0.50, -0.30) を注視。アスペクト比が 1 未満の場合は FOV を広げます
光の方向(0.40, -0.75, 0.45) に正規化。窓の中心 (-0.60, 1.40, -1.0)、開口部 1.5 x 1.5
レンダーターゲットHalfFloat、MSAA 4、DPR 上限 2
キー/フィル/リム/半球光2.0 / 0.60 / 0.72 / 0.19(Kibori のフィルは 0.76)
光線5 枚の平面、幅 0.44-0.52、長さ 2.75、ゲイン 0.13、足元のフェード 0.22(Kibori は 0.30)、fbm 3 オクターブ
壁面の光線10 枚の平面、幅 0.20-0.90、長さ 2.2、ゲイン 0.08
ダスト1900 個のポイント、サイズは 17 / depth px(3.4-46 にクランプ)、1 - smoothstep ボケ、ビーム軸から約 0.33 m 以内に制限
フィールド1024^2 RGBA8、64 個の周期セル(n、f、2 番目の n)、ミップマップあり。「low」切り替え時は 16 px
最終パスアンシャープ 0.10、ビネット 0.42 + 0.58 v、ACES、sRGB、ディザ 0.003

コスト(Apple Silicon、Metal 上のヘッドレス Chromium で測定)

フルパイプラインでは、1 フレームと 1 px の読み戻しに、1440x900 x1 で 7.4-7.9 ms、2880x1800(DPR 2)で 16.5 ms、390x844 DPR 2 で 7.2 ms かかります。最もコストの高いレイヤーは光線と壁面の光線の平面です(加算描画されるピクセルごとに 3 オクターブの fbm を計算し、x1 で約 1 ms、x2 で 5 ms)。シャドウマップは静的なので、初回以降はコストがかかりません。グレイン、環境光、キーライト、ダストはそれぞれ約 0.2-0.7 ms です。手軽に調整できるのは、光線の fbm のオクターブ数(または半分の解像度で描画すること)であり、平面の数を減らすことではありません。切り替えを 1 回試しただけで結論を出さないでください。2x では実行ごとのノイズの差が約 1 ms あります。

スコアリングの手順

  1. ページを配信し、npm i playwright-core を実行してから、node references/scorecard.mjs を実行します(--url、--size 1440x900、--dpr 1、--target 8、--no-ablate、--sync)。ヘッドレスでキャプチャし、時間を固定して、references/rubric.md の 8 項目を採点し、各レイヤーを除去テストして JSON に書き込みます。項目のいずれかが基準に届かない場合は、終了コード 1 を返します。
  2. 変更前にベースラインを採点し(ここでは全レイヤーをオフにすると 3.7)、ファイルを保存します。除去テストは指標を検証するためのものです。レイヤーを取り除いたときに合計スコアが上がるなら、測定に誤りがあるか、そのコストに見合う効果がありません。
  3. 最も低い項目のうち、最も安価なサブ指標から修正し、同じサイズと DPR で再実行して、画像で効果を確認できた変更だけを残します。指標に基づく変更を加えるたびに、2x のクロップを確認してください。このデモでは「改善」のうち 3 つがスコアを上げた一方で、ボードの見た目を悪くしたため(ボード全体のクリアコート、異方性 0.3、ピクセルごとのトーンゲイン 2.4)、元に戻しました。
  4. 「見た目が正しい」ことを示すスコアについては、キャプチャとルーブリックを数値を見ていない別の評価者に渡し、2 つのスコアの低いほうを採用します。
  5. 正直に報告します。デモ、1440x900 x1:平均 8.8。グレイン 8.1、キー/フィル 10、シャドウ 9.6、光線 8.0、壁面の光線 8.8、トーン 9.8、テクスチャ 9.7、スペキュラー 6.5(8 に届きません。光沢が広がっていて、はっきりしたハイライトになっておらず、それを追加する修正ではグレインが損なわれました)。DPR 2 ではスペキュラー項目が 7.4、光線が 7.3 です(ダストの粒が測定ウィンドウより細かいため)。

アクセシビリティとライフサイクル

  • prefers-reduced-motion:時間を 5.2 に固定し、揺れとダストの移動をなくしたデザイン済みの静止画を、要求に応じて描画します。レイヤーの切り替えとスコアは引き続き動作します。
  • document.hidden のとき、およびキャンバスがビューポートから外れたときは一時停止します。dt は 1/30 s にクランプし、一時停止後の最初のフレームでは dt = 0 にします。
  • ResizeObserver からサイズを取得します。DPR は 2 を上限とし、レンダーターゲットとダストのポイントサイズもこれに追従します。
  • レイヤーの切り替えには、フォーカスが見える実際のチェックボックスを使います。スコアは aria-live 領域です。
  • 820 px 未満ではパネルがキャンバスの下に移動し、スコアチップがキャンバス上に表示されます。

確認

  • demo.html のメトリクスブロックが references/metrics.js と一致していること(node scorecard.mjs --sync)。
  • 1440x900 と 390x844 でコンソールにエラーがなく、モーション軽減時に画面が整って表示され、タブ順ですべての切り替えに到達できること。
  • ベースライン、全レイヤー有効時、除去テスト、コストを含むスコアカード JSON が存在し、上記の報告と一致していること。

コメント