Animação para Roblox

GFX no Roblox: rig posado no Studio antes do Blender

Seu GFX sai com o avatar sem textura, em pose de boneco de vitrine e com render granulado. O conserto começa dentro do Studio: montar o rig com acessórios, posar pelos Motor6D e congelar antes de exportar.

por Gustavo Cantino10 min de leituraIntermediário

Rascunho assistido por IA, revisado, testado e editado por um humano antes de publicar. Ver Política Editorial. · Revisado por Gustavo Cantino, 15/09/2026 Política Editorial

Placa tipográfica com a categoria Animação para Roblox em destaque, o nível intermediário abaixo, e um campo de barras verticais verde-limão sobre fundo escuro.

O GFX não está ruim no Blender. Ele está ruim a 150 pixels

Você quer fazer GFX — aquela arte com o avatar renderizado que vira ícone e thumbnail do jogo — e travou em um destes três pontos: o avatar chega no Blender branco, sem camisa e sem acessório; a pose fica de boneco de vitrine parado em T; ou o render sai granulado, com cara de plástico molhado. Os três têm conserto, e nenhum deles é "compre uma placa de vídeo melhor". A ordem que resolve é montar o rig posado dentro do Studio, exportar já posado, e deixar pro Blender só o que o Blender faz bem: luz, câmera e ruído.

Antes de mexer em qualquer coisa, entenda onde esse arquivo vai parar. O ícone da experiência é enviado em 512x512, mas na lista de jogos do app ele aparece perto de 150 pixels de lado — menos de 10% da área que você está olhando no monitor. A thumbnail vai em 1920x1080 e é exibida em um card bem menor que isso na home. Ou seja: aquele detalhe de textura do tênis que te custou 40 minutos não existe pro jogador. O que existe é silhueta, contraste e rosto.

O prejuízo é medível. GFX encomendado custa dinheiro, e GFX feito por você custa de 4 a 8 horas na primeira vez. Se depois disso a taxa de clique do ícone não mexe, você não perdeu só as horas: perdeu a janela do lançamento, que é quando o algoritmo de descoberta te dá impressão de graça. Vale mais fazer um GFX legível em 150px em 90 minutos e medir, do que um trabalho de 6 horas que ninguém consegue decifrar no tamanho real.

Oito passos, na ordem que economiza retrabalho

1. Monte o avatar dentro do Studio, não baixe do site. A página de avatar do Roblox te dá um render 2D, não uma malha. O que você quer é o personagem 3D com camisa, calça, acessórios e layered clothing já aplicados. No servidor, Players:CreateHumanoidModelFromUserId devolve exatamente isso: um Model pronto, montado com o mesmo pipeline que o jogo usa. Isso elimina o cenário mais comum de "avatar chegou branco no Blender" — ele chegou branco porque você exportou um Dummy da aba Avatar, que não tem roupa nenhuma.

2. Pose pelos Motor6D, nunca pelo CFrame da parte. Esse é o erro que faz gente desistir de GFX. Você gira o braço no Explorer, ele volta sozinho, você gira de novo, ele volta de novo:

Trecho ERRADO — Command Bar do Studio
local rig = workspace.RigGFX
-- o braço gira por 1 frame e o Motor6D puxa ele de volta pro lugar
rig.RightUpperArm.CFrame = rig.RightUpperArm.CFrame * CFrame.Angles(0, 0, math.rad(45))

No R15 cada membro está preso ao pai por um Motor6D (RightShoulder, LeftElbow, Waist, Neck...). Enquanto o Motor6D existir, ele manda no CFrame da parte filha todo frame. Quem você precisa girar é o C0 do Motor6D, em graus convertidos com math.rad. Aí a pose gruda.

3. Congele a pose antes de exportar. Depois de escrever nos C0, espere um Heartbeat para o motor de física recalcular as posições no mundo e só então marque Anchored = true em todas as BaseParts. Se você ancorar antes, o Motor6D não consegue mover nada e o rig exporta em T-pose mesmo com o código "certo". A ordem importa: posa, espera um frame, ancora.

