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.
Auf dieser Seite
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:
{
"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
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.
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
{
"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:
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}{
"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.
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)}{
"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.
