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无法渲染透明背景,因此透明度是测量出来的:
- 物品副本先在一面平坦的黑色背景前拍摄,再在一面平坦的白色背景前拍摄。两拍之间其他任何内容都不改变。
- 逐个像素:如果它在一种颜色中是白色,在另一种中是黑色,那么它就是背景(alpha 0)。如果它在两者中完全相同,那么它就是实心物品(alpha 1)。介于两者之间:
alpha = 1 - (白色 - 黑色) / 255。这处理了玻璃和抗锯齿边缘。 - 颜色来自在黑色背景上使用地点自身外观(泛光、色彩校正)拍摄的第三张照片,然后除以alpha。
导致失败的一切,都是两次拍摄之间除了背景之外有所不同的东西,或者是图片中除了物品之外的其他东西。这就是下一节要讲的内容。
留意世界
截图会捕捉相机前的任何东西以及照亮物品的任何东西。在渲染前后,请考虑:
- 挡路的东西。 建筑物、地形、其他模型、底板、附近物体的粒子。脚本通过从不拍摄物品所在位置来避免所有这些:它克隆物品,将克隆体提升到空旷的天空中,并在其正后方放置一个背景平面。它会检查放置点是否确实空旷(在
clearance虚化单位内没有部件,正上方没有东西,地面在下方很远),并一次爬升1500个虚化单位,直到找到空旷位置。阅读报告中的staged at Y=行;如果它说强制高度不清晰,或者找不到清晰的天空,在查看地图高度后自行选择一个altitude。 - 投下阴影的东西。 物品和太阳之间的东西会给它投下阴影,即使它超出了画面。脚本会向太阳方向投射射线,并告诉您是否有东西被击中。
- 光源来自哪里。 太阳(或月亮)是唯一的方向光,并且始终在地平线上方。
lighting = "studio"(默认)会临时移动一天中的时间,使太阳位于相机的左肩上方,并提高环境光,这样无论地点如何,每个物品都以相同的方式被照亮。lighting = "world"会保留地点的光照;然后报告会说明太阳是在可见侧的前方还是后方。后方意味着渲染结果较暗。 - 向上看。
lookStraightUp = true会旋转克隆体并将相机指向+Y方向,因此物品后面永远只能有天空。当地图太高或杂乱,难以找到侧面清晰的天空时使用它。代价是:相机现在位于下方,太阳永远无法到达其后方,报告中关键光对齐度会下降(约0.6而不是1.0)。实际上它仍然可以正常渲染;只是对阴影的控制较少。 - 天空效果。 大气雾霾、云朵、太阳光、景深和模糊位于相机和背景之间。脚本在拍摄时会移除或禁用它们。
- 发光。 背景是一个Neon部件,因为Neon忽略光照和阴影,提供精确的平坦颜色。但Neon会发光,并且发光会溢出到物品的薄部分,使它们被读取为透明。关闭BloomEffects并不能阻止这种情况(引擎会回退到默认发光);一个启用的、Intensity为0、Size为0的BloomEffect可以做到,并且脚本会为测量拍摄添加一个。如果实心物品出现幽灵般的效果,这是首先要怀疑的。
- 会移动的东西。 粒子、光束、火、烟雾和轨迹在每次拍摄中位于不同的位置,会变成黑色斑点。默认情况下,它们在克隆体上被关闭。使用
particles = true,它们会被保留并在拍摄时冻结。 - Studio覆盖层。 选择框、悬停轮廓和StarterGui屏幕会出现在截图中。脚本会清除选择并隐藏它们,并且只使用视口居中的正方形区域,远离角落的小部件。
- 视口本身。 输出尺寸受Studio视口较短边的限制,并且Studio必须处于编辑模式,且3D视图可见。图形模式"OpenGL"不起作用。
脚本在完成时会将所有这些恢复原状,包括在出错时。原始物品永远不会被移动或修改。
工作流程
在任务进行期间,不要运行任何计时命令。Claude Code会为每次调用添加时间戳,因此运行时间在最后一步(步骤7和基准测试)的单个调用中从会话记录中计时。每次任务都以该计时结束,并且给用户的最终消息以总时间结束。 不要跳过它,也不要等待被询问。
-
找到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, "(更多;缩小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。 -
加载渲染器,除非状态检查显示
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以及上面的数字。 -
渲染。每次调用都很简短:
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") -
阅读报告。一个良好的报告看起来像:
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:下的任何内容采取行动。 -
查看它。数字不会告诉您物品朝向错误的方向。调用
shared.SkyRenderBoard({ "banana" })(或使用{}获取所有结果)。它在天空中构建一个临时展板,在洋红色和深灰色背景上显示每个图像,并返回精确的camera_position和look_at_position,以传递给screen_capture。screen_capture不绘制GUI,这就是展板使用部件上的贴花的原因。判断:角度正确吗?整个物品在画面中吗?两种背景上的边缘干净吗?照亮侧可见吗?没有杂散斑点吗?使用更改的选项重新渲染,直到正确为止,然后使用shared.SkyRenderBoard(nil)移除展板。 -
交付(见下文),然后使用
shared.SkyRenderClear(),除非用户希望结果保留在会话中。 -
计时(必需,每次任务)。 作为任务的最后一次工具调用,在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会读取它们。
- 时间: 作为任务回合的最后一次工具调用。它可以看到直到自身在内的所有调用,因此只有最终消息(几秒钟)被排除在外。如果用户后来询问已完成的任务,请再次运行它以获得完整数据。
- 运行:
python scripts/benchmark.py会选择运行了此技能的最新会话及其上次渲染请求。传递会话id或.jsonl路径以选择其他会话,传递--list和--turn <n>以选择其中的其他请求,传递--calls以列出每次调用及其计时。 - 报告: 阶段表(查找、设置、渲染、导出、组装、清理、簿记、回复),每个都分为模型时间(思考和编写调用,包括脚本粘贴)和工具时间(等待结果)。并行调用只计数一次,因此各阶段加起来等于总时。然后是图像计数和每秒图像数。
- 渲染细节(可选): 每次渲染报告中的
time行是在Studio内部围绕该渲染测量的。使用它来比较单个渲染。
说明运行是冷启动(渲染器和导出器在此任务中粘贴)还是热启动(已经安装),因为粘贴是冷启动设置时间中最大的一部分。使用来自步骤7的Total time:行结束最终消息。
选项
除了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] | 从带有地点泛光和". . . " |
…
[为翻译截断]