- Render improvements, camera plane, update
This commit is contained in:
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user