Blender

Ein Modell steuert Blender 4.3.2 über den Hub, um aus der Standardszene eine Szene zu bauen und als PNG zu rendern. Die Seite nennt den Text, das Skript, die Antworten des Hubs und das gerenderte Bild, samt dem Farbfehler, den eine erfolgreiche Antwort verbarg, und den 30 Sekunden des ersten Renderns.

Die Szene des Logos, vom Hub in Blender aufgebaut und gerendert: Ein Schnitt wechselt vom gerenderten Bild zum Drahtgitter, während die Kamera um den Kern kreist.

Blender mit seiner Brücke starten

Blender führt die Brücke in seinem eigenen Python aus. Im Hintergrund gestartet, beendet es sich, sobald die Brückendatei gelaufen ist; deshalb übergibt eine Boot-Datei der Brücke mit serve_main_thread() den Hauptthread, wie der Leitfaden es beschreibt. Der Lauf hatte keine Variable DISPLAY.

blender --background --python boot.py
import runpy

ns = runpy.run_path('/usr/share/libremodular-mcp/bridges/blender_bridge.py')
ns['serve_main_thread']()

Der Lauf nutzte in config.toml den Port 9313 und ein neues Token. Außerdem setzte er im Abschnitt [hub] default_timeout_s = 120, weil das erste Rendern eines neuen Profils langsam ist und die Standardfrist 30 s beträgt (siehe die Antwort unten). Der Port lauschte 1,3 s nach dem Start, und get_state für hub listete Blender als verbunden auf:

Antwort des Hubs
{
  "app": "hub",
  "app_version": "0.1.0",
  "bridge_version": null,
  "captured_at": "2026-10-21T12:47:58.570Z",
  "connected": true,
  "content_origin": "hub",
  "screenshot": null,
  "state": {
    "modules": [
      {
        "app_version": "4.3.2",
        "bridge_version": "1",
        "connected": true,
        "mode": "tcp",
        "name": "blender"
      }
    ]
  },
  "warnings": []
}

Dem Modell gegebener Text