4. Traga o modelo pro Edit mode. O rig nasce durante o Run (F8), e tudo que acontece no Run some quando você aperta Stop. Com o jogo ainda rodando, selecione o modelo no Explorer, Ctrl+C, Stop, Ctrl+V. Agora ele está no seu arquivo de verdade, posado e ancorado, e você pode salvar esse .rbxl como base pros próximos GFX.

5. Exporte e importe com atenção à escala. Com o modelo selecionado, File → Export Selection → .obj. O Studio cospe o .obj, o .mtl e um PNG de atlas com todas as texturas. No Blender, importe e cheque a altura do rig no painel N: se o avatar tem menos de 1 unidade de altura, luzes medidas em Watts vão parecer apagadas e você vai achar que errou a iluminação quando errou foi a escala. Escale o rig para algo em torno de 2 unidades e trabalhe com distâncias em metros.

6. Conserte os materiais em 2 cliques por textura. O importador de .obj entrega o material com interpolação Linear, que borra o rosto e as texturas desenhadas à mão — e todo rosto de Roblox é pixel art pequena esticada. No nó Image Texture, troque Interpolation para Closest. Para cabelo, óculos e qualquer acessório com fundo transparente, ligue o Alpha da imagem no Alpha do Principled BSDF e coloque o Blend Mode em Alpha Clip com threshold 0.5. Sem isso, o cabelo vira um quadrado preto — o segundo maior motivo de GFX abandonado no meio.

7. Três luzes e uma lente longa. Key light: Area, 45 graus à frente e acima do rosto, a uns 3 unidades, com o tamanho da luz grande (1 a 2 unidades) pra sombra ficar macia. Fill: do lado oposto, com um quarto da potência da key, só pra sombra não virar buraco preto. Rim: atrás e um pouco acima, potência alta e cor saturada — é o rim que desenha a silhueta e é ele que faz o personagem "descolar" do fundo em 150 pixels. Na câmera, use 85mm. Lente de 24mm ou 35mm entorta o rosto e denuncia amador na hora.

8. Renderize com alpha e componha depois. Em Render Properties, Cycles, Max Samples entre 128 e 192, Noise Threshold 0.02 e denoiser ligado (OptiX se você tem NVIDIA). Em Film, marque Transparent. Salve em PNG com RGBA. O fundo — gradiente, cenário, blur — você monta em cima no Photopea ou no Photoshop, em camada separada. Dobrar samples de 192 para 4096 multiplica o tempo de render por mais de 20 e não muda um pixel visível depois que a imagem é reduzida pro tamanho do card.

O script que monta e posa o rig antes da exportação

O artefato abaixo é um Script para ServerScriptService. Ele faz os passos 1 a 3 sozinho: busca o avatar por UserId, aplica a tabela de ângulos nos Motor6D pelo nome, espera um frame e ancora tudo.

Troque USER_ID pelo ID do perfil que você quer renderizar (o número que aparece na URL do perfil). A tabela POSE está em graus e cada entrada é o nome exato do Motor6D no R15 — mexa em RightShoulder e LeftShoulder primeiro, são eles que dão atitude à pose. Valores entre -90 e 90 cobrem quase tudo; acima disso o ombro atravessa o tronco.

Depois de colar, aperte F8 (Run, não Play). O Output vai imprimir quantas juntas foram posadas — se imprimir zero, o avatar era R6 e os nomes das juntas são outros. Com o rig no Workspace, Ctrl+C, Stop, Ctrl+V, e siga pro Export Selection.

O segundo artefato é a folha de valores do Blender: câmera, as três luzes e as configurações de render. Não é arquivo pra importar — são os números que você digita nos painéis, reunidos num lugar só pra você não ficar caçando tutorial no meio do trabalho.

O erro caro: julgar o GFX em tela cheia

O erro que custa mais caro não é técnico. É aprovar o GFX olhando ele em 1920x1080 no seu monitor de 27 polegadas. Em tela cheia, qualquer render com denoiser parece bom. O jogador nunca vai ver essa imagem.

