CommunityImagemgithub.com

Wh1msicaly/roblox-sky-render

Renderiza um item do Roblox (modelo, ferramenta, acessório, peça) como uma imagem limpa com fundo transparente usando apenas capturas de tela do Roblox Studio, estagiando uma cópia no céu vazio e fotografando contra fundos branco e preto.

O que é roblox-sky-render?

Este skill permite gerar imagens de itens do Roblox (modelos, ferramentas, acessórios, peças) com fundo transparente, utilizando apenas capturas de tela do Roblox Studio. Ele funciona clonando o item, movendo-o para uma área vazia no céu e fotografando-o contra fundos preto e branco para calcular a transparência pixel a pixel. É útil para criar ícones, miniaturas, imagens de loja, sprites ou 'renders' de itens, especialmente em lote. O skill é otimizado para rodar no modelo Claude Haiku 5.5 e requer as ferramentas MCP do Roblox Studio (execute_luau, screen_capture). A saída é uma imagem PNG de qualidade sem fundo.

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

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

Perguntar na sua IA favorita

Abre um novo chat com esta habilidade de agente já pré-carregada.

Documentação

Render de céu Roblox

Produz um recorte transparente em PNG de um item de um lugar Roblox usando apenas capturas de tela do Studio. Nenhum plugin é necessário; o script incluído reproduz a técnica que o plugin Photobooth usa.

Primeiro: verifique o modelo

Este skill foi feito para rodar no Claude Haiku 5.5 (claude-haiku-5-5). O Haiku é significativamente mais rápido, e o trabalho aqui é um procedimento fixo e roteirizado que não precisa de um modelo maior como Opus 5.5, que é feito para codificação e tarefas mais complexas. Antes de fazer qualquer outra coisa, verifique qual modelo você é. Se você não for Claude Haiku 5.5, pare e peça ao usuário para trocar antes de começar: no Claude Code eles podem executar /model claude-haiku-5-5, ou selecionar Haiku 5.5 no seletor de modelo do aplicativo desktop. Continue quando eles tiverem trocado. Se eles disserem que querem ficar no modelo atual, prossiga com ele.

Como funciona (para que você possa raciocinar sobre falhas)

O Roblox não pode renderizar com fundo transparente, então a transparência é medida:

  1. Uma cópia do item é fotografada na frente de um fundo preto liso e novamente na frente de um fundo branco liso. Nada mais muda entre as duas fotos.
  2. Por pixel: se ele for branco em uma e preto na outra, é fundo (alfa 0). Se for idêntico em ambas, é item sólido (alfa 1). No meio, alfa = 1 - (branco - preto) / 255. Isso lida com vidro e bordas anti-aliasing.
  3. A cor vem de uma terceira foto em preto com a aparência própria do lugar (bloom, correção de cor), dividida pelo alfa.

Tudo que faz isso falhar é algo que difere entre as duas fotos além do fundo, ou algo diferente do item que está na imagem. É sobre isso que a próxima seção se trata.

Esteja ciente do mundo

