- Render improvements, camera plane, update

This commit is contained in:
bklronin
2026-07-13 23:01:35 +02:00
parent c78d0af78c
commit 742d06d242
7 changed files with 1226 additions and 346 deletions
+68 -31
View File
@@ -7,7 +7,6 @@ from __future__ import annotations
import logging
import os
import tempfile
from typing import Callable, Optional
import numpy as np
@@ -31,6 +30,7 @@ class MitsubaBackend(RenderBackend):
sys.stderr = io.StringIO()
try:
import mitsuba # noqa: F401
return True
except ImportError:
return False
@@ -116,9 +116,7 @@ class MitsubaBackend(RenderBackend):
}
# ── 3-point lighting (colors and intensities from config) ───
key_rgb = [
c * lighting.key_intensity for c in lighting.key_color
]
key_rgb = [c * lighting.key_intensity for c in lighting.key_color]
key_to_world = mi.ScalarTransform4f.look_at(
origin=[1.0, -0.8, 1.2],
target=[0.0, 0.0, 0.0],
@@ -130,9 +128,7 @@ class MitsubaBackend(RenderBackend):
"irradiance": {"type": "rgb", "value": key_rgb},
}
fill_rgb = [
c * lighting.fill_intensity for c in lighting.fill_color
]
fill_rgb = [c * lighting.fill_intensity for c in lighting.fill_color]
fill_to_world = mi.ScalarTransform4f.look_at(
origin=[-1.0, 0.6, 0.8],
target=[0.0, 0.0, 0.0],
@@ -144,9 +140,7 @@ class MitsubaBackend(RenderBackend):
"irradiance": {"type": "rgb", "value": fill_rgb},
}
rim_rgb = [
c * lighting.rim_intensity for c in lighting.rim_color
]
rim_rgb = [c * lighting.rim_intensity for c in lighting.rim_color]
rim_to_world = mi.ScalarTransform4f.look_at(
origin=[-0.3, 1.2, -0.8],
target=[0.0, 0.0, 0.0],
@@ -158,24 +152,63 @@ class MitsubaBackend(RenderBackend):
"irradiance": {"type": "rgb", "value": rim_rgb},
}
# ── Ground plane (optional) ─────────────────────────────────
# ── Ground plane / backdrop (optional) ─────────────────────
if ground.enabled:
scene["ground_plane"] = {
"type": "rectangle",
"size": [500.0, 500.0],
"to_world": mi.ScalarTransform4f.translate(
[0.0, 0.0, -ground.distance_below]
)
@ mi.ScalarTransform4f.rotate_about_z(90),
"bsdf": {
"type": "diffuse",
"reflectance": {
"type": "rgb",
"value": list(ground.color),
},
},
try:
# Load mesh to compute bounds for ground/backdrop placement
mesh_shape = mi.load_dict({"type": shape_type, "filename": mesh_path})
bbox = mesh_shape.bbox()
bbox_min, bbox_max = bbox[0], bbox[1]
# Ground at model's lowest Z with 0.1% offset
model_height = bbox_max[2] - bbox_min[2]
ground_z = bbox_min[2] - 0.001 * model_height
dx = bbox_max[0] - bbox_min[0]
dy = bbox_max[1] - bbox_min[1]
dz = bbox_max[2] - bbox_min[2]
diag = float((dx * dx + dy * dy + dz * dz) ** 0.5)
except Exception:
# Fallback: place at origin with large default size
ground_z = -ground.distance_below
diag = 1000.0
bsdf_ground = {
"type": "diffuse",
"reflectance": {"type": "rgb", "value": list(ground.color)},
}
if ground.curved_backdrop:
# Photo booth style curved leinwand:
# - Flat floor section in front of the model
# - Curved cylinder behind that sweeps up and over
half_size = diag * 50.0 # huge floor
radius = diag * 3.0 # curvature radius
cyl_height = diag * 20.0 # width of the cylinder (along its axis)
scene["ground_floor"] = {
"type": "rectangle",
"to_world": mi.ScalarTransform4f.translate([0.0, 0.0, ground_z])
@ mi.ScalarTransform4f.scale([half_size, half_size, 1.0]),
"bsdf": bsdf_ground,
}
# Cylinder: axis along Y, positioned behind model, radius sweeps up
scene["ground_backdrop"] = {
"type": "cylinder",
"radius": radius,
"p0": [-cyl_height / 2, -radius, ground_z],
"p1": [cyl_height / 2, -radius, ground_z],
"to_world": mi.ScalarTransform4f.rotate([1, 0, 0], 90)
@ mi.ScalarTransform4f.translate([0.0, 0.0, -radius]),
"bsdf": bsdf_ground,
}
else:
# Simple flat ground plane — very large so edges aren't visible
half_size = diag * 50.0
scene["ground_plane"] = {
"type": "rectangle",
"to_world": mi.ScalarTransform4f.translate([0.0, 0.0, ground_z])
@ mi.ScalarTransform4f.scale([half_size, half_size, 1.0]),
"bsdf": bsdf_ground,
}
return scene
def _make_bsdf(self, material: RenderMaterial) -> dict:
@@ -247,12 +280,14 @@ class MitsubaBackend(RenderBackend):
scene_dict = self._build_scene_dict(mesh_path, material, camera, settings)
scene = mi.load_dict(scene_dict)
logger.info(
f"Rendering {settings.width}x{settings.height} @ {settings.spp} spp"
)
logger.info(f"Rendering {settings.width}x{settings.height} @ {settings.spp} spp")
# Render
image = mi.render(scene, spp=settings.spp, seed=settings.seed or 0)
try:
image = mi.render(scene, spp=settings.spp, seed=int(settings.seed or 0)) # type: ignore[arg-type] # Mitsuba accepts int at runtime
except Exception as e:
logger.error(f"Mitsuba render failed: {e}")
raise
if progress_callback:
progress_callback(1.0)
@@ -281,6 +316,8 @@ class MitsubaBackend(RenderBackend):
spp=max(settings.spp // 4, 16),
max_depth=min(settings.max_depth, 4),
seed=settings.seed,
lighting=settings.lighting,
ground_plane=settings.ground_plane,
)
return self.render(mesh_path, material, camera, preview_settings)
@@ -295,8 +332,8 @@ class MitsubaBackend(RenderBackend):
if ext == ".exr":
# Save as EXR (HDR) — no tonemapping
try:
import OpenEXR
import Imath
import OpenEXR # type: ignore[import-not-found]
import Imath # type: ignore[import-not-found]
h, w = image.shape[:2]
header = OpenEXR.Header(w, h)