O teste que corrige isso leva 30 segundos: abra o PNG final, reduza para 150 pixels de largura, afaste-se meio metro da tela e conte até três. Se em três segundos você não consegue dizer quem é o personagem e qual é a ação, o GFX falhou — e falhou por um destes motivos, nesta ordem de frequência: o personagem está pequeno demais no quadro, o fundo tem contraste parecido com o do personagem, ou não existe rim light separando os dois.

O conserto é sempre o mesmo e é rápido: aproxime a câmera até a cabeça ocupar perto de 40% da altura da imagem, escureça ou desfoque o fundo, e aumente a potência do rim. Nada disso exige renderizar de novo em alta — teste com resolução em 25% e samples em 32, que sai em segundos, e só renda a versão final quando o teste de 150px passar.

O segundo erro caro mora no passo 8: renderizar sem Film Transparent. Você fecha o Blender, o cliente (ou você mesmo, dois dias depois) pede outro fundo, e não dá — o fundo está queimado no pixel junto com a sombra e o reflexo. Refazer é meia hora. Marcar uma caixa de seleção é meio segundo.

O que medir depois de trocar o ícone

GFX não se avalia por "ficou bonito". Se avalia por taxa de clique. No Creator Dashboard, na aba de Analytics, você tem impressões e plays da sua experiência — a razão entre os dois é o número que o ícone mexe. Anote o valor dos 7 dias anteriores à troca, publique o ícone novo e compare com os 7 dias seguintes, sem mexer em mais nada no mesmo período.

Troque uma variável por vez. Se você sobe ícone novo, thumbnail nova e muda o nome do jogo na mesma tarde, você nunca vai saber qual dos três moveu o ponteiro — e vai repetir os outros dois de graça no próximo lançamento. Pose e enquadramento primeiro, fundo depois, texto por último.

E guarde o .blend com as luzes e a câmera já posicionadas. O segundo GFX, reusando a cena e trocando só o rig exportado, sai em 20 ou 30 minutos. É isso que separa quem faz um GFX de quem consegue testar quatro thumbnails diferentes em uma semana.

Artefato pronto pra usar

Script em ServerScriptService
lua
--!strict
--[[
	GFX Rig Builder
	Onde mora: um Script (não LocalScript) dentro de ServerScriptService.
	Como usar:
	  1. Troque USER_ID pelo ID do perfil (o número na URL do perfil do Roblox).
	  2. Ajuste POSE (valores em GRAUS).
	  3. Aperte F8 (Run), NÃO Play.
	  4. Com o Run ligado: selecione RigGFX no Explorer, Ctrl+C, Stop, Ctrl+V.
	  5. File > Export Selection > .obj
]]

local Players = game:GetService("Players")
local RunService = game:GetService("RunService")

-- ID do dono do avatar que você vai renderizar
local USER_ID: number = 156 -- builderman, troque pelo seu

type Rot = { x: number, y: number, z: number }
type Pose = { [string]: Rot }

-- Nomes exatos dos Motor6D do R15. Valores em graus.
-- Comece mexendo em RightShoulder / LeftShoulder: são eles que dão atitude.
local POSE: Pose = {
	Waist          = { x = 0,   y = -12, z = 0 },
	Neck           = { x = 6,   y = 14,  z = 0 },
	RightShoulder  = { x = 0,   y = 0,   z = 65 },
	LeftShoulder   = { x = 0,   y = 0,   z = -38 },
	RightElbow     = { x = -22, y = 0,   z = 0 },
	LeftElbow      = { x = -55, y = 0,   z = 0 },
	RightHip       = { x = -8,  y = 0,   z = 0 },
	LeftHip        = { x = 12,  y = 0,   z = 0 },
	RightKnee      = { x = -14, y = 0,   z = 0 },
	LeftKnee       = { x = -6,  y = 0,   z = 0 },
}