Dem Modell gegebener Text
In Blender, build a small scene from what is already open and render it: the cube in copper (#c25438), a light grey (#d8d4cf) ground just under it, the light turned into a sun, a dark blue (#1c385c) background. Render 640 by 360 pixels and write render.png in your working directory.

Vor dem Schreiben rief das Modell get_state für blender auf: die Standardszene mit den Objekten Cube, Light und Camera, der Engine BLENDER_EEVEE_NEXT und den Einzelbildern 1 bis 250. Es schrieb sein Skript für das, was vorhanden war: Es nutzte den Würfel und das Licht weiter und behielt die Kamera.

Geschriebenes Skript

Ein Modell, Claude Sonnet 5.5, schrieb dieses Skript nach dem Leitfaden und nach diesem Zustand. Es ist kein Skript aus den Tests des Projekts. Die Farben werden von sRGB in linear umgerechnet, bevor sie das Material und die Welt erreichen, weil Blender sie linear speichert.

python · Geschriebenes Skript
import os

import bpy


def to_linear(value):
    return value / 12.92 if value <= 0.04045 else ((value + 0.055) / 1.055) ** 2.4


def colour(code):
    red, green, blue = (int(code[i:i + 2], 16) / 255 for i in (1, 3, 5))
    return (to_linear(red), to_linear(green), to_linear(blue), 1.0)


def material(name, code):
    item = bpy.data.materials.new(name)
    item.use_nodes = True
    item.node_tree.nodes["Principled BSDF"].inputs["Base Color"].default_value = colour(code)
    return item


scene = bpy.context.scene
cube = bpy.data.objects["Cube"]
cube.data.materials.clear()
cube.data.materials.append(material("Copper", "#c25438"))

ground_mesh = bpy.data.meshes.new("Ground")
ground_mesh.from_pydata([(-6, -6, -1), (6, -6, -1), (6, 6, -1), (-6, 6, -1)], [], [(0, 1, 2, 3)])
ground_mesh.update()
ground = bpy.data.objects.new("Ground", ground_mesh)
scene.collection.objects.link(ground)
ground.data.materials.append(material("Grey", "#d8d4cf"))

light = bpy.data.objects["Light"]
light.data.type = "SUN"
light.data.energy = 4.0
light.rotation_euler = (0.9, 0.2, 0.6)

world = scene.world or bpy.data.worlds.new("World")
scene.world = world
world.use_nodes = True
world.node_tree.nodes["Background"].inputs["Color"].default_value = colour("#1c385c")

scene.render.resolution_x = 640
scene.render.resolution_y = 360
scene.render.filepath = os.path.join(LMM_WORK_DIR, "render.png")
bpy.ops.render.render(write_still=True)
result = {"objects": [item.name for item in scene.objects], "png_bytes": os.path.getsize(scene.render.filepath)}

Antwort des Hubs

Antwort des Hubs
{
  "app": "blender",
  "duration_ms": 29947,
  "error": null,
  "result": {
    "objects": [
      "Ground",
      "Cube",
      "Light",
      "Camera"
    ],
    "png_bytes": 216713
  },
  "status": "ok",
  "stderr": "",
  "stdout": "",
  "truncated": false
}

status ist ok, und duration_ms ist 29 947: Das erste Rendern in einem neuen Profil dauerte 30 s, wie der Leitfaden sagt. Das nächste Rendern im selben Blender dauerte 149 ms (unten). Mit der Standardfrist des Hubs von 30 s kann derselbe Aufruf stattdessen mit timeout enden, wie die Seite Kompatibilität festhält.

Was das erste gerenderte Bild zeigte

Die Antwort sagt nicht, ob die Farben stimmen. Im PNG gelesen, war das Pixel oben links, also die Welt, #103759, wo das Skript #1c385c gesetzt hatte. Ein zweites Skript las die Farbverwaltung der Szene:

python · Geschriebenes Skript
import bpy

view = bpy.context.scene.view_settings
result = {"view_transform": view.view_transform, "look": view.look, "exposure": view.exposure, "gamma": view.gamma, "engine": bpy.context.scene.render.engine}
Antwort des Hubs
{
  "app": "blender",
  "duration_ms": 0,
  "error": null,
  "result": {
    "engine": "BLENDER_EEVEE_NEXT",
    "exposure": 0.0,
    "gamma": 1.0,
    "look": "None",
    "view_transform": "AgX"
  },
  "status": "ok",
  "stderr": "",
  "stdout": "",
  "truncated": false
}

Die Szene nutzt die Ansichtstransformation AgX, den Standard dieses Blender, die die Farben auf dem Weg zum PNG verändert. Die Transformation Standard lässt eine einfarbige Fläche, wie sie ist.

Das korrigierte Skript und seine Antwort

Nur das, was fehlschlug, änderte sich: Das Skript setzt die Ansichtstransformation auf Standard und rendert erneut.

python · Geschriebenes Skript
import os

import bpy

scene = bpy.context.scene
scene.view_settings.view_transform = "Standard"
scene.render.filepath = os.path.join(LMM_WORK_DIR, "render.png")
bpy.ops.render.render(write_still=True)
result = {"view_transform": scene.view_settings.view_transform, "png_bytes": os.path.getsize(scene.render.filepath)}
Antwort des Hubs
{
  "app": "blender",
  "duration_ms": 149,
  "error": null,
  "result": {
    "png_bytes": 210434,
    "view_transform": "Standard"
  },
  "status": "ok",
  "stderr": "",
  "stdout": "",
  "truncated": false
}

Das Pixel oben links liest sich jetzt #1C385C, die Farbe, die verlangt war.

Erzeugtes Bild

Das ist das gerenderte Bild, 640 × 360 Pixel. Der Würfel und der Boden werden von der Sonne beleuchtet, daher sind ihre Pixel nicht die Farben ihrer Materialien: Die Oberseite des Würfels liest sich #A84A39 bei einem auf #c25438 gesetzten Material, und der Boden liest sich #BEBEC1 für #d8d4cf.

Ein kupferfarbener Würfel auf einem hellgrauen Boden vor einem dunkelblauen Hintergrund.
render.png nach dem korrigierten Skript, in EEVEE gerendert mit der Ansichtstransformation Standard.

Referenz-Leitfaden im Repository →

WeiterInkscape →