Uma captura de tela captura tudo que está na frente da câmera e tudo que está iluminando o item. Antes e depois de um render, pense em:

  • Coisas no caminho. Construções, terreno, outros modelos, a baseplate, partículas de objetos próximos. O script evita tudo isso nunca fotografando o item onde ele está: ele clona o item, levanta o clone para o céu vazio e coloca um plano de fundo bem atrás dele. Ele verifica se o local de estagiamento está realmente vazio (sem partes dentro de clearance studs, nada diretamente acima, o chão bem abaixo) e sobe 1500 studs de cada vez até que esteja. Leia a linha estagiado em Y= no relatório; se ela disser que a altitude forçada não está clara, ou não conseguir encontrar céu claro, escolha uma altitude você mesmo depois de olhar o quão alto o mapa é.
  • Coisas que projetam sombras. Algo entre o item e o sol projeta sombra nele mesmo se estiver fora do quadro. O script faz raycast em direção ao sol e informa se algo for atingido.
  • De onde a luz vem. O sol (ou lua) é a única luz direcional e está sempre acima do horizonte. lighting = "studio" (padrão) move temporariamente a hora do dia para que o sol fique sobre o ombro esquerdo da câmera, e aumenta a luz ambiente, para que todo item seja iluminado da mesma forma independentemente do lugar. lighting = "world" mantém a iluminação do lugar; o relatório então informa se o sol está na frente ou ATRÁS do lado visível. Atrás significa um render escuro.
  • Olhando para cima. lookStraightUp = true rotaciona o clone e aponta a câmera para +Y, então a única coisa que pode ficar atrás do item é o céu. Use quando o mapa for tão alto ou desordenado que céu claro ao lado seja difícil de encontrar. O custo: a câmera agora está embaixo, o sol nunca pode ficar atrás dela, e o alinhamento da luz chave do relatório cai (cerca de 0,6 em vez de 1,0). Na prática, ainda renderiza bem; só tem menos controle sobre sombreamento.
  • Efeitos do céu. Névoa atmosférica, nuvens, raios solares, profundidade de campo e desfoque ficam entre a câmera e o fundo. O script os remove ou desabilita durante as fotos.
  • Brilho. O fundo é uma parte Neon porque Neon ignora iluminação e sombras, dando uma cor exata e lisa. Mas Neon brilha, e o brilho se espalha sobre partes finas do item e as faz parecer translúcidas. Desligar os BloomEffects não para isso (o motor cai para um brilho padrão); um BloomEffect habilitado com Intensidade 0 e Tamanho 0 para, e o script adiciona um para as fotos de medição. Se um item sólido sair fantasmagórico, esta é a primeira coisa a suspeitar.
  • Coisas que se movem. Partículas, feixes, fogo, fumaça e rastros estão em lugares diferentes em cada foto e se transformam em bolhas pretas. Eles são desligados no clone por padrão. Com particles = true eles são mantidos e congelados durante as fotos.
  • Sobreposições do Studio. Caixas de seleção, contornos de hover e telas do StarterGui estariam na captura de tela. O script limpa a seleção e as esconde, e só usa um quadrado centralizado do viewport, longe dos widgets de canto.
  • O viewport em si. O tamanho da saída é limitado pelo lado mais curto do viewport do Studio, e o Studio deve estar no modo Editar com a visualização 3D visível. O Modo Gráfico "OpenGL" não funciona.

O script coloca cada um deles de volta quando termina, inclusive em erro. O item original nunca é movido ou modificado.

Fluxo de trabalho

