Docs do Nodaro
DocumentaçãoReferência de nósModelosAgentes de IA (MCP)DesenvolvedoresSelf-hostingPesquisa
SDK para TypeScript

Cenas 3D

Em TypeScript, gere uma cena 3D editável com um prompt, edite-a, renderize-a em MP4 e execute a Renderização 3D Pro com client.scene3d e os nós de cena 3D.

client.scene3d faz cenas 3D editáveis: cria uma cena a partir de um prompt, edita a cena por instrução ou por operações exatas e renderiza uma revisão em MP4. Também executa a Renderização 3D Pro (3D Render Pro), uma operação que cria uma cena e a renderiza, e lê os arquivos que uma renderização entregou. Os métodos usam os nós Gerar cena 3D (Generate 3D Scene), Editar cena 3D (Edit 3D Scene), Renderizar vídeo (Render Video) e Renderização 3D Pro. Veja a API REST de cenas 3D para os endpoints.

Métodos

MétodoO que faz
capabilities()Lê quais mecanismos e opções Pro esta instalação oferece
generate(params) e generateAndWait()Cria uma nova cena a partir de um prompt
edit(params) e editAndWait()Faz uma nova revisão de uma cena
render(params) e renderAndWait()Renderiza uma revisão em MP4, sem etapa de criação
applyEdits(revisionId, params)Salva edições exatas sem etapa de criação e sem cobrança
quotePro(params)Calcula o preço de uma execução da Renderização 3D Pro
runPro(params, options?)Inicia uma execução cotada da Renderização 3D Pro
renderProAndWait(params, options?)Faz a cotação, executa e aguarda em uma chamada
getDelivery(jobId)Lê os arquivos que uma renderização entregou
deliveryAssetBytes(jobId, asset, options?)Baixa um arquivo entregue
retainedRecipe(jobId, options?)Lê a receita que uma execução Pro recusada guardou
assetBytes(revisionId, asset, options?)Baixa um arquivo de uma revisão de cena
sourceBytes(revisionId, options?)Baixa o arquivo-fonte nativo de uma revisão

Do prompt ao MP4

const created = await client.scene3d.generateAndWait({
  prompt: "Orbit a single box on a floor over four seconds",
  durationSeconds: 4,
  fps: 24,
  aspectRatio: "16:9",
})

const edited = await client.scene3d.editAndWait({
  scenePlan: created.scenePlan,
  expectedRevisionId: created.scenePlan.revisionId,
  operations: [{ op: "set-camera", changes: { focalLengthMm: 50 } }],
})

const clip = await client.scene3d.renderAndWait({ planType: "3d-scene", plan: edited.scenePlan })
console.log(clip.videoUrl)

Os mesmos três passos funcionam com client.nodes.runAndWait() e os tipos de nó generate-3d-scene, edit-3d-scene e render-video, que têm parâmetros tipados. Para permitir que as pessoas girem e ajustem uma cena na sua página, use a incorporação da prévia 3D. Ela não precisa de uma cópia do renderizador nem de tokens nas mensagens.

client.scene3d

capabilities()

Retorna o que esta instalação consegue criar e renderizar (GET /v1/3d-scene/capabilities).

capabilities(): Promise<Scene3DCapabilities>
const caps = await client.scene3d.capabilities()
if (caps.advanced) console.log(caps.advanced.engines) // for example ["blender-cloud"]
if (caps.pro) console.log("3D Render Pro is available")
  • basic é o mecanismo determinístico: { available, sceneSchemaVersions }.
  • advanced lista os mecanismos opcionais, ou é null quando não há nenhum. Um mecanismo explícito que não está disponível é recusado antes de uma geração Basic ou das verificações de créditos dela.
  • pro descreve a Renderização 3D Pro: os mecanismos, os perfis de qualidade, os estilos e as proporções que esta instalação oferece. Ofereça só esses. Quando pro está ausente, o nó não está disponível.

generate(params) e generateAndWait(params, options?)