-- Gira o C0 de cada Motor6D cujo nome aparece na tabela POSE.
-- Girar o CFrame da parte direto NÃO funciona: o Motor6D reescreve no frame seguinte.
local function applyPose(model: Model, pose: Pose): number
	local applied = 0
	for _, inst in model:GetDescendants() do
		if inst:IsA("Motor6D") then
			local rot = pose[inst.Name]
			if rot then
				inst.C0 = inst.C0 * CFrame.Angles(math.rad(rot.x), math.rad(rot.y), math.rad(rot.z))
				applied += 1
			end
		end
	end
	return applied
end

-- Congela o rig no lugar. SÓ depois da pose ter sido aplicada e de 1 frame de física.
local function freeze(model: Model)
	for _, inst in model:GetDescendants() do
		if inst:IsA("BasePart") then
			inst.Anchored = true
			inst.CanCollide = false
		end
	end
end

local ok, result = pcall(function()
	-- Monta o avatar completo: camisa, calça, acessórios e layered clothing.
	return Players:CreateHumanoidModelFromUserId(USER_ID)
end)

if not ok or typeof(result) ~= "Instance" then
	warn("[GFX] Falhou ao montar o avatar do UserId " .. tostring(USER_ID) .. " -> " .. tostring(result))
	return
end

local rig = result :: Model
rig.Name = "RigGFX"
rig.Parent = workspace
rig:PivotTo(CFrame.new(0, 5, 0))

-- Impede que a state machine do Humanoid empurre o personagem de volta pro Idle.
local humanoid = rig:FindFirstChildOfClass("Humanoid")
if humanoid then
	humanoid.AutoRotate = false
	humanoid:ChangeState(Enum.HumanoidStateType.Physics)
	local animator = humanoid:FindFirstChildOfClass("Animator")
	if animator then
		animator:Destroy() -- nada de animação padrão sobrescrevendo a pose
	end
end

local applied = applyPose(rig, POSE)
RunService.Heartbeat:Wait() -- deixa os Motor6D recalcularem o mundo antes de ancorar
freeze(rig)

if applied == 0 then
	warn("[GFX] Nenhuma junta posada. O avatar provavelmente é R6 e usa outros nomes de Motor6D.")
else
	print(string.format("[GFX] RigGFX pronto no Workspace com %d juntas posadas. Ctrl+C agora, Stop, Ctrl+V.", applied))
end
Folha de valores para digitar no Blender (Render Properties, Light Data e Image Texture)
json
{
  "escala_do_rig": { "altura_alvo_unidades": 2.0, "observacao": "se as luzes em Watts parecerem fracas, o problema e escala, nao potencia" },
  "camera": { "focal_length_mm": 85, "distancia_unidades": 6.0, "altura_alvo": "linha dos olhos do rig", "cabeca_ocupa_da_altura": 0.4 },
  "luzes": [
    { "nome": "key", "tipo": "AREA", "power_w": 900, "size": 1.5, "angulo_horizontal_graus": 45, "altura_unidades": 4.0, "cor": "#FFFFFF" },
    { "nome": "fill", "tipo": "AREA", "power_w": 220, "size": 2.0, "angulo_horizontal_graus": -50, "altura_unidades": 2.0, "cor": "#CFE2FF" },
    { "nome": "rim", "tipo": "AREA", "power_w": 1400, "size": 0.8, "angulo_horizontal_graus": 165, "altura_unidades": 3.5, "cor": "#FF6A3D" }
  ],
  "materiais": { "image_texture_interpolation": "Closest", "blend_mode": "Alpha Clip", "alpha_threshold": 0.5, "roughness_pele": 0.55, "roughness_tecido": 0.75 },
  "render": { "engine": "CYCLES", "max_samples": 192, "noise_threshold": 0.02, "denoiser": "OPTIX", "film_transparent": true, "view_transform": "AgX ou Filmic" },
  "saida": { "icone_px": [512, 512], "thumbnail_px": [1920, 1080], "formato": "PNG", "color": "RGBA", "teste_rapido": { "resolution_percent": 25, "max_samples": 32 } },
  "checklist_final": ["reduzir para 150px e identificar o personagem em 3s", "fundo com contraste diferente do personagem", "rim light visivel na silhueta"]
}
Neste artigo

Continue por aqui