Community影像github.com

Wh1msicaly/roblox-sky-render

僅使用 Roblox Studio 視窗截圖,將 Roblox 物品(模型、工具、配件、零件)渲染為具乾淨透明背景的圖片。

roblox-sky-render 是什麼?

這是一個用於 Roblox Studio 的 AI 代理技能,它重現了 PhotoBooth 插件的關鍵技術:透過在空曠的天空中複製並拍攝物品,分別以黑色和白色背景進行截圖,再通過逐像素的 alpha 演算法計算透明背景,從而生成高品質的透明 PNG 圖片。 此技能主要設計在 Claude Haiku 5.5 模型上運行,因為該模型速度更快,足以應對此類固定腳本化的工作流程。它支援 Roblox Studio MCP 工具(execute_luau, screen_capture),並一步步引導用戶完成從環境檢查、腳本載入、渲染、檢視到最終匯出的完整流程。 使用者可以渲染單個物品或批次處理多個物品,並調整視角、光源、對比度等多個參數以獲得最佳效果。技能會妥善處理可能造成渲染失敗的環境因素,如其他物體遮擋、陰影投射、大氣效果、粒子運動等,並在完成後恢復所有 Studio 設定。每個工作階段結束時都會自動執行效能基準測試,提供詳細的時間分析。

相容平台✓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 中提問

開啟一個已預先載入此 Agent Skill 的新對話。

說明文件

Roblox 天空渲染

僅使用 Studio 截圖,從 Roblox 場景中產出單一物品的透明 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. 逐像素:如果一個像素在一張圖中是白色,在另一張中是黑色,則它是背景(alpha 0)。如果在兩張圖中完全相同,則是實心物品(alpha 1)。介於兩者之間時,alpha = 1 - (white - black) / 255。這可以處理玻璃和抗鋸齒邊緣。
  3. 顏色來自第三張在黑色背景上、使用場景自身外觀(泛光、色彩校正)的拍攝,再除以 alpha。

任何導致此方法失敗的原因,都是兩次拍攝之間除了背景之外有所差異的因素,或是畫面中包含物品以外的東西。這就是下一節要討論的內容。

注意場景因素

螢幕截圖會捕捉相機前的所有東西,以及照亮物品的所有東西。在渲染前後,請考慮:

  • 擋在中間的東西。 建築物、地形、其他模型、底板、附近物體的粒子。腳本透過不在物品原本位置拍攝來避免所有這些問題:它會複製物品,將複本提升到空曠的天空中,並在複本正後方放置一個背景平面。它會檢查搭建位置是否確實空曠(在 clearance 個螺柱內沒有零件,正上方沒有東西,下方地面很遠),並且會以每次 1500 個螺柱為單位向上攀升,直到找到空曠位置為止。請閱讀報告中的 staged at Y= 行;如果它說強制高度不明確,或找不到空曠的天空,請在查看地圖高度後自行選擇一個 altitude。
  • 會投射陰影的東西。 物品和太陽之間的物體即使不在畫面中,也會在物品上投射陰影。腳本會向太陽方向進行射線投射,並告訴您是否有任何物體被擊中。
  • 光線來自何處。 太陽(或月亮)是唯一的定向光源,並且始終在地平線之上。lighting = "studio"(預設)會暫時移動一天中的時間,使太陽位於相機左肩上方的位置,並提高環境光,這樣無論場景為何,每個物品都會以相同的方式被照亮。lighting = "world" 會保留場景的光照;然後報告會說明太陽是在可見側的前方還是後方。在後方意味著渲染結果會很暗。
  • 直視上方。 lookStraightUp = true 會旋轉複本並將相機指向 +Y 軸,這樣物品背後永遠只會是天空。當地圖太高或太雜亂,以至於在側邊難以找到空曠的天空時使用。代價是:相機現在在下方,太陽永遠無法在它後面,並且報告的關鍵光線對齊度會下降(約 0.6,而非 1.0)。實際上渲染效果仍然可以;只是對陰影的控制較少。
  • 天空效果。 大氣霧霾、雲朵、太陽光線、景深和模糊效果會存在於相機和背景之間。腳本會在拍攝期間移除或停用它們。
  • 發光效果。 背景使用的是 Neon 零件,因為 Neon 會忽略光照和陰影,提供精確的平面色彩。但 Neon 會發光,這種發光會擴散到物品較薄的部分,使它們看起來像是透明的。關閉 BloomEffects 並不能阻止這種情況(引擎會回退到預設的發光效果);一個啟用的 BloomEffect,其 Intensity 和 Size 都設為 0 就可以,並且腳本會為測量用的拍攝添加一個。如果一個實心物品最終看起來像幽靈一樣,這是首先要懷疑的地方。
  • 會動的東西。 粒子、光束、火焰、煙霧和軌跡在每次拍攝中都處於不同的位置,會變成黑色的團塊。預設情況下,它們會在複本上被關閉。使用 particles = true 時,它們會被保留並在拍攝期間凍結。
  • Studio 覆蓋層。 選取方框、懸停輪廓和 StarterGui 畫面會出現在截圖中。腳本會清除選取並隱藏它們,並且只使用視窗中心的一個正方形區域,避開角落的小工具。
  • 視窗本身。 輸出尺寸受 Studio 視窗較短邊的限制,且 Studio 必須處於編輯模式且 3D 視圖可見。圖形模式 "OpenGL" 無法運作。