Cria uma nova cena a partir de um prompt. generate() retorna o job imediatamente; generateAndWait() espera por ele e resolve com scenePlan e um changeSummary opcional.

generate(params: GenerateScene3DParams): Promise<RunNodeResult>
generateAndWait(params: GenerateScene3DParams, options?: RunAndWaitOptions): Promise<Scene3DJobOutput>

Prop

Type

const { scenePlan } = await client.scene3d.generateAndWait({
  prompt: "A paper boat drifts across a puddle as rain starts",
  references: [{ id: "look", kind: "image", role: "appearance", url: moodImageUrl }],
})

inputAssets seleciona modelos GLB que você tem permissão de usar, pelos IDs imutáveis deles; references continua trazendo as imagens de aparência e os vídeos de movimento. Não envie URLs nem hashes dos arquivos: o servidor fornece os registros dos bytes. O Basic e os mecanismos que não conseguem importar recusam inputAssets antes de cobrar. Reutilize as mesmas seleções quando enviar uma cotação Pro.

edit(params) e editAndWait(params, options?)

Faz uma nova revisão de uma cena. A cena que você passa nunca é alterada. Dê uma instrução em prompt ou operations exatas, nunca os dois.

edit(params: EditScene3DParams): Promise<RunNodeResult>
editAndWait(params: EditScene3DParams, options?: RunAndWaitOptions): Promise<Scene3DJobOutput>

Prop

Type

const { scenePlan: next, changeSummary } = await client.scene3d.editAndWait({
  scenePlan,
  expectedRevisionId: scenePlan.revisionId,
  prompt: "Make the rain heavier and lower the camera",
})

render(params) e renderAndWait(params, options?)

Renderiza em MP4 exatamente a revisão que você passa, sem criar nem reconstruir nada (POST /v1/render-video/plan).

render(params: { planType: "3d-scene"; plan: Scene3DPlan; workflowId?: string; nodeId?: string }): Promise<RunNodeResult>
renderAndWait(params: RenderScene3DParams, options?: RunAndWaitOptions): Promise<NodeJobOutput>

Prop

Type

const { videoUrl } = await client.scene3d.renderAndWait({ planType: "3d-scene", plan: scenePlan })

O preço segue a width e a height do próprio plano:

QuadroCréditosIdentificador de preço
Até 1920 pixels no lado maior55render-video
Maior, até 5,12 megapixels83render-video:3d-large
Acima de 5,12 megapixels138render-video:3d-xlarge

Leia os preços atuais desses identificadores com client.credits.modelCosts().

applyEdits(revisionId, params)

Salva edições exatas em uma revisão guardada, sem etapa de criação e sem cobrança (POST /v1/3d-scene/revisions/:id/edits). Retorna o novo scenePlan e um changeSummary.

applyEdits(revisionId: string, params: {
  newRevisionId: string
  expectedContentHash: string
  operations: Scene3DV2EditOperation[]
  lockedObjectIds?: string[]
}): Promise<{ scenePlan: Scene3DPlanV2; changeSummary: string }>

Prop

Type

const { scenePlan: saved } = await client.scene3d.applyEdits(revisionId, {
  newRevisionId: crypto.randomUUID(),
  expectedContentHash,
  operations,
})

Adote a cena retornada só se o usuário ainda estiver editando a revisão de onde você partiu. Os arquivos de geometria e de câmera são reutilizados. Os pôsteres, a validação e os downloads nativos só são anexados de novo depois de serem gerados para a nova revisão.

Renderização 3D Pro

A Renderização 3D Pro é uma única operação: entra uma source, e um único job termina com os dois resultados: o scenePlan (a composição exata) e o videoUrl (o MP4). O resultado também traz a revisão, um pôster, shotStills, a validação e os detalhes do renderizador. client.nodes.run("pro-3d-render") e runAndWait chegam à mesma rota com os mesmos parâmetros tipados.

Uma instalação sem o mecanismo responde 503 SCENE_CAPABILITY_UNAVAILABLE e nunca recorre ao Basic. Uma instalação sem preço configurado responde 503 price_not_configured antes de reservar qualquer coisa. Não há configuração de modelo nem de esforço: o servidor define o planejador.

quotePro(params)

Calcula o preço de uma execução sem iniciá-la. Não reserva nem gasta nada. A resposta tem quoteId, um teto em maxCredits, um breakdown para mostrar e o hash de entrada com o qual a execução é verificada depois.

quotePro(params: Pro3DRenderParams): Promise<Pro3DRenderQuote>

Prop

Type

source tem um destes três formatos:

  • { kind: "prompt", prompt, references? } cria uma nova cena.
  • { kind: "scene", revisionId, sourceJobId } sem editPrompt exporta uma cena guardada sem cobrança de criação. Adicionar editPrompt revisa a cena antes. sourceJobId é obrigatório para cenas Basic que existem só no histórico de jobs.
  • { kind: "local-export", exportId, connectionId } usa um computador pareado.
const quote = await client.scene3d.quotePro(params)
showPrice(quote.maxCredits, quote.breakdown)

runPro(params, options?)

Inicia uma execução cotada. O método precisa do quoteId de quotePro(), para que nenhuma execução comece com um preço que ninguém viu. Ele envia um Idempotency-Key: um novo a cada chamada, ou o seu em options.idempotencyKey. Reutilize o seu quando repetir uma chamada que esgotou o tempo.

runPro(params: Pro3DRenderParams & { quoteId: string }, options?: { idempotencyKey?: string }): Promise<RunNodeResult>

Prop

Type

await client.scene3d.runPro({ ...params, quoteId: quote.quoteId })

renderProAndWait(params, options?)

Faz a cotação quando params não tem quoteId, executa e espera: a operação inteira em uma chamada. Envia no máximo duas requisições e inicia um único job pago, admitido com base em um hash exatamente do que foi cotado.

renderProAndWait(params: Pro3DRenderParams | Pro3DRenderRunParams, options?: RunAndWaitOptions & { idempotencyKey?: string }): Promise<Pro3DRenderJobOutput>

Prop

Type

const caps = await client.scene3d.capabilities()
if (caps.pro?.available) {
  const shot = await client.scene3d.renderProAndWait({
    source: {
      kind: "prompt",
      prompt: "A red suitcase rolls behind a central pillar and reappears",
      references: [{ id: "look", kind: "image", role: "appearance", url: appearanceImageUrl }],
    },
    durationSeconds: 30,
    fps: 24,
    aspectRatio: "21:9",
    maxRepairPasses: 2,
    acceptedSceneSchemaVersions: [2],
  })
  console.log(shot.videoUrl)        // the MP4
  console.log(shot.sceneRevisionId) // export it again later, with no authoring charge
}

Imagens fixas das tomadas. shotStills é uma lista de { shotIndex, frame, assetId, url }, uma imagem fixa por tomada no quadro em que a tomada começa, feita pela mesma execução sem custo extra. Uma cena de uma única tomada tem uma, no quadro 0. Os resultados feitos antes de o campo existir não têm nenhuma, então leia shot.shotStills ?? [].

Cada url é um endpoint autenticado da sua instalação, não um link público. Busque-a com as mesmas credenciais que você usou na execução; uma tag img, ou um serviço de terceiros, recebe um 401:

for (const still of shot.shotStills ?? []) {
  const res = await fetch(still.url, { headers: { Authorization: `Bearer ${token}` } })
  const bytes = await res.arrayBuffer() // use the bytes, or store them where your pipeline can read them
}

Você ainda pode usar uma imagem fixa como referência de modelo: passe a URL dela do jeito que você a leu, em referenceImageUrls ou pela saída stills do nó. A plataforma concede a essa execução uma leitura breve desse único arquivo, em seu nome. A concessão expira minutos depois, então armazene a URL autenticada, nunca a concessão.

O que uma execução Pro informa sobre si mesma

Uma execução que criou uma cena informa o que presumiu e o que fez. Trate todos os campos como opcionais: uma instalação sem mecanismo avançado, ou um resultado mais antigo, não tem nenhum deles.

const summary = shot.metadata?.summary // what the planner says it built
const repairs = shot.repairPasses      // 0 means accepted the first time
const retries = shot.admissionRetries  // planner retries before a build
const mechanical = shot.mechanicalPasses
const restored = shot.restoredAssertions
const assumptions = (shot.validation?.warnings ?? [])
  .filter((w) => w.code === "SCENE_AUTHORING_ASSUMPTION")
  .map((w) => w.message)
  • Os avisos SCENE_AUTHORING_ASSUMPTION listam o que o prompt não disse e, por isso, a execução decidiu. Novos códigos de aviso podem ser adicionados: trate um código desconhecido como informação, não como erro.
  • repairPasses conta reparos, não passadas de criação, então 0 significa que a cena foi aceita de primeira. admissionRetries conta outra coisa: os pedidos repetidos de receita ao planejador antes de qualquer construção.
  • mechanicalPasses conta os reparos que o mecanismo aplicou a partir da solução do próprio compilador, sem o planejador. Eles têm uma franquia cotada própria de até 2, liberada quando não é usada, e cada um adiciona um aviso REMEDY_AUTO_APPLIED. Uma execução cotada antes de essa franquia existir cobrava essas passadas como reparos; leia a cotação que você recebeu para saber.
  • restoredAssertions lista as verificações obrigatórias que o mecanismo restaurou depois que o planejador alterou uma delas sem que isso tivesse sido pedido. Cada uma também adiciona um aviso ASSERTION_RESTORED.
  • Uma exportação só de renderização não criou nada, então não tem contagens nem resumo. Especialmente em mechanicalPasses, ausente não é 0.

Os resultados de generateAndWait() trazem os mesmos campos quando um mecanismo avançado criou a cena. O mecanismo Basic não consulta nenhum modelo e não traz nenhum deles.

Uma entrega que o revisor visual não aprovou

Um resultado concluído pode chegar sem a aprovação do revisor visual. Nos dois casos, o vídeo é real e os créditos foram gastos. metadata.review.verdict diz qual é o caso:

  • "refused": o orçamento de reparos foi gasto, todas as verificações obrigatórias passaram, e o revisor ainda assim fez objeções. A cena foi entregue com a recusa anexada.
  • "unavailable": a revisão não deu um veredito utilizável. reason é "provider" quando ela nunca chegou ao provedor, e "unusable" quando a resposta não pôde ser usada. attempts diz quantas vezes a revisão foi solicitada. Ninguém avaliou esta cena.
import { scene3DReviewNote, scene3DReviewVerdictOf } from "@nodaro/shared"

const review = scene3DReviewVerdictOf(shot)
if (review) {
  console.log(scene3DReviewNote(review)) // one user-safe sentence for either verdict
  if (review.verdict === "unavailable") console.log(`unreviewed (${review.reason}) after ${review.attempts} attempts`)
  for (const objection of review.objections) console.log(objection.category, objection.what, objection.correction)
}

Use os dois helpers de @nodaro/shared em vez de ler os campos você mesmo, porque três leituras parecem certas e não são:

  • validation.status continua "passed". As verificações obrigatórias de fato passaram, e é por isso que a cena foi entregue.
  • objections pode estar vazio. Uma recusa que não apontou nada específico continua sendo uma recusa, então contar os avisos SCENE_REVIEW_REFUSED não a detecta.
  • As objeções sob "unavailable" não são o veredito. Elas vêm de lotes de revisão que responderam antes de um deles falhar. Nesse caso, uma lista vazia significa silêncio, não aprovação.

Cada objeção é { category, what, correction?, frames }. No veredito "unavailable", um aviso SCENE_REVIEW_UNAVAILABLE vem primeiro em validation.warnings. Uma recusa visual sozinha não faz mais o job falhar. SCENE_QUALITY_FAILED significa que uma verificação obrigatória falhou ou que o compilador recusou a receita. Veja Renderização 3D Pro para saber o que um resultado com falha desse tipo mantém.

Entregas e arquivos

Estes métodos leem arquivos que uma execução já entregou. Eles nunca iniciam uma renderização. Toda leitura verifica de novo o seu acesso à entrega e à origem dela, mesmo depois que a revisão de origem foi excluída.

getDelivery(jobId)

Lê o registro de uma entrega (GET /v1/3d-scene/deliveries/:jobId): o sourceKind dela, a revisão de origem exata e os arquivos que ela fixou.

getDelivery(jobId: string): Promise<Scene3DDelivery>

Prop

Type

const delivery = await client.scene3d.getDelivery(jobId)
const stills = delivery.assets.filter((a) => a.kind === "shot-still")

Aparecem quatro tipos de arquivo: validation-report em toda entrega, poster em toda entrega que não foi recusada, shot-still uma vez por tomada, cada um com o seu shotIndex, frame, width e height, e source-json só em uma entrega refused-authoring. sourceKind é retained-revision, job-output ou refused-authoring. Em uma entrega recusada, sceneRevisionId e sourcePlanSha256 são null e não há pôster, porque nada foi compilado.

deliveryAssetBytes(jobId, asset, options?)

Baixa um arquivo listado pela entrega, com autenticação nova e um limite de tamanho.

deliveryAssetBytes(jobId: string, asset: Scene3DDeliveryAsset, options?: { signal?: AbortSignal }): Promise<ArrayBuffer>

Prop

Type

const bytes = await client.scene3d.deliveryAssetBytes(jobId, stills[0])

retainedRecipe(jobId, options?)

Lê a receita que uma execução recusada da Renderização 3D Pro guardou, já interpretada, ou null quando não há nenhuma. A receita de uma execução recusada nunca foi compilada, então não há revisão de cena, pôster nem arquivo-fonte; a receita é o que ela deixa para trás.

retainedRecipe(jobId: string, options?: { signal?: AbortSignal }): Promise<unknown | null>

Prop

Type

const recipe = await client.scene3d.retainedRecipe(jobId)
  • Exige acesso de edição ao workflow do job. Um leitor com menos acesso nem chega a ver a receita, então a resposta é null, não um erro.
  • O job com falha diz se vale a pena pedir. O validation.sourceRetained dele é true quando uma receita foi guardada.
  • É evidência, não entrada. Uma execução recusada não publicou nenhuma revisão, então você não pode executá-la de novo a partir da receita. Leia a receita para ver o que foi tentado e melhorar o próximo prompt. A leitura não custa créditos.

assetBytes(revisionId, asset, options?)

Baixa um arquivo de uma revisão de cena guardada: um modelo GLB, uma trilha de câmera, o pôster ou o relatório de validação. Passe o descritor exato dessa revisão. O SDK limita o download ao tamanho declarado, e o renderizador de cenas também verifica o hash SHA-256.

assetBytes(revisionId: string, asset: Scene3DAssetRef, options?: { signal?: AbortSignal }): Promise<ArrayBuffer>

Prop

Type

const glb = await client.scene3d.assetBytes(scenePlan.revisionId, glbAsset)

sourceBytes(revisionId, options?)

Baixa o arquivo-fonte nativo de uma revisão, como um arquivo .blend, com uma autorização própria. Exige o mesmo acesso de edição que uma receita guardada. Um arquivo nativo só está disponível quando representa exatamente aquela revisão aceita.

sourceBytes(revisionId: string, options?: { signal?: AbortSignal }): Promise<ArrayBuffer>

Prop

Type

const blend = await client.scene3d.sourceBytes(revisionId)

Os dois métodos de bytes usam credenciais novas, respeitam o cancelamento e lançam os erros tipados de sempre.

Perguntas frequentes

Última atualização

Nesta página