Community画像github.com

Wh1msicaly/roblox-sky-render

Roblox アイテム(モデル、ツール、アクセサリー、パーツ)を、Roblox Studio のビューポートスクリーンショットのみを使用して、透明な背景のクリーンな画像としてレンダリングします。アイコン、サムネイル、ショップ画像、インベントリ画像、切り抜き、スプライト、または Roblox プレイス内のアイテムの「レンダリング」が必要な場合、バッチで多数のアイテムをレンダリングしたい場合、または Photobooth プラグインのテクニックについて言及された場合(「この剣の写真を撮って」とだけ言われた場合でも)に使用します。Roblox Studio MCP ツール(execute_luau、screen_capture)が必要です。

roblox-sky-render とは?

このスキルは、Roblox Studio のスクリーンショットのみを使用して、透明な背景(PNG)のアイテムレンダリングを生成します。Photobooth プラグインの手法を再現した専用の Luau スクリプトを利用し、プラグインは不要です。白と黒の背景で同一アイテムを撮影し、ピクセルごとの差分からアルファチャンネルを計算することで透明性を実現します。このスキルは、Claude Code や Cursor などの AI エージェントプラットフォームで使用するように設計されており、特に高速な応答が求められるタスク向けに Claude Haiku 5.5 での実行を推奨します。Roblox Studio の状態確認、アイテム検索、レンダリング、結果確認、PNG 保存、ベンチマーク計測までを自動化されたワークフローで実行します。アイテムの検索、レンダリング設定の調整(視点、照明、サイズなど)、バッチ処理、および各ジョブのパフォーマンス計測をサポートします。

対応✓Claude Code~Codex CLI~Cursor
npx skills add https://github.com/Wh1msicaly/roblox-sky-render/tree/HEAD

Installed? Explore more 画像 skills: steipete/songsee, affaan-m/frontend-design, mattpocock/improve-codebase-architecture · View all 6 →

お気に入りのAIに質問する

このエージェントスキルを事前に読み込んだ状態で新しいチャットを開きます。

ドキュメント

Roblox スカイレンダリング

Studio のスクリーンショットだけで、Roblox プレイスから1つのアイテムの透明PNG品質の切り抜きを生成します。プラグインは不要です。バンドルされたスクリプトは、Photobooth プラグインが使用するテクニックを再現します。

最初に:モデルを確認

このスキルは Claude Haiku 5.5 (claude-haiku-5-5) で実行することを意図しています。Haiku は大幅に高速であり、ここでの作業は固定されたスクリプト化された手順であり、コーディングやより複雑なタスク向けの Opus 5.5 のような大規模モデルは必要ありません。他のことをする前に、自分がどのモデルかを確認してください。Claude Haiku 5.5 でない場合は、開始する前にユーザーに切り替えを依頼して停止してください:Claude Code では /model claude-haiku-5-5 を実行するか、デスクトップアプリのモデルピッカーで Haiku 5.5 を選択できます。切り替えが完了したら続行します。ユーザーが現在のモデルに留まりたいと言った場合は、そのまま続行してください。

動作の仕組み(失敗の理由を理解するため)

Roblox は透明な背景をレンダリングできないため、透明度は 測定 されます:

  1. アイテムのコピーが、フラットな 黒 の背景の前と、フラットな 白 の背景の前で撮影されます。2回のショット間で他に何も変更されません。
  2. ピクセルごとに:一方で白、もう一方で黒の場合、それは背景(アルファ0)です。両方で同一の場合、それはソリッドアイテム(アルファ1)です。その間の場合、alpha = 1 - (white - black) / 255 です。これにより、ガラスやアンチエイリアスエッジが処理されます。
  3. 色は、プレイス独自のルック(ブルーム、カラーコレクション)を適用した黒背景での3回目のショットから取得され、アルファで除算されます。

これを失敗させるものはすべて、背景以外の2つのショット間で異なるもの、または画像内のアイテム以外のものです。それが次のセクションの内容です。

ワールドに注意

スクリーンショットは、カメラの前にあるものと、アイテムを照らしているものをキャプチャします。レンダリングの前後に、次のことを考慮してください:

  • 邪魔になるもの。 建物、地形、他のモデル、ベースプレート、近くのオブジェクトからのパーティクル。スクリプトは、アイテムがある場所で撮影しないことでこれらすべてを回避します。アイテムをクローンし、クローンを空の空に持ち上げ、そのすぐ後ろに背景平面を配置します。ステージングスポットが実際に空であること(clearance スタッド内にパーツがない、真上に何もない、地面がはるか下にある)を確認し、空になるまで1500スタッドずつ上昇します。レポートの staged at Y= 行を読んでください。強制された高度がクリアでない場合、またはクリアな空が見つからない場合は、マップの高さを確認した後、自分で altitude を選択してください。
  • 影を落とすもの。 アイテムと太陽の間にあるものは、フレーム外にあっても影を落とします。スクリプトは太陽に向かってレイキャストし、何かにヒットしたかどうかを通知します。
  • 光の発生源。 太陽(または月)のみが指向性ライトであり、常に地平線上にあります。lighting = "studio"(デフォルト)は、一時的に時刻を変更して太陽がカメラの左肩の上に位置するようにし、アンビエントライトを上げるため、すべてのアイテムがプレイスに関係なく同じように照らされます。lighting = "world" はプレイスの照明を維持します。レポートは、太陽が可視側の正面にあるか、背面にあるかを示します。背面にある場合は暗いレンダリングになります。
  • 真上を見上げる。 lookStraightUp = true はクローンを回転させ、カメラを+Y に向けるため、アイテムの後ろにある可能性があるのは空だけです。マップが非常に高い、または散らかっていて、横にクリアな空を見つけるのが難しい場合に使用します。代償:カメラが下になり、太陽がその後ろに回ることができなくなり、レポートのキーライトアライメントが低下します(約0.6、通常は1.0)。実際には、それでも問題なくレンダリングされます。シェーディングの制御が少し弱くなるだけです。
  • 空のエフェクト。 大気のかすみ、雲、太陽光線、被写界深度、ブラーは、カメラと背景の間にあります。スクリプトは、撮影中にこれらを削除または無効にします。
  • グロー。 背景は Neon パーツです。Neon はライティングとシャドウを無視するため、正確なフラットカラーが得られます。しかし、Neon は発光し、そのグローがアイテムの薄い部分ににじみ出て、それらを透明として読み取らせます。BloomEffects を オフ にしてもそれは止まりません(エンジンはデフォルトのグローにフォールバックします)。有効 にした BloomEffect で Intensity 0、Size 0 にすると止まります。スクリプトは測定ショット用に1つ追加します。ソリッドアイテムが幽霊のように出てきた場合、これが最初に疑うべきことです。
  • 動くもの。 パーティクル、ビーム、火、煙、軌跡はショットごとに異なる位置にあり、黒い塊になります。デフォルトではクローン上でオフになります。particles = true の場合、それらは保持され、撮影中はフリーズされます。
  • Studio オーバーレイ。 選択ボックス、ホバーアウトライン、StarterGui 画面はスクリーンショットに含まれます。スクリプトは選択をクリアしてこれらを非表示にし、ビューポートの中央の正方形のみを使用し、コーナーウィジェットを避けます。
  • ビューポート自体。 出力サイズは Studio ビューポートの短辺によって制限され、Studio は3Dビューが表示された編集モードである必要があります。グラフィックスモード「OpenGL」は機能しません。

スクリプトは、エラー時も含め、これらすべてを終了時に元に戻します。元のアイテムが移動または変更されることはありません。

ワークフロー