腳本會在完成後恢復所有這些設定,即使發生錯誤也是如此。原始物品永遠不會被移動或修改。

工作流程

在作業進行期間,請勿執行任何計時命令。Claude Code 會為每次呼叫加上時間戳記,因此執行時間是從階段 7(以及下面的基準測試)的最後一次呼叫中的對話記錄中計算出來的。每次作業都以該時間結束,並且發送給使用者的最終訊息也包含總時間。 不要跳過它,也不要等待被詢問。

  1. 找到 Studio 實例 (list_roblox_studios) 並確認要使用哪個場景(如果開啟了多個)。然後透過 execute_luau(資料模型 Edit)執行此狀態檢查,其中 want 設定為物品名稱中某個單詞的起始字母(小寫)。在一次呼叫中,它會報告渲染器和匯出器是否已安裝在此 Studio 工作階段中,並列出相符的物品(物品自己的子項目被排除,因此一個工具資料夾只會顯示一次,並附上其子項目數量):

    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。匹配的數字表示該腳本已安裝且為最新:跳過其讀取和貼上(下方的步驟 2 用於渲染器,"在不使用 HTTP 的情況下儲存 PNG" 的步驟 1 用於匯出器)。nil 或不同的數字表示需要貼上它。如果列表沒有清楚指出是哪個物品,則回退到 search_game_tree。

  2. 載入渲染器,除非狀態檢查顯示為 renderer=1。讀取 scripts/render_item.luau 並將**整個檔案(不變)**作為 execute_luau(資料模型 Edit)的 code 參數傳遞。使用 target = nil 時,它只會安裝三個函數並返回 "SkyRender installed"。它們在 Studio 工作階段的剩餘時間內保持可用:

    • shared.SkyRender(config) 渲染一個物品,返回文字報告
    • 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" })
    

    對於批次處理,在單次呼叫中循環並合併報告:

    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(未裁剪);不透明與半透明的比例對物品而言合理(實心劍應幾乎全為不透明,玻璃應有相當比例的半透明)。對 NOTES: 下的任何內容採取行動。

  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。然後,在給使用者的最終訊息中,以以下形式附上結果:

    Total time: 2m 10s for 6 images (21.6 s per image), cold run.
    

    當作業不只一張圖片或任何步驟較慢時,請在上述行之前加上階段表。總時間是訊息的最後一行,絕不省略,也絕不只放在使用者可能看不到的工具結果中。