Não execute comandos de temporização enquanto o trabalho estiver em andamento. O Claude Code adiciona timestamp a cada chamada, então o tempo de execução é medido a partir da transcrição da sessão em uma única chamada no final (passo 7, e Benchmark abaixo). Todo trabalho termina com essa temporização, e a mensagem final para o usuário termina com o tempo total. Não pule e não espere ser perguntado.

  1. Encontre a instância do Studio (list_roblox_studios) e confirme qual lugar usar se mais de um estiver aberto. Então execute esta verificação de status através de execute_luau (datamodel Edit), com want definido como o início de uma palavra no nome do item, em minúsculas. Em uma chamada, ele informa se o renderizador e exportador já estão instalados nesta sessão do Studio e lista itens correspondentes (os filhos de um item são omitidos, então uma pasta de ferramentas aparece uma vez, com sua contagem de filhos):

    local want = "espada" -- início de uma palavra no nome do item, minúsculo; "" para pular a busca
    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 -- apenas no início de uma palavra: "Gatos", "Gato Voador", "GatoTorre", não "Localização"
          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() .. " filhos)")
                if #out >= 40 then table.insert(out, "(mais; estreite want)") break end
             end
          end
       end
    end
    return "renderer=" .. tostring(shared.SkyRenderVersion) .. " exporter=" .. tostring(shared.SkyExportVersion) .. "\n" .. table.concat(out, "\n")
    

    As versões atuais são renderer=1 exporter=1. Um número que corresponde significa que o script está instalado e atualizado: pule sua Leitura e colagem (passo 2 abaixo para o renderizador, passo 1 de "Salvando PNGs sem HTTP" para o exportador). nil ou um número diferente significa cole-o. Se a lista não tornar o item claro, recorra a search_game_tree.

  2. Carregue o renderizador, a menos que a verificação de status mostre renderer=1. Leia scripts/render_item.luau e passe o arquivo inteiro, inalterado, como o code de execute_luau (datamodel Edit). Com target = nil ele apenas instala três funções e retorna "SkyRender instalado". Elas permanecem disponíveis pelo resto da sessão do Studio:

    • shared.SkyRender(config) renderiza um item, retorna um relatório de texto
    • shared.SkyRenderBoard(names) mostra resultados para inspeção
    • shared.SkyRenderClear() remove o quadro e todos os resultados armazenados

    Se uma chamada posterior disser que shared.SkyRender é nil (Studio foi reiniciado), cole o arquivo novamente. Quando você alterar render_item.luau ou export_nohttp.luau, aumente shared.SkyRenderVersion ou shared.SkyExportVersion no final do arquivo e o número acima, juntos.

  3. Renderize. Cada chamada é curta:

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

    Para um lote, faça um loop em uma chamada e junte os relatórios:

    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. Leia o relatório. Um bom parece com:

    OK 512x512px "banana" -> CoreGui.SkyRenderResults.banana
    raio do item 2.45 studs, câmera a 18.7 studs de distância, estagiado em Y=3001
    iluminação do studio, alinhamento da luz chave 1.00 (1 = ideal)
    cobertura: 8.8% opaco, 0.4% semitransparente, limites x 44-474 y 148-365
    calibração do fundo (neon) branco=255,255,255 preto=0,0,0 osScale=1
    tempo 3.41s (relógio de parede: estagiamento, fotos, composição e restauração)
    

    Verifique: calibração é 255 / 0; limites não tocam 0 ou size-1 (cortado); a divisão entre opaco e semitransparente faz sentido para o item (uma espada sólida deve ser quase toda opaca, um vidro deve ter uma parte semitransparente real). Aja sobre qualquer coisa sob NOTAS:.

  5. Olhe. Números não dizem se o item está virado para o lado errado. Chame shared.SkyRenderBoard({ "banana" }) (ou {} para todo resultado). Ele constrói um quadro temporário no céu mostrando cada imagem sobre magenta e sobre cinza escuro, e retorna a posição exata camera_position e look_at_position para passar para screen_capture. screen_capture não desenha GUIs, e é por isso que o quadro usa decalques em partes. Julgue: ângulo certo? item inteiro no quadro? bordas limpas em ambos os fundos? lado iluminado visível? sem bolhas estranhas? Re-renderize com opções alteradas até que esteja certo, então shared.SkyRenderBoard(nil) para remover o quadro.

  6. Entregue (veja abaixo), então shared.SkyRenderClear() a menos que o usuário queira os resultados mantidos na sessão.

  7. Temporize (obrigatório, todo trabalho). Como a última chamada de ferramenta do trabalho, depois que os PNGs são salvos e o Studio está limpo, execute python scripts/benchmark.py. Então termine sua mensagem final para o usuário com o resultado, neste formato:

    Tempo total: 2m 10s para 6 imagens (21.6 s por imagem), execução fria.
    

    Adicione a tabela de fases acima dessa linha quando o trabalho foi mais de uma única imagem ou quando algo foi lento. O total é a última linha da mensagem, nunca omitido, e nunca apenas em um resultado de ferramenta que o usuário pode não ver.

Benchmark (prompt do usuário para a última mensagem)

Sempre, no final de cada trabalho (passo 7 do fluxo de trabalho), quer o usuário tenha pedido ou não. Custa uma chamada de ferramenta: o Claude Code escreve cada prompt, chamada de ferramenta e resultado na transcrição da sessão (~/.claude/projects/<project>/<session>.jsonl) com um timestamp, e scripts/benchmark.py os lê.

  1. Quando: como a última chamada de ferramenta da vez do trabalho. Ela vê todas as chamadas até e incluindo ela mesma, então apenas a mensagem final (alguns segundos) é omitida. Se o usuário depois perguntar sobre um trabalho finalizado, execute-o novamente para obter o valor completo.
  2. Execute: python scripts/benchmark.py escolhe a sessão mais recente que executou este skill e seu último pedido de render. Passe um id de sessão ou caminho .jsonl para escolher outra sessão, --list e --turn <n> para escolher outro pedido nela, e --calls para listar cada chamada com sua temporização.
  3. Relatório: a tabela de fases (encontrar, configurar, render, exportar, montar, limpar, contabilidade, resposta), cada uma dividida em tempo do modelo (pensar e escrever a chamada, incluindo as colagens de script) e tempo da ferramenta (esperar por resultados). Chamadas paralelas são contadas uma vez, então as fases somam o total. Depois a contagem de imagens e segundos por imagem.
  4. Detalhe do render (opcional): a linha time em cada relatório de render é medida dentro do Studio ao redor desse render. Use-a para comparar renders individuais.

Diga se a execução foi fria (renderizador e exportador colados neste trabalho) ou quente (já instalados), já que as colagens são a maior parte do tempo de configuração de uma execução fria. Finalize a mensagem final com a linha Tempo total: do passo 7.