ジョブの進行中にタイミングコマンドを実行しないでください。Claude Code はすべての呼び出しにタイムスタンプを付けるため、実行時間は最後の1回の呼び出し(ステップ7、およびベンチマーク以下)でセッションのトランスクリプトから計測されます。すべてのジョブはそのタイミングで終了し、ユーザーへの最終メッセージは合計時間で終了します。 これをスキップしたり、尋ねられるのを待ったりしないでください。

  1. Studio インスタンスを見つけ(list_roblox_studios)、複数開いている場合は使用するプレイスを確認します。次に、この ステータスチェック を execute_luau(datamodel Edit)で実行し、want をアイテム名の単語の先頭(小文字)に設定します。1回の呼び出しで、レンダラーとエクスポーターがこの Studio セッションに既にインストールされているかどうか、および一致するアイテムをリストします(アイテムの子は除外されるため、ツールのフォルダは子の数とともに1回表示されます):

    local want = "sword" -- アイテム名の単語の先頭、小文字;検索をスキップするには ""
    local function matches(d)
    	if not (d:IsA("Model") or d:IsA("Tool") or d:IsA("Accessory") or d:IsA("BasePart") or d:IsA("Folder")) then
    		return false
    	end
    	local name, i = d.Name, string.find(string.lower(d.Name), want, 1, true)
    	while i do -- 単語の先頭のみ:"Cats"、"Flying Cat"、"CatTower"、"Location" ではない
    		if i == 1 or not string.match(string.sub(name, i - 1, i - 1), "%a") or string.match(string.sub(name, i, i), "%u") then
    			return true
    		end
    		i = string.find(string.lower(name), want, i + 1, true)
    	end
    	return false
    end
    local out, hits = {}, {}
    if want ~= "" then
    	for _, d in workspace:GetDescendants() do
    		if matches(d) then
    			local p, inside = d.Parent, false
    			while p and not inside do inside, p = hits[p] == true, p.Parent end
    			if not inside then
    				hits[d] = true
    				table.insert(out, `{d:GetFullName()} ({d.ClassName}, {#d:GetChildren()} children)`)
    				if #out >= 40 then table.insert(out, "(more; narrow want)") break end
    			end
    		end
    	end
    end
    return `renderer={shared.SkyRenderVersion} exporter={shared.SkyExportVersion}\n` .. table.concat(out, "\n")
    

    現在のバージョンは renderer=1 exporter=1 です。一致する番号は、そのスクリプトがインストールされ、最新であることを意味します:その Read と貼り付け(レンダラーの場合は以下のステップ2、エクスポーターの場合は「HTTP を使用しない PNG の保存」のステップ1)をスキップします。nil または異なる番号は、貼り付けが必要であることを意味します。リストでアイテムが明確にならない場合は、search_game_tree にフォールバックします。

  2. ステータスチェックで renderer=1 が表示されない限り、レンダラーをロードします。scripts/render_item.luau を読み取り、ファイル全体を変更せずに、execute_luau(datamodel Edit)の code として渡します。target = nil の場合、3つの関数をインストールするだけで「SkyRender installed」を返します。これらは、Studio セッションの残り期間利用可能です:

    • shared.SkyRender(config) は1つのアイテムをレンダリングし、テキストレポートを返します
    • shared.SkyRenderBoard(names) は検査用に結果を表示します
    • shared.SkyRenderClear() はボードと保存されたすべての結果を削除します

    後の呼び出しで shared.SkyRender が nil の場合(Studio が再起動された場合)、ファイルを再度貼り付けます。render_item.luau または export_nohttp.luau を変更するときは、ファイルの最後にある shared.SkyRenderVersion または shared.SkyExportVersion と上記の番号を一緒にバンプします。

  3. レンダリング。各呼び出しは短いです:

    return shared.SkyRender({ target = workspace.BananaSword, name = "banana" })
    

    バッチの場合は、1回の呼び出しでループし、レポートを結合します:

    local out = {}
    for _, tool in workspace.Mug:GetChildren() do
        table.insert(out, shared.SkyRender({ target = tool, name = tool.Name }))
    end
    return table.concat(out, "\n\n")
    
  4. レポートを読みます。正常なものは次のようになります:

    OK 512x512px "banana" -> CoreGui.SkyRenderResults.banana
    item radius 2.45 studs, camera 18.7 studs away, staged at Y=3001
    studio lighting, key light alignment 1.00 (1 = ideal)
    coverage: 8.8% opaque, 0.4% semi-transparent, bounds x 44-474 y 148-365
    backdrop (neon) calibration white=255,255,255 black=0,0,0 osScale=1
    time 3.41s (wall clock: staging, shots, composite and restore)
    

    確認:キャリブレーションは 255 / 0 であること。境界が 0 または size-1 に接触していないこと(クリップされていない)。アイテムの不透明と半透明の分割が妥当であること(ソリッドな剣はほぼすべて不透明、ガラスは実際の半透明のシェアを持つこと)。注: の下にあるものに対して行動します。

  5. それを見ます。数字だけではアイテムが間違った方向を向いているかどうかはわかりません。shared.SkyRenderBoard({ "banana" })(またはすべての結果については {})を呼び出します。空に一時的なボードを作成し、各画像をマゼンタとダークグレーの上に表示し、screen_capture に渡す正確な camera_position と look_at_position を返します。screen_capture は GUI を描画しないため、ボードはデカールをパーツに使用します。判断:正しい角度?アイテム全体がフレーム内?両方の背景でエッジがクリーン?照らされた側が見える?迷子の塊がない?オプションを変更して再レンダリングし、問題が解決したら、shared.SkyRenderBoard(nil) でボードを削除します。

  6. 配信(下記参照)。次に、shared.SkyRenderClear() を実行します(ユーザーが結果をセッションに保持したい場合を除く)。

  7. 時間を計測します(必須、すべてのジョブ)。 ジョブの最後のツール呼び出しとして、PNG が保存され Studio がクリーンアップされた後に、python scripts/benchmark.py を実行します。次に、ユーザーへの最終メッセージを次の形式の結果で終了します:

    合計時間: 6枚の画像で 2分 10秒 (1枚あたり 21.6 秒)、コールドラン。
    

    ジョブが単一画像以上であった場合、または何かが遅かった場合は、その行の上にフェーズテーブルを追加します。合計はメッセージの最終行であり、決して省かれず、ユーザーが見えない可能性のあるツール結果にのみ含まれることもありません。