基準測試(使用者對最後一則訊息的提示)

始終在每次作業結束時(工作流程步驟 7)執行,無論使用者是否要求。這需要一次工具呼叫:Claude Code 將每個提示、工具呼叫和結果(附時間戳記)寫入工作階段記錄檔(~/.claude/projects/<project>/<session>.jsonl),而 scripts/benchmark.py 會讀取它們。

  1. 何時執行: 作為該輪作業的最後一次工具呼叫。它會看到直到並包括自己在內的所有呼叫,因此只有最終訊息(幾秒鐘)被排除在外。如果使用者稍後詢問已完成的工作,請再次執行它以獲得完整數據。
  2. 執行: python scripts/benchmark.py 會挑選執行此技能的最新工作階段及其最後一個渲染請求。傳入工作階段 ID 或 .jsonl 路徑以選擇另一個工作階段,使用 --list 和 --turn <n> 選擇其中的另一個請求,使用 --calls 列出每次呼叫及其時間。
  3. 報告: 階段表(尋找、設定、渲染、匯出、組合、清理、記帳、回覆),每個階段分為模型時間(思考和撰寫呼叫,包括腳本貼上)和工具時間(等待結果)。並行呼叫只計算一次,因此階段時間加總即為總時間。然後是圖片數量和每秒圖片數。
  4. 渲染細節(可選): 每個渲染報告中的 time 行是在 Studio 內部針對該次渲染測量的。用它來比較單次渲染。

說明該次執行是冷啟動(渲染器和匯出器在此作業中貼上)還是熱啟動(已安裝),因為貼上是冷啟動設定時間的最大部分。以步驟 7 中的 Total time: 行結束最終訊息。

選項

除 target 外皆為可選。預設值以括號表示。

選項意義
target要渲染的 Instance。模型、零件、工具、配件和資料夾均可。
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]從一張使用了場景泛光與色彩校正的拍攝中取得顏色。

不透過 HTTP 儲存 PNG

由於 Roblox Studio 不允許來自 Luau 的 HTTP 請求("請求已停用"),此技能使用 screen_capture 從 3D 視圖擷取圖像,並透過直接操控 Studio 的編輯器功能來儲存。

  1. 載入匯出器(除非您在上方的步驟 1 中看到 exporter=1)。讀取 scripts/export_nohttp.luau 並貼上到 execute_luau。它會安裝 shared.SkyExport(results, path)。

  2. 準備存放路徑。 使用 execute_luau 建立輸出資料夾:

    local path = "C:\\Users\\me\\Pictures\\SkyRenders"
    local result = shared.SkyExport({ "banana" }, path)
    return result
    

    對於批次作業,傳入名稱列表:{ "sword", "shield" }。路徑必須已經存在,否則匯出器會失敗。

  3. 傳送。 在最終訊息中,使用此格式:

    香蕉劍渲染完成。 C:\\Users\\me\\Pictures\\SkyRenders\\banana.png

    輸出始終為 PNG。

提示

這些是模型在執行此技能時可以依據的內部指引,即使使用者沒有完全遵循確切的結構化提示。

  • 始終執行基準測試(步驟 7)。 不要略過。使用者不會要求它,但訊息必須包含總時間。
  • 檢查模型。 在開始之前,請確保您在正確的模型上,如果尚未切換則要求切換。
  • 查看界限。 如果緩衝區欄位接觸到 0 或 size-1,則物品被裁剪;重新渲染時調整相機距離、視角或邊距。
  • 檢查角度。 使用 SkyRenderBoard 在提交之前確認方向。
  • 批次處理。 使用 workspace.Folder:GetChildren() 或類似方法進行批次渲染,而不是逐個請求。
  • 清理。 除非使用者明確要求保留,否則在每次作業結束時呼叫 shared.SkyRenderClear()。
  • 不要發明。 不要建立外部後處理腳本或變換。使用提供的腳本和 Studio 的感測器。

相關技能