A Pixelsonly Racing product photography scene.
A Python script that builds a complete studio product photography scene in Blender from a single execution. Apex is the first in a series of Pixelsonly Racing product viz scenes — designed for the brand's dark, premium aesthetic (matte black products on a #1F1F21 cyclorama backdrop) and calibrated for fist-to-shoebox accessories at close camera distance.
The script handles the studio. You handle the product.
A single execution gives you a reusable .blend template with:
- A seamless cyclorama backdrop in the Pixelsonly Racing brand color (
#1F1F21) - A calibrated four-light rig that creates proper rim separation on dark-on-dark products
- A 100mm camera with shallow DoF, auto-tracking the subject
- Cycles + Metal + OpenImageDenoise rendering at 4096×4096
- AgX color management with a subtle contrast curve
- Four configured view layers producing three layered compositing passes — product, backdrop, and shadow — for free recompositing in any image editor
Open the template, import your product, render, done.
In motorsport, the apex is the inside point of a corner — the moment a driver hits the perfect line, the geometric peak of the turn. It's also the word for the top of anything: peak, summit, first.
Apex is the first Pixelsonly Racing studio scene and sets the visual standard for everything that follows — hence the name. Subsequent studios will follow the same motorsport-themed convention, each with a name evoking a different part of the racing world. Naming them individually makes it clear at a glance which workflow and aesthetic you're loading, and keeps templates from getting confused as the library grows.
- One-script setup. Builds the entire scene from a fresh Blender file in under a second.
- Calibrated for product viz scale. Light energies tuned for fist-to-shoebox products at 0.5m camera distance (~2W baseline, not the studio-strobe values most tutorials suggest).
- Layered compositing output. Three separate PNGs (product on alpha / backdrop solid / shadow on alpha) drop straight into your image editor as a layer stack.
- Dark-on-dark optimized. Backdrop material and rim lighting designed for matte black products on
#1F1F21without losing silhouette definition. - Track To everywhere. Camera and all four lights aim at a single
Subject_Anchorempty, so moving any of them keeps the product in focus and centered. - One-handle lighting orbit. Rotate the
Light_Rigparent empty to spin the entire light setup around the product. - Pre-calibrated materials. Two default product materials included —
Product_MatteBlack(powder coat) andProduct_StainlessSteel(polished hardware) — ready to assign to imported geometry. - Mac-first development. Tested on Apple Silicon with Metal acceleration. Should work cross-platform but device setup may differ.
| Component | Required | Notes |
|---|---|---|
| Blender | 5.1.1 or newer | API targets 5.1; 5.0 and earlier untested |
| OS | macOS (tested) | Linux/Windows should work; device setup may need adjustment |
| GPU | Apple Silicon strongly recommended | Cycles + Metal at 4K renders in ~30s on M4 Max |
| RAM | 16 GB minimum | 64 GB ideal for working with HDRIs and large products |
| Disk | ~5 GB free | For rendered output |
Reference development environment:
- Blender 5.1.1
- macOS Tahoe 26.5
- Mac Studio M4 Max, 64 GB RAM
- Cycles render engine with Metal GPU
.
├── README.md ← this file
├── apex_studio_scene.py ← the scene builder script
├── templates/ ← (suggested) save built templates here
│ └── apex_template.blend
├── renders/ ← script outputs render here by default
│ ├── product_0001.png
│ ├── backdrop_0001.png
│ └── shadow_0001.png
└── reference/ ← (suggested) calibration shots, example renders
- Clone this repository:
git clone https://github.com/pixelsonly-racing/blender-studio-apex.git - Open Blender 5.1.1+, then File > New > General to start from a clean slate
- Switch to the Scripting workspace (top tabs)
- In the text editor: Text > Open, then select
apex_studio_scene.py - Press Alt+P (⌥P on Mac) to run, or click the play button
- The scene builds. File > Save As
templates/apex_template.blend— that's your reusable base for any Pixelsonly Racing product rendered in the Apex aesthetic.
After this, you only need to re-run the script if you want to rebuild the template from scratch with new defaults. For routine product work, just open the template.
In Fusion, right-click your component → Save as Mesh:
| Setting | Value |
|---|---|
| Format | OBJ |
| Refinement | Custom |
| Surface Deviation | 0.005 mm |
| Normal Deviation | 10° |
| Structure | One File |
Tighter values are fine for hero close-ups; the above is calibrated to avoid visible faceting on curved surfaces under 4K rendering without producing absurdly heavy files.
- Open
apex_template.blend - File > Save As a new working file named after the product (preserves the template)
File > Import > Wavefront (.obj)
| Import setting | Value | Why |
|---|---|---|
| Forward Axis | Y | Matches Fusion's coordinate system |
| Up Axis | Z | Matches Fusion's coordinate system |
| Scale | 0.001 (typically) | Fusion exports in mm; Blender uses meters. Verify with N panel Dimensions after import — should match real product size in mm |
If dimensions look way off, try scale 0.01 (cm-based Fusion docs) or 0.0254 (inch-based). When in doubt, set scale to 1.0, check the natural dimensions, and divide.
- In the Outliner, drag the imported mesh into the Product collection
- Open Material Properties and assign the appropriate default material to each material slot:
Product_MatteBlackfor powder-coated body parts — Pixelsonly Racing's signature finishProduct_StainlessSteelfor hardware (bolts, screws, machined edges, stainless components)
- Build custom materials for any other surfaces (anodized aluminum, brass, painted accents, etc.) — Blender's Principled BSDF can match most real-world materials with a handful of values
Subject_Anchor defaults to z=0 (floor level). To center the camera/lights on your specific product's visual center:
Option A — manual: Select Subject_Anchor, press N, set Location Z to roughly half the product's height in meters (e.g., 0.025 for a 50mm tall product).
Option B — automatic: With the imported product selected, open the Scripting workspace's Python Console and run:
snap_anchor_to_object()This snaps the anchor to the geometric center of the selected object.
Hero shot:
Press F12. The full Cycles render appears in the render window. Save manually if you want this hero shot.
Layered compositing passes:
- In the Python Console, run
setup_shadow_catcher_pass()with the product selected. This configures the backdrop as a shadow catcher and the product as a holdout for the shadow pass. - Press F12 again.
- Three named PNGs appear in
renders/:product_0001.png— product on transparent backgroundbackdrop_0001.png— clean brand-color backdropshadow_0001.png— just the contact shadow on transparent
Drag all three into Pixelmator Pro (or any image editor) as a layer stack:
- Open
backdrop_0001.pngas your base document - Drag
shadow_0001.pngin as a layer above the backdrop; set blend mode to Multiply - Drag
product_0001.pngin as the top layer - Each layer is independently editable — change the backdrop tone, lift or darken the shadow, adjust the product without touching anything else
All defaults live in the CONFIG dictionary at the top of the script. Edit and re-run to rebuild the template from scratch with new values.
| Parameter | Default | Description |
|---|---|---|
backdrop_hex |
"#1F1F21" |
Studio backdrop color; matches shop.pixelsonly.racing background |
world_ambient_hex |
"#0A0A0B" |
Ambient color for the world environment |
world_strength |
0.05 |
World strength; keep low so the four-light rig does the actual lighting work |
| Parameter | Default | Description |
|---|---|---|
subject_center_z |
0.0 |
Initial anchor height in meters; reposition after import |
| Parameter | Default | Description |
|---|---|---|
camera_focal_length |
100 |
Lens focal length in mm; classic product viz length |
camera_distance |
0.55 |
Distance from subject in meters |
camera_elevation |
8 |
Angle above horizon in degrees |
camera_fstop |
8.0 |
Aperture; controls depth of field falloff |
| Parameter | Default | Description |
|---|---|---|
resolution |
(4096, 4096) |
Output dimensions in pixels |
samples_max |
1024 |
Max Cycles samples (adaptive sampling) |
samples_min |
64 |
Minimum samples per pixel |
adaptive_threshold |
0.005 |
Noise threshold; tighter = more samples in dark areas |
| Parameter | Default | Description |
|---|---|---|
key_energy |
2.0 |
Key light (upper-left softbox), slight warmth |
fill_energy |
1.5 |
Fill light (right side), slight coolness |
rim_back_energy |
2.0 |
Rim light behind subject; draws silhouette against dark backdrop |
rim_top_energy |
1.5 |
Top accent for hardware highlights (bolts/screws) |
A note on the watt values: Blender area light "Power" represents total radiant flux in watts. For a small product photographed close (0.5m), even single-digit wattages produce significant irradiance — much more than the hundreds of watts you'd see on a real studio strobe rated for several meters of working distance. If you find yourself reaching for 50W+ values, something is off with your scene scale.
| Parameter | Default | Description |
|---|---|---|
render_output_dir |
"//renders/" |
Output path relative to the .blend file |
Studio_Cam has a Track To constraint pointing at Subject_Anchor, so any translation keeps the camera aimed at the product.
| Shortcut | What it does |
|---|---|
G, Y, then drag or type |
Dolly along world Y (closer/farther from initial position) |
G, Z, then drag or type |
Move up/down for high/low angle |
G, X, then drag or type |
Side-to-side for 3/4 angle |
G, Z, Z |
Same axis as before, but in the camera's local space |
| N panel → Item → Location | Direct numeric editing for precise positioning |
To orbit around the subject like a turntable, add an empty at the anchor's location, parent the camera to it (Ctrl+P → Object Keep Transform), and rotate the empty.
The four lights live under Light_Rig, a parent empty. Rotate the rig to orbit the entire lighting setup around the product without touching individual lights:
Select Light_Rig → R, Z, [angle in degrees], Enter
Common adjustments:
| What you want | What to change |
|---|---|
| Darker backdrop, same product illumination | Drop Rim_Back power; it spills most onto the floor |
| More silhouette separation | Raise Rim_Back, but watch backdrop brightness |
| Hardware (bolts/screws) more visible | Raise Rim_Top_Accent slightly |
| Softer overall feel | Increase area light size in Object Data Properties without changing power |
| Punchier hero shot | Switch view transform Look to "AgX - Punchy" in Render Properties → Color Management |
The script creates three default materials, ready to use as-is or duplicate and tune per product.
Product_MatteBlack — powder-coated matte black aluminum, Pixelsonly Racing's signature finish:
- Base Color
#0A0A0A— very dark but not pure black, which would absorb all light and read flat - Roughness
0.45— slightly satin, not perfectly matte - Metallic
0.0— dielectric (paint/powder coat), not bare metal - Coat Weight
0.25— subtle clearcoat for the powder-coat shimmer - Coat Roughness
0.2— slightly diffused clearcoat highlights
Product_StainlessSteel — polished 304-grade stainless, for hardware (bolts, screws) and stainless components:
- Base Color
#E0E0E4— slightly cool light gray; for metallic materials this represents the F0 reflectance color - Metallic
1.0— full metal - Roughness
0.25— polished but not mirror
To switch to other finishes, just adjust Roughness:
0.15— mirror polish0.40— brushed/satin finish0.50–0.60— industrial / heavily machined
For a true brushed look with directional reflections, enable Anisotropic (~0.5) and add an Anisotropic Rotation to align with the brush direction.
Backdrop_Mat is locked to maximum diffuse:
- Roughness
0.95— essentially no specular highlights from the lights - Specular IOR Level
0.1— minimal dielectric specular contribution - Change
Base Colorto switch brand backdrop colors without touching anything else
If your Fusion file has multiple bodies with different appearances, the OBJ import will create multiple material slots on the imported mesh. The slots will likely all have generic default materials.
To unify:
- Select the product
- In Material Properties, click each slot in the list
- Use the material dropdown to assign
Product_MatteBlack(or your custom material) to each
To keep them separate (e.g., body in matte black, hardware in brushed aluminum), build a second material and assign it to the specific slots that represent those parts.
The scene creates four view layers, each rendering the scene with different visibility:
| View Layer | Output File | What renders |
|---|---|---|
Combined |
(saved manually via F12 render window) | Hero shot with everything visible |
Product_Only |
product_####.png |
Product on transparent background |
Backdrop_Only |
backdrop_####.png |
Clean backdrop, no product |
Shadow_Only |
shadow_####.png |
Contact shadow on transparent |
The three layered passes write automatically to renders/ via File Output nodes in the Compositor when you hit F12 — no manual save needed.
Compositing the passes in Pixelmator Pro:
- Open
backdrop_0001.pngas your base document - Drag
shadow_0001.pngin as a layer above the backdrop - Set the shadow layer's blend mode to Multiply so it darkens the backdrop where the shadow falls
- Drag
product_0001.pngin as the top layer - Each layer is independently editable: change the backdrop color, lift or darken the shadow opacity, mask the product, add highlights, etc.
You're in Material Preview mode, not Rendered. The viewport shading icons in the top-right of the 3D viewport look nearly identical. Material Preview uses Blender's built-in HDRI environment and ignores your world/light setup; Rendered uses the actual scene.
Click the rightmost sphere icon. Hover each one to confirm — the tooltip should say "Rendered."
Fusion writes OBJ vertex coords in your document's units. Blender's OBJ importer assumes meters by default. For a Fusion document in millimeters (the default), set Import → Scale to 0.001. Verify with N panel Dimensions after import.
Subject_Anchor is at a different height than your product's visual center. Edit the anchor's Z location in the N panel to match, or run snap_anchor_to_object() with the product selected.
- The product has multiple material slots and only one got the matte black assignment
- The material's Metallic is set to 1.0 instead of 0.0
- The imported mesh still has its default Blender white material assigned
Check Material Properties carefully — each slot independently.
- Verify
Backdrop_MatRoughness is 0.95 and Specular IOR Level is 0.1 - Drop
Rim_Backpower; this is almost always the cause - Verify World Strength isn't bumped above 0.05
Blender behavior: hiding a parent collection or empty doesn't propagate to children. Either click each child light's visibility icon individually, or select all lights in the viewport and press H.
This is expected in camera view. Use G, Y to dolly the camera physically closer.
- Shadow catcher is per-object, not per-view-layer. The
setup_shadow_catcher_pass()helper enables it globally; you need to toggle it off for hero renders and back on for the layered passes. A future version will likely solve this cleanly with duplicate backdrop geometry. - No HDRI environment by default. The scene relies entirely on the four-light rig plus a near-black world. Adding an HDRI variant is on the roadmap.
- OBJ-only import path is tested. STEP files require a separate add-on (see STEP files below). FBX should work but is untested.
- Single hero shot per render. No turntable or multi-angle setup; adding one is straightforward but currently manual.
Blender 5.x has no native STEP import. If you want to skip OBJ and import STEP directly, the main options as of mid-2026:
- SimLab STEP Importer — commercial, explicit Blender 5.1 + macOS support, 14-day trial
- STEPmac (Gumroad) — macOS-specific, cheap, Apple Silicon builds available; 5.1 compatibility may need testing
- FreeCAD as intermediary — free, reliable: open STEP in FreeCAD, export as OBJ, import into Blender
The OBJ-from-Fusion path is the simplest and most reliable workflow if your design source is Fusion.
Possible future work for the Apex studio, ordered roughly by usefulness:
- v3: Clean shadow catcher via duplicate-backdrop geometry — no more manual toggling
- v3: HDRI environment variant for products that benefit from environmental reflections
- v3: Turntable rig for multi-angle product cards
- v3: Operator-style installable Blender add-on instead of a one-shot script
- Eventually: Multi-camera batch rendering for capturing several angles in a single render run
Sibling studios planned, each in its own repository under the pixelsonly org, following the same motorsport-themed naming convention:
- Large-format scene — for the upcoming 24×36" metal track poster line; different scale, different lighting, name TBD
- Lifestyle/context scene — context renders showing Pixelsonly Racing products integrated into sim rigs; name TBD
This project is licensed under the MIT License — see LICENSE for the full text.
Copyright © 2026 Ryan Lindsey / Pixelsonly Racing. All rights reserved.
Built for Pixelsonly Racing — premium sim racing accessories.
Calibrated against real renders of the DDU Link mount and other Pixelsonly Racing products on the brand backdrop.
This is currently a single-author project supporting an internal Pixelsonly Racing product photography workflow, but contributions are welcome. If you adapt this for a different product category, brand backdrop, or output pipeline, open an issue or PR — the script's CONFIG dict is designed to be customized rather than forked.