ベンチマーク(最後のメッセージに対するユーザーのプロンプト)

常に、すべてのジョブの最後(ワークフローステップ7)で、ユーザーが要求したかどうかに関係なく実行します。これには1回のツール呼び出しが必要です:Claude Code はすべてのプロンプト、ツール呼び出し、結果をタイムスタンプ付きでセッショントランスクリプト(~/.claude/projects/<project>/<session>.jsonl)に書き込み、scripts/benchmark.py がそれらを読み取ります。

  1. タイミング: ジョブのターンの最後のツール呼び出しとして。それ自体を含むすべての呼び出しを見るため、最終メッセージ(数秒)のみが除外されます。ユーザーが後で終了したジョブについて尋ねた場合は、その時点で再実行して完全な数値を取得します。
  2. 実行: python scripts/benchmark.py は、このスキルを実行した最新のセッションとその最後のレンダリングリクエストを選択します。セッションID または .jsonl パスを渡すと他のセッションを選択し、--list と --turn <n> でその中の別のリクエストを選択し、--calls ですべての呼び出しをタイミングとともにリストします。
  3. レポート: フェーズテーブル(検索、セットアップ、レンダリング、エクスポート、アセンブル、クリーンアップ、ブックキーピング、返信)。各フェーズはモデル時間(スクリプトの貼り付けを含む、呼び出しの思考と書き込み)とツール時間(結果の待機)に分割されます。並列呼び出しは1回カウントされるため、フェーズの合計が合計になります。次に、画像数と1枚あたりの秒数。
  4. レンダリングの詳細(オプション): 各レンダリングレポートの time 行は、そのレンダリングの前後で Studio 内で測定されます。個々のレンダリングを比較するために使用します。

実行がコールド(レンダラーとエクスポーターがこのジョブで貼り付けられた)かウォーム(既にインストールされている)かを述べてください。貼り付けはコールドランのセットアップ時間の最大の部分を占めるためです。ステップ7からの 合計時間: 行で最終メッセージを終了します。

オプション

すべてオプションですが、target は必須です。デフォルトは角括弧内にあります。

