Skip to content

[BUG] blender_export: transparent_background removes the environment light (metals render black) and directional light positions are silently ignored #956

Open

Description

@hpopols

A note before you read on: I'm a hardware engineer, not a Blender/Python person,
as you may remember from my previous (considerably less technical) bug reports :-).
Full transparency: the analysis below was done by an AI assistant (Claude) while it
was setting up our KiBot CI. I did drive and check the process myself: I saw the
black renders, watched the A/B screenshots being produced from the exact
configs quoted below, and the fixes genuinely solved it in our CI image. But if you
ask me pointed questions about Blender internals I'll be out of my depth quickly;
I'll relay them to the assistant and come back with answers. I figured a
well-documented report was still more useful to you than no report at all, so please
take it with the appropriate grain of salt.

Describe the bug

Two findings in blender_scripts/blender_export.py (KiBot 1.9.1, Blender 4.2.4 LTS):

  1. transparent_background: true removes the environment light, metals render
    black.
    Two different things are involved in Blender: the world (the environment
    sphere around the scene, used by Cycles for indirect lighting and for what shiny
    surfaces reflect) and the film (film_transparent, an output setting that writes
    alpha 0 where no object is seen, without touching the lighting). In the script the
    background1/2 gradient is implemented as the world, and the code does
    if transparent_background: film_transparent = True else
    create_background_gradient(): with the option on, no world is created at all, the
    scene is surrounded by black. Diffuse surfaces (mask, packages) are still lit by the
    sun, but metallic materials (ENIG pads and rails, solder, pins) only reflect their
    environment, so they render near-black. This is independent of the view transform
    (same result with the default AgX and with Standard).

  2. SUN/SPOT/AREA light positions are silently ignored. BlenderLightOptions
    documents pos_x/pos_y/pos_z for every light type, but the script only sets
    light_ob.location = pos. For directional lights Blender uses the object's
    rotation, not its location, so every configured sun position produces the same
    straight-down sun (default rotation). Two configs with very different sun positions
    give byte-identical renders (RMS difference 0.00).

To Reproduce

Issue 1, two outputs identical except transparent_background:

outputs:
  - name: opaque
    type: blender_export
    options:
      light: [{name: sun, type: SUN, pos_x: '-size', pos_y: 'size', pos_z: 'size*4', energy: 4}]
      render_options:
        samples: 40
        resolution_x: 1920
        resolution_y: 1080
        transparent_background: false   # default: gradient drawn AND used as world
        background1: '#8a8378'
        background2: '#e8e2d4'
      point_of_view: [{view: top, rotate_x: 40, rotate_z: 10}]
      outputs: [{type: render, output: '%i_opaque.%x'}]

  - name: transparent
    type: blender_export
    extends: opaque
    options:
      render_options:
        samples: 40
        resolution_x: 1920
        resolution_y: 1080
        transparent_background: true    # no world is created: metals reflect black
        background1: '#8a8378'
        background2: '#e8e2d4'
      point_of_view: [{view: top, rotate_x: 40, rotate_z: 10}]
      outputs: [{type: render, output: '%i_transparent.%x'}]

(render_options and point_of_view are repeated because extends merges options
one level deep.)

Issue 2, two outputs identical except the sun position:

# A                                            # B
light: [{name: sun, type: SUN,                 light: [{name: sun, type: SUN,
  pos_x: '-size', pos_y: 'size',                 pos_x: '-size*0.25', pos_y: 'size*0.25',
  pos_z: 'size*4', energy: 1}]                   pos_z: 'size*5', energy: 1}]

Run as kibot -c project.kibot.yaml -b board.kicad_pcb -d out opaque transparent
(issue 1) or with the two outputs A and B (issue 2). The board is a client design I can't publish; it is a 6-layer ENIG
PCB whose stackup copper_finish "ENIG" is correctly propagated to the .pcb3d
(pcb2blender applies its ENIG material, so metals are what shows the problem). Neither
issue depends on the board; I can prepare a minimal public example if useful.

Expected behavior

  1. A transparent background should only change the output alpha, not the lighting:
    the world should stay what it is with an opaque background. Verified fix: keep
    creating the gradient world and only add the transparent film on top, i.e. replace
    the elif transparent_background ... else: create_background_gradient branches
    with:
        else:
            # The gradient is also the environment light: keep it even when the
            # film is transparent, otherwise metals reflect a black world
            create_background_gradient(render.get('background1', '#66667F'), render.get('background2', '#CCCCE5'))
            if render.get('transparent_background'):
                r.film_transparent = True
                r.image_settings.color_mode = 'RGBA'

With this the output keeps its transparent alpha (corners alpha 0) and the board is
lit as with an opaque background: mean luminance of the opaque pixels 27.9 -> 114.3,
bright metal pixels 0.1 % -> 23.7 % (screenshot below). An optional world colour or
HDRI decoupled from the visible background would be a nice extra, not needed for the
fix.

  1. pos_x/pos_y/pos_z should aim directional lights at the board. One-line fix after
    light_ob.location = pos:
if typ in ('SUN', 'SPOT', 'AREA'):
    light_ob.rotation_euler = (-mathutils.Vector(pos)).to_track_quat('-Z', 'Y').to_euler()

With this, configs A and B above produce genuinely different lighting (RMS 9.1),
letting users control the specular highlights on solder fillets.

Screenshots

All screenshots are crops of the same region of the board (client design). The red
boxes are the spots where the colours quoted below each image were measured.

Issue 1, same scene and view transform, gradient background vs transparent background:

Image

Issue 1, stock script vs the fix above, transparent background (checkerboard = alpha 0):

Image

Issue 2, configs A and B before the fix (byte-identical):

Image

Issue 2, after the fix:

Image

Environment (please complete the following information):

  • CI/CD workflow: self-hosted GitLab CI (docker executor).
  • GitHub action: no.
  • Docker image: ghcr.io/inti-cmnb/kicad10_auto_full:dev_1.9.1-dc4733c_k10.0.4_d13.2_b4.2.4LTS
    (KiBot 1.9.1, KiCad 10.0.4, Blender 4.2.4 LTS, Debian 13.2). Both findings were
    reproduced on an image whose only additions are the kicad-packages3d Debian package
    and patch; the fixes were applied afterwards as a downstream patch on
    blender_export.py. Dockerfile available on request.
  • Desktop: not tested (renders are produced by the CI image only).

Additional context

We currently carry the fixes as a downstream patch in our CI image (light aiming and
an optional HDRI world that survives transparent_background). Happy to prepare a PR
if the approach suits you.

Activity

  1. changed the title [-]blender_export: transparent_background removes the environment light (metals render black) and directional light positions are silently ignored[/-] [+][BUG] blender_export: transparent_background removes the environment light (metals render black) and directional light positions are silently ignored[/+] on Aug 26, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions