Nodaro ドキュメント
ドキュメントノードリファレンスモデルAI エージェント(MCP)開発者向けセルフホスティングリサーチ
TypeScript SDK

3D シーン

client.scene3d と 3D シーンのノードを使って、TypeScript からプロンプトで編集できる 3D シーンを生成し、編集し、MP4 にレンダリングし、3D レンダリング Pro を実行します。

client.scene3d は、編集できる 3D シーンを作成します。プロンプトからシーンを作成し、指示または正確な操作で編集し、リビジョンを MP4 にレンダリングします。3D レンダリング Pro(3D Render Pro)も実行します。これは、シーンの作成とレンダリングを 1 つの操作で行う機能です。また、レンダリングが納品したファイルも読み取ります。これらのメソッドは、3D シーン生成(Generate 3D Scene)、3D シーン編集(Edit 3D Scene)、動画レンダリング(Render Video)、3D レンダリング Pro(3D Render Pro)の各ノードを使います。エンドポイントについては、3D シーン API を参照してください。

メソッド

メソッド内容
capabilities()この環境が対応しているエンジンと Pro のオプションを読み取ります
generate(params) と generateAndWait()プロンプトから新しいシーンを作成します
edit(params) と editAndWait()シーンの新しいリビジョンを作成します
render(params) と renderAndWait()作成せずに、リビジョンを MP4 にレンダリングします
applyEdits(revisionId, params)作成や課金なしで、正確な編集を保存します
quotePro(params)3D レンダリング Pro の実行の料金を見積もります
runPro(params, options?)見積もり済みの 3D レンダリング Pro の実行を開始します
renderProAndWait(params, options?)見積もり、実行、待機を 1 回の呼び出しで行います
getDelivery(jobId)レンダリングが納品したファイルを読み取ります
deliveryAssetBytes(jobId, asset, options?)納品された 1 つのファイルをダウンロードします
retainedRecipe(jobId, options?)拒否された Pro の実行が保持したレシピを読み取ります
assetBytes(revisionId, asset, options?)シーンのリビジョンの 1 つのファイルをダウンロードします
sourceBytes(revisionId, options?)リビジョンのネイティブのソースファイルをダウンロードします

プロンプトから 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)

同じ 3 つの手順は、client.nodes.runAndWait() と、型付きパラメーターを持つノードタイプ generate-3d-scene、edit-3d-scene、render-video でも行えます。自分のページでシーンを回転させたり調整させたりできるようにするには、3D シーンビューポートの埋め込みを使います。レンダラーのコピーは不要で、メッセージにトークンが含まれることもありません。

client.scene3d

capabilities()

この環境が作成・レンダリングできるものを返します(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 は決定論的なエンジンで、{ available, sceneSchemaVersions } の形です。
  • advanced は、利用できるエンジンを一覧にします。ない場合は null です。利用できない特定のエンジンを指定した場合は、ベーシックでの生成やそのクレジットのチェックより前に拒否されます。
  • pro は 3D レンダリング Pro について説明します。この環境が提供するエンジン、品質プロファイル、スタイル、アスペクト比です。それら以外は提示しないでください。pro がない場合、そのノードは利用できません。

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

プロンプトから新しいシーンを作成します。generate() はジョブをすぐに返し、generateAndWait() はジョブを待って、scenePlan と任意の changeSummary を返します。

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 は、使用が許可されている GLB ファイルを、変わることのない ID で指定します。見た目用の画像とモーション用の動画は、引き続き references で渡します。アセットの URL やハッシュは送らないでください。サーバー自身がバイトデータの記録を提供します。ベーシックと、インポートに対応していないエンジンは、課金の前に inputAssets を拒否します。Pro の見積もりを送るときも、同じセレクターを再利用してください。

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

シーンの新しいリビジョンを作成します。渡したシーン自体は変更されません。prompt で指示を伝えるか、正確な operations を渡してください。両方を同時には使えません。

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) と renderAndWait(params, options?)

渡した正確なリビジョンを、作成や再構築を行わずに、MP4 にレンダリングします(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 })

料金は、プラン自身の width と height によって決まります。

フレームクレジット料金の識別子
長辺が 1,920 ピクセルまで50render-video
それより大きく、5.12 メガピクセルまで75render-video:3d-large
5.12 メガピクセルを超える場合125render-video:3d-xlarge

これらの識別子の現在の料金は、client.credits.modelCosts() で読み取ってください。

applyEdits(revisionId, params)

保存済みのリビジョンに、作成も課金もなしで、正確な編集を保存します(POST /v1/3d-scene/revisions/:id/edits)。新しい scenePlan と 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,
})

返されたシーンを採用してよいのは、ユーザーがまだ、編集の起点にしたリビジョンを編集している場合だけです。ジオメトリとカメラのファイルは再利用されます。ポスター、検証結果、ネイティブのダウンロードファイルは、新しいリビジョン用に作られた後になって初めて、再び添付されます。

3D レンダリング Pro

3D レンダリング Pro は 1 つの操作です。source を渡すと、1 つのジョブが、正確なコンポジションである scenePlan と、MP4 である videoUrl の両方を用意して完了します。結果には、リビジョン、ポスター、shotStills、検証結果、レンダラーの詳細も含まれます。client.nodes.run("pro-3d-render") と runAndWait も、同じ型付きパラメーターで同じルートを呼び出します。

エンジンがない環境は 503 SCENE_CAPABILITY_UNAVAILABLE を返し、ベーシックへのフォールバックは行われません。料金が設定されていない環境は、何かを確保する前に 503 price_not_configured を返します。モデルや推論の強度の設定はありません。プランナーはサーバー側で固定されています。

quotePro(params)

実行を開始せずに、料金を見積もります。何も確保せず、何も消費しません。応答には quoteId、上限を示す maxCredits、表示用の breakdown、そして後で実行が照合される入力ハッシュが含まれます。

quotePro(params: Pro3DRenderParams): Promise<Pro3DRenderQuote>

Prop

Type

source は、次の 3 つの形のいずれかです。

  • { kind: "prompt", prompt, references? } は、新しいシーンを作成します。
  • { kind: "scene", revisionId, sourceJobId } は、editPrompt がなければ、作成の課金なしで保存済みのシーンをエクスポートします。editPrompt を加えると、先にシーンを編集します。ジョブの履歴にしか残っていないベーシックのシーンには sourceJobId が必要です。
  • { kind: "local-export", exportId, connectionId } は、ペアリング済みのデスクトップを使います。
const quote = await client.scene3d.quotePro(params)
showPrice(quote.maxCredits, quote.breakdown)

runPro(params, options?)

見積もり済みの実行を開始します。quotePro() から得た quoteId が必要なので、誰も見ていない料金で実行が始まることはありません。Idempotency-Key を送信します。送信するのは、呼び出しごとに新しく作られるキーか、options.idempotencyKey で渡した自分のキーです。タイムアウトした呼び出しを再試行するときは、自分のキーを再利用してください。

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

Prop

Type

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

renderProAndWait(params, options?)

params に quoteId がない場合は見積もりを行い、実行し、待機します。1 回の呼び出しで全体を行います。送信するリクエストは最大 2 回で、開始される有料のジョブは 1 つだけです。そのジョブは、見積もった内容とまったく同じもののハッシュに対して認められます。

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
}

ショットの静止画。shotStills は { shotIndex, frame, assetId, url } のリストで、各ショットの開始フレームの静止画が 1 枚ずつ、同じ実行の中で追加料金なしに作られます。ショットが 1 つだけのシーンには、フレーム 0 の 1 枚があります。このフィールドができる前に作られた結果にはこれがないため、shot.shotStills ?? [] の形で読み取ってください。

各 url は、公開リンクではなく、自分の環境の認証が必要なエンドポイントです。実行に使ったものと同じ認証情報で取得してください。img タグやサードパーティのサービスからは 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
}

それでも、静止画をモデルのリファレンスとして使えます。読み取った URL を、そのまま referenceImageUrls に渡すか、ノードの stills 出力から渡します。プラットフォームは、その実行に対して、あなたの名前で、そのファイル 1 つだけを短時間読み取れる権限を与えます。その権限は数分後に失効するため、保存するのは認証済みの URL であり、権限そのものではありません。

Pro の実行が自分自身について報告する内容

シーンを作成した実行は、何を仮定し、何を行ったかを報告します。どのフィールドも、任意のものとして読み取ってください。アドバンスエンジンのない環境や、古い結果には、これらのフィールドがまったくありません。

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)
  • SCENE_AUTHORING_ASSUMPTION の警告は、プロンプトが指定しなかったために実行側が決めた内容を示します。警告コードは今後増える可能性があるため、知らないコードはエラーではなく情報として扱ってください。
  • repairPasses は、作成のパスではなく修正の回数を数えます。そのため 0 は、シーンが最初の試行で受け入れられたことを意味します。admissionRetries は別のものを数えます。ビルドの前に、プランナーがレシピをもう一度求められた回数です。
  • mechanicalPasses は、プランナーを介さずに、コンパイラー自身の対処によってエンジンが適用した修正の回数です。これには最大 2 回までの見積もり済みの許容量があり、使わなければ解放されます。1 回ごとに REMEDY_AUTO_APPLIED の警告が加わります。この許容量ができる前に見積もった実行では、こうしたパスも修正として課金されました。渡された見積もりを読んで確認してください。
  • restoredAssertions は、プランナーが、変更を求められていないのに変更してしまった必須のチェックを、エンジンが元に戻したものを示します。1 件ごとに ASSERTION_RESTORED の警告も加わります。
  • レンダリングのみのエクスポートは何も作成していないため、件数もサマリーもありません。特に mechanicalPasses では、フィールドがないことは 0 を意味しません。

generateAndWait() の結果にも、アドバンスエンジンがシーンを作成した場合は、同じフィールドが含まれます。ベーシックエンジンはモデルを呼び出さないため、これらのフィールドはまったく含まれません。

ビジュアルレビューが承認しなかった納品物

完了した結果が、ビジュアルレビューの承認なしに届くことがあります。どちらの場合も、動画は本物であり、クレジットは消費されています。どちらに当たるかは、metadata.review.verdict が示します。

  • "refused":修正の予算を使い切り、必須のチェックはすべて合格したものの、レビューがそれでも異議を示しました。シーンは、その異議とともに納品されました。
  • "unavailable":レビューが、使える判定を返しませんでした。reason は、プロバイダーに接続できなかった場合は "provider"、回答が使えなかった場合は "unusable" です。attempts は、何回問い合わせたかを示します。このシーンは、誰にも判定されていません。
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)
}

フィールドを自分で読み取るのではなく、@nodaro/shared のこの 2 つのヘルパーを使ってください。次の 3 つの読み方は、正しそうに見えて実際は正しくないためです。

  • validation.status は、それでも "passed" のままです。必須のチェックには合格しているからこそ、シーンが納品されています。
  • objections は空になることがあります。具体的な指摘がない異議も、やはり異議です。そのため SCENE_REVIEW_REFUSED の警告を数えるだけでは、これを見逃します。
  • "unavailable" の場合の objections は、判定ではありません。これらは、失敗する前に回答したレビューのバッチから来たものです。この場合の空のリストは、判定がなかったことを意味し、承認を意味しません。

各異議は { category, what, correction?, frames } の形です。"unavailable" の判定では、SCENE_REVIEW_UNAVAILABLE の警告が validation.warnings の先頭に来ます。ビジュアルの異議だけでは、もはやジョブは失敗しません。SCENE_QUALITY_FAILED は、必須のチェックが失敗したか、コンパイラーがレシピを拒否したことを意味します。そのような失敗した結果に何が残るかについては、3D レンダリング Pro を参照してください。

納品物とファイル

これらのメソッドは、実行がすでに納品したファイルを読み取ります。レンダリングを新たに開始することはありません。どの読み取りも、納品物とそのソースの両方へのアクセス権を、ソースのリビジョンが削除された後であっても、そのたびに確認します。

getDelivery(jobId)

納品物の記録を読み取ります(GET /v1/3d-scene/deliveries/:jobId)。その sourceKind、正確なソースのリビジョン、固定されたファイルです。

getDelivery(jobId: string): Promise<Scene3DDelivery>

Prop

Type

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

ファイルには 4 種類があります。すべての納品物にある poster と validation-report、ショットごとに 1 つずつある shot-still(それぞれに shotIndex、frame、width、height が付きます)、そして refused-authoring の納品物にのみある source-json です。sourceKind は retained-revision、job-output、refused-authoring のいずれかです。拒否された納品物では、sceneRevisionId と sourcePlanSha256 が null になり、何もコンパイルされていないため、ポスターもありません。

deliveryAssetBytes(jobId, asset, options?)

納品物が一覧するファイルを 1 つ、新しい認証情報とサイズの上限付きでダウンロードします。

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

Prop

Type

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

retainedRecipe(jobId, options?)

拒否された 3D レンダリング Pro の実行が保持したレシピを、パース済みの形で読み取ります。ない場合は null です。拒否された実行のレシピはコンパイルされていないため、シーンのリビジョンもポスターもソースファイルもありません。レシピは、それでも残っているものです。

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

Prop

Type

const recipe = await client.scene3d.retainedRecipe(jobId)
  • ジョブのワークフローへの編集権限が必要です。それより低い権限の読み取り専用ユーザーには、レシピはまったく見えません。この場合の応答は、エラーではなく null です。
  • 問い合わせるべきかどうかは、失敗したジョブ自身が示します。レシピが保持されている場合、その validation.sourceRetained は true です。
  • これは入力ではなく証拠です。拒否された実行はリビジョンを公開していないため、このレシピから再実行することはできません。何が試みられたかを見て、次のプロンプトを改善するために読んでください。読み取りにクレジットはかかりません。

assetBytes(revisionId, asset, options?)

保存済みのシーンのリビジョンのファイルをダウンロードします。GLB ファイル、カメラトラック、ポスター、検証レポートのいずれかです。そのリビジョンから得た正確な記述子を渡してください。SDK は、宣言されたサイズにダウンロードを制限し、シーンのレンダラーも 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?)

リビジョンのネイティブのソースファイル(.blend ファイルなど)を、専用の認可を通じてダウンロードします。保持されたレシピと同じ編集権限が必要です。ネイティブファイルが利用できるのは、それがまさにその採用されたリビジョンを表している場合だけです。

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

Prop

Type

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

どちらのバイトデータ用メソッドも、新しい認証情報を使い、キャンセルに対応し、通常の型付きエラーをスローします。

よくある質問

最終更新

目次