Opções

Todas opcionais exceto target. Padrões entre colchetes.

OpçãoSignificado
targetA Instância a renderizar. Modelos, partes, Ferramentas, Acessórios e Pastas funcionam.
name ["render"]Rótulo para o resultado. Reutilizar um nome substitui o resultado anterior.
viewDir [(-1, 0.6, -1)]Direção do item em direção à câmera, em eixos mundiais como o item está atualmente. (0,0,-1) olha para sua face -Z; aumente Y para olhar de cima.
up [(0,1,0)]Qual direção mundial é para cima na imagem. Use para colocar uma espada na diagonal, etc.
fov [20]Estreito é plano e parecido com ícone. 50-70 exagera a perspectiva.
size [512]Saída quadrada em pixels, limitada pelo lado mais curto do viewport.
padding [0.08]Margem como uma fração da imagem.
lighting ["studio"]"studio" ou "world", veja acima.
particles [false]Manter efeitos de partícula (congelados) em vez de desligá-los.
lookStraightUp [false]Estagiar com a câmera apontando para +Y, veja acima.
altitude [auto]Forçar a altura de estagiamento.
clearance [400]Studs de espaço vazio necessários ao redor do local de estagiamento.
backdrop ["neon"]"neon", ou "fog" (um plano simples escondido em névoa sólida) como fallback.
refit [true]Testar foto primeiro, então re-enquadrar para que os pixels visíveis preencham a imagem. As caixas delimitadoras das partes são soltas, então sem isso os itens saem pequenos.
keepLook [true]Pegar cor de uma foto com o bloom do lugar e correção de cor ativos. Desligar usa a medição de transparência para cor também (mais rápido, mas sem bloom, efeitos de pós-processamento).
ambient [Color3.fromRGB(204, 204, 204)]Cor da luz ambiente durante a foto de cor. Ignorado se keepLook=true.
resultsParent [game.CoreGui]Onde a placa de resultados e os itens estagiados vão. Prefira CoreGui para que não salvem.

Salvando PNGs sem HTTP

O Roblox Studio pode salvar imagens sem HTTP via file.write em scripts, mas file.write não é executado no sandbox Edit onde o script do renderizador roda. As imagens são armazenadas em CoreGui.SkyRenderResults.<name> e o exportador deve ser executado do Run sandbox file.write. Ele é instalado uma vez por sessão de Studio e então invocado quando você precisa de PNGs.

  1. Instale o exportador quando o passo de status acima mostrar exporter=nil ou um número diferente. Leia scripts/export_nohttp.luau e passe o arquivo inteiro como code de execute_luau com sandbox="Run". Ele adiciona uma função shared.SkyExportAll() que escreve todos os resultados de render para scripts/exports/<name>.png.

  2. Use. Depois de renderizar e inspecionar (passos 3-5), execute:

    return shared.SkyExportAll()
    

    em sandbox="Run". Ele retorna os caminhos:

    salvo scripts/exports/banana.png (512x512)
    

    Então você pode pegar os arquivos de imagem com browse_folder.

Falhas comuns

  • renderer=nil depois de colar. O caminho do arquivo está errado, ou você colou sandbox "Edit" para o script errado. Verifique se você leu scripts/render_item.luau literalmente: o nome do arquivo e o caminho devem ser exatos.
  • shared.SkyRender is nil. O Studio foi reiniciado ou o script nunca foi colado nesta sessão. Cole novamente.
  • O tempo é longo. Execuções frias duram ~40s devido a colagens de script. Execuções quentes são ~8s. Se ambas forem lentas, a máquina pode estar sobrecarregada.
  • A imagem está cortada. O viewport do Studio ou sua configuração de "Interface" (Guias UI) estreita o viewport 3D. Redimensione o Studio para dar mais espaço ao viewport ou use um size menor.
  • Itens de vidro saem opacos ou com bordas sujas. A medição alfa falha em superfícies que refratam ou refletem o fundo de forma diferente dependendo da cor do fundo. Use backdrop="fog" como fallback.
  • O progresso trava. O Studio pode travar se o script for muito longo. Reinicie o Studio e cole novamente.
  • Sobreposições antigas. O script desliga a seleção, contornos e StarterGui, mas a janela de código pode estar sobre o viewport. O usuário deve mover ou fechar janelas flutuantes antes de renderizar.

Habilidades Relacionadas