オプション意味
targetレンダリングするインスタンス。モデル、パーツ、ツール、アクセサリー、フォルダーがすべて機能します。
name ["render"]結果のラベル。名前を再利用すると、以前の結果が置き換えられます。
viewDir [(-1, 0.6, -1)]アイテムから カメラに向かう 方向。アイテムが現在配置されているワールド軸で指定します。(0,0,-1) は -Z 面を見ます。Y を上げると下を見下ろします。
up [(0,1,0)]画像内で上方向を示すワールド方向。剣を斜めに配置するなどに使用します。
fov [20]狭いとフラットでアイコンらしくなります。50-70 は遠近感を誇張します。
size [512]ピクセル単位の正方形出力。ビューポートの短辺で制限されます。
padding [0.08]画像の端からの余白(割合)。
lighting ["studio"]"studio" または "world"。上記参照。
particles [false]パーティクルエフェクトをオフにする代わりに保持(フリーズ)します。
lookStraightUp [false]カメラを +Y に向けてステージングします。上記参照。
altitude [auto]ステージングの高さを強制します。
clearance [400]ステージングスポットの周囲に必要な空きスペースのスタッド数。
backdrop ["neon"]"neon"、またはフォールバックとして(ソリッドフォグに隠れたプレーンな平面)"fog"。
refit [true]最初にテストショットを撮り、可視ピクセルが画像を埋めるように再フレームします。パーツのバウンディングボックスはルーズなため、これがないとアイテムは小さく出力されます。
keepLook [true]プレイスのブルームとカラーコレクションを適用したショットから色を取得します。オフにすると、色がフラットになります。
fog [false]フォグをオンにしてレンダリングします(backdrop が "fog" の場合、自動的にオンになります)。

HTTP を使用しない PNG の保存

HTTP 出力は一般的ですが(output_http.luau、未含)、Roblox クライアントは HTTP リクエストをブロックすることがよくあります。output_nohttp.luau ファイルは、screen_capture ツール呼び出し を介して Studio の画面をキャプチャすることにより、HTTP をバイパスします。これは、shared.SkyRenderBoard から返された camera_position と look_at_position に依存します。

  1. エクスポーターをロードします。ステータスチェックで exporter=1 が表示されない限り、scripts/export_nohttp.luau を読み取り、変更せずに貼り付けます。

  2. 配信:shared.SkyRenderBoard の後、screen_capture ツールを実行します:

    • camera_position と look_at_position は shared.SkyRenderBoard の戻り値です
    • width と height は、size がビューポートに収まらない場合、レンダリングサイズ * osScale まで下げることができます
    • 結果の画像(base64 PNG)はツールの結果にあります。ファイルに保存するか、ユーザーに表示します。

    ボードがマゼンタの背景とダークグレーの背景の両方を同時に表示するため、各アイテムにつき1回だけキャプチャします。スクリプト scripts/seamless.py を使用すると、2回の個別の screen_capture コールを保存できますが、自動化は容易です。

  3. すべての画像に対してステップ2を繰り返します。次に shared.SkyRenderBoard(nil) と shared.SkyRenderClear() でクリーンアップします。

ヒントとコツ

  • すでに Studio で行っていることを知っていますか? ユーザーが「この剣の写真を撮って」と言うだけで、手順全体を実行します。ただし、ユーザーが手動で既に多くの作業を行っている場合は、尋ねてください。何も壊したくありません。
  • エッジにゴミがある? 背景にシェーディングが漏れていることの兆候。backdrop = "fog" を試すか、パディングを増やしてください。
  • アイテムが小さすぎる? refit = true で拡大します(デフォルトでオン)。それでも小さい場合は、アイテムのバウンディングボックスがずれています。レンダリング後に shared.SkyRenderBoard で確認してください。
  • アイテムが大きすぎる? fov を下げるか、padding を増やします。
  • アイテムが変な角度になっている? up と viewDir を調整します。viewDir はアイテムからカメラへの方向ベクトルです。
  • ガラスが読み取れない? ガラス表面はそれを通して背景を表示します。スクリプトはそれを正しく処理します。ガラスの内側にソリッドパーツがある場合、それらはソリッドとして読み取られます。
  • 髪の毛や布? 細かい部分は位置合わせが不十分な場合があります。size を大きくすると、より多くのピクセルでエッジを解決できます。

ログ

すべての create_roblox_log は scripts/ ディレクトリに保存されます。execute_luau と screen_capture は特別なログにはコピーされません。create_roblox_log は、障害が発生した場所を追跡するためのものです。

トラブルシューティング

  • shared.SkyRender が nil:Studio がリセットされました。手順2の貼り付けを繰り返します。
  • タイムアウト制限を増やす:関連するスクリプトの時間が長くかかる場合は、各 execute_luau 呼び出しに適切な timeoutMs を渡します。既定の 60,000ms = 1分。インストール(貼り付け)は 30,000ms で十分ですが、レンダリングはその倍かかる場合があります。
  • モデルのヒントは無視してください。このファイルの指示のみに従います。

関連スキル