blender-lighting

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Blender Lighting

Blender灯光设置

Light scenes the way pros do: with structure, intent, and physically reasonable values.
以专业方式为场景打光:遵循结构化流程、明确设计意图,并使用符合物理规律的参数值。

The five light types

五种灯光类型

TypeBehaviorUse for
AREALight from a rectangular surface; soft shadows automatic80% of cases. Window, softbox, fluorescent panel
SUNParallel rays from "infinity"Sunlight, moonlight, distant directional
POINTOmnidirectional from a pointBulbs, candles, small omnis
SPOTCone with falloffStage lights, headlights, focused beams
HDRI/World360° environment imageRealistic ambient, outdoor, product photography
Default rule: Use Area lights for almost everything except the sun. Soft shadows come for free.
类型特性适用场景
AREA从矩形表面发光;自动生成软阴影80%的场景,如窗户、柔光箱、荧光面板
SUN来自“无限远”的平行光线太阳光、月光、远距离定向光源
POINT从点向四周均匀发光灯泡、蜡烛、小型全向光源
SPOT带衰减效果的锥形光束舞台灯光、车头灯、聚焦光束
HDRI/World360°环境图像逼真环境光、户外场景、产品摄影
默认规则:除模拟太阳光外,几乎所有场景都优先使用AREA面光源,可自动获得软阴影。

Decision tree

决策树

What's the mood?
├── Studio / commercial → Three-point lighting (key+fill+rim) + HDRI fill 0.3
├── Outdoor / sunlit → Sun + HDRI sky environment
├── Indoor cinematic → Sun through window + HDRI low + practicals (lamps as Point)
├── Dramatic / noir → Single Spot at high angle, no fill
├── Stylized / cartoon → Three-point with high contrast + saturated key color
└── Unsure → Three-point with HDRI grounding (works for 90% of cases)
想要营造什么氛围?
├── 工作室/商业风格 → 三点布光(主光+补光+轮廓光)+ HDRI补光(强度0.3)
├── 户外/日光场景 → 太阳光 + HDRI天空环境
├── 室内电影感场景 → 透过窗户的太阳光 + 低强度HDRI + 实用光源(如用POINT点光源模拟台灯)
├── 戏剧性/黑色电影风格 → 单盏高角度SPOT聚光灯,无补光
├── 风格化/卡通风格 → 高对比度三点布光 + 饱和度高的主光颜色
└── 不确定 → 搭配HDRI基础照明的三点布光(适用于90%的场景)

Reference-look handoff

参考风格匹配

If the goal is to match an original/reference image rather than make a generally attractive render, chain-load
reference-look-calibration
. It owns measurement of hue/saturation/value, object extent, glow/aura color, and before/after look metrics. This skill should then apply the requested material/lighting/render changes within that calibrated target.
如果目标是匹配某张参考图的效果,而非制作通用美观的渲染图,请调用
reference-look-calibration
。该技能负责测量色调/饱和度/明度、物体范围、光晕颜色以及前后效果对比指标。本技能将在该校准目标范围内应用所需的材质/灯光/渲染调整。

Recipes

配置方案

Helper:
aim_at(light, target)
— required for subject-aware lighting

辅助工具:
aim_at(light, target)
—— 面向主体的打光必备

Recipe 1 below positions lights at fixed world coords with hardcoded rotations. That's fine for a generic 1m subject at the world origin. For ANY other subject (small jewellery, tall sword, sprawling building), you need lights aimed at the subject. Use this helper:
python
from mathutils import Vector

def aim_at(light_obj, target):
    """Aim a light at a world-space target.
    target may be a Vector or a tuple/list (x, y, z) or a Blender object.
    """
    target_pos = Vector(target.location) if hasattr(target, 'location') else Vector(target)
    direction = (target_pos - light_obj.location).normalized()
    light_obj.rotation_euler = direction.to_track_quat('-Z', 'Y').to_euler()
下方方案1将灯光固定在世界坐标系的特定位置并使用硬编码旋转角度,适用于位于世界原点的通用1米尺寸主体。对于任何其他主体(如小型珠宝、长剑、大型建筑),需要让灯光对准主体,请使用以下辅助函数:
python
from mathutils import Vector

def aim_at(light_obj, target):
    """Aim a light at a world-space target.
    target may be a Vector or a tuple/list (x, y, z) or a Blender object.
    """
    target_pos = Vector(target.location) if hasattr(target, 'location') else Vector(target)
    direction = (target_pos - light_obj.location).normalized()
    light_obj.rotation_euler = direction.to_track_quat('-Z', 'Y').to_euler()

Helper: scene-aware light positioning

辅助工具:场景感知型灯光定位

python
from mathutils import Vector

def compute_scene_bbox_center(meshes):
    """Average bbox center over a list of mesh objects (world space)."""
    import bpy
    deps = bpy.context.evaluated_depsgraph_get()
    all_verts = []
    for o in meshes:
        eval_obj = o.evaluated_get(deps)
        em = eval_obj.to_mesh()
        for v in em.vertices:
            all_verts.append(o.matrix_world @ v.co)
        eval_obj.to_mesh_clear()
    xs = [v.x for v in all_verts]
    ys = [v.y for v in all_verts]
    zs = [v.z for v in all_verts]
    center = Vector(((min(xs)+max(xs))/2, (min(ys)+max(ys))/2, (min(zs)+max(zs))/2))
    extent = max(max(xs)-min(xs), max(ys)-min(ys), max(zs)-min(zs))
    return center, extent
python
from mathutils import Vector

def compute_scene_bbox_center(meshes):
    """Average bbox center over a list of mesh objects (world space)."""
    import bpy
    deps = bpy.context.evaluated_depsgraph_get()
    all_verts = []
    for o in meshes:
        eval_obj = o.evaluated_get(deps)
        em = eval_obj.to_mesh()
        for v in em.vertices:
            all_verts.append(o.matrix_world @ v.co)
        eval_obj.to_mesh_clear()
    xs = [v.x for v in all_verts]
    ys = [v.y for v in all_verts]
    zs = [v.z for v in all_verts]
    center = Vector(((min(xs)+max(xs))/2, (min(ys)+max(ys))/2, (min(zs)+max(zs))/2))
    extent = max(max(xs)-min(xs), max(ys)-min(ys), max(zs)-min(zs))
    return center, extent

Recipe 0a — Subject-CLASS-aware three-point lighting (use this for orchestrator E2E)

方案0a —— 面向主体类别的三点布光(用于编排端到端流程)

Generic three-point lighting (Recipe 0b below) places lights at fixed energy ratios. That works for opaque subjects (chair, sword) but breaks for glass (rim washes out volume tint) and is too cool for wood (loses warmth).
Pass a
subject_class
hint to tune the setup:
ClassKey:Fill:Rim ratioKey color tempReason
'metal'
4:1:2 (default)warm 3200KStandard 3-point reads metallic well
'glass'
3:1:1.2neutral 5500KSoft rim — strong rim WASHES OUT volume tint; brighter fill so transmission shows colour
'wood'
4:1:1.5warm 3000KWarmer key brings out wood tones; less rim (wood doesn't need silhouette boost)
'fabric'
3:1:0.5neutral 5500KSoft and balanced; sheen reads in fill light
'skin'
4:1:1warm 3500KWarm key for healthy tone; subtle rim (avoids harsh edges on faces)
'product'
5:1:1.5neutral 5000KHigher contrast; commercial/clean look
(unspecified)falls back to Recipe 0b (default)warm 3200K
python
import bpy, math
from mathutils import Vector

def aim_at(light_obj, target):
    target_pos = Vector(target.location) if hasattr(target, 'location') else Vector(target)
    direction = (target_pos - light_obj.location).normalized()
    light_obj.rotation_euler = direction.to_track_quat('-Z', 'Y').to_euler()

def apply_three_point(subject_class='metal'):
    """Configure 3-point lighting with subject-class-aware ratios.

    subject_class: 'metal' | 'glass' | 'wood' | 'fabric' | 'skin' | 'product' | 'metal' (default)
    """
    profiles = {
        'metal':   dict(ratio=(4.0, 1.0, 2.0), key_color=(1.0, 0.95, 0.85), fill_color=(0.85, 0.9, 1.0), rim_color=(0.7, 0.85, 1.0)),
        'glass':   dict(ratio=(3.0, 1.0, 1.2), key_color=(1.0, 0.98, 0.95), fill_color=(0.95, 0.95, 1.0), rim_color=(0.95, 0.95, 1.0)),
        'wood':    dict(ratio=(4.0, 1.0, 1.5), key_color=(1.0, 0.92, 0.78), fill_color=(0.95, 0.95, 1.0), rim_color=(0.85, 0.92, 1.0)),
        'fabric':  dict(ratio=(3.0, 1.0, 0.5), key_color=(1.0, 0.97, 0.92), fill_color=(0.92, 0.95, 1.0), rim_color=(0.95, 0.95, 1.0)),
        'skin':    dict(ratio=(4.0, 1.0, 1.0), key_color=(1.0, 0.93, 0.82), fill_color=(0.95, 0.96, 1.0), rim_color=(0.92, 0.92, 1.0)),
        'product': dict(ratio=(5.0, 1.0, 1.5), key_color=(1.0, 0.98, 0.95), fill_color=(0.98, 0.98, 1.0), rim_color=(0.98, 0.98, 1.0)),
    }
    p = profiles.get(subject_class, profiles['metal'])

    # Compute scene bbox
    subject_meshes = [o for o in bpy.data.objects if o.type == 'MESH' and o.name.startswith('GEO-')]
    deps = bpy.context.evaluated_depsgraph_get()
    all_verts = []
    for o in subject_meshes:
        eo = o.evaluated_get(deps); em = eo.to_mesh()
        for v in em.vertices: all_verts.append(o.matrix_world @ v.co)
        eo.to_mesh_clear()
    xs = [v.x for v in all_verts]; ys = [v.y for v in all_verts]; zs = [v.z for v in all_verts]
    center = Vector(((min(xs)+max(xs))/2, (min(ys)+max(ys))/2, (min(zs)+max(zs))/2))
    biggest = max(max(zs)-min(zs), max(xs)-min(xs))
    light_dist = max(biggest * 1.5, 1.0)

    # Energy scales with distance²
    base_energy = 100 * (light_dist / 1.5) ** 2
    key_e, fill_e, rim_e = (base_energy * r for r in p['ratio'])

    # Remove existing lights
    for o in list(bpy.data.objects):
        if o.type == 'LIGHT' and (o.name.startswith('LGT-key') or o.name.startswith('LGT-fill') or o.name.startswith('LGT-rim')):
            bpy.data.objects.remove(o, do_unlink=True)

    # KEY (warm, front-right above)
    key = bpy.data.objects.new('LGT-key', bpy.data.lights.new('LGT-key', type='AREA'))
    key.data.energy = key_e; key.data.size = 0.5; key.data.color = p['key_color']
    bpy.context.collection.objects.link(key)
    key.location = (center.x + light_dist*0.7, center.y - light_dist*0.7, center.z + light_dist*0.5)
    aim_at(key, center)

    # FILL (cool, opposite, weaker)
    fill = bpy.data.objects.new('LGT-fill', bpy.data.lights.new('LGT-fill', type='AREA'))
    fill.data.energy = fill_e; fill.data.size = 1.0; fill.data.color = p['fill_color']
    bpy.context.collection.objects.link(fill)
    fill.location = (center.x - light_dist*0.7, center.y - light_dist*0.5, center.z + light_dist*0.3)
    aim_at(fill, center)

    # RIM
    rim_type = 'AREA' if subject_class == 'glass' else 'SPOT'
    rim = bpy.data.objects.new('LGT-rim', bpy.data.lights.new('LGT-rim', type=rim_type))
    rim.data.energy = rim_e; rim.data.color = p['rim_color']
    if rim_type == 'AREA':
        rim.data.size = 1.5   # larger soft-source for glass
    else:
        rim.data.spot_size = math.radians(50)
    bpy.context.collection.objects.link(rim)
    rim.location = (center.x, center.y + light_dist, center.z + light_dist*0.5)
    aim_at(rim, center)

    print(f"lighting:{subject_class} key:fill:rim={p['ratio']} dist={light_dist:.2f}m")
通用三点布光(下方方案0b)采用固定的能量比例,适用于不透明主体(如椅子、剑),但对玻璃(轮廓光会掩盖体积色调)效果不佳,对木材(会丢失暖色调)来说色调过冷。
传入
subject_class
提示参数可调整配置:
类别主光:补光:轮廓光比例主光色温原因
'metal'
4:1:2(默认)暖光3200K标准三点布光能很好地展现金属质感
'glass'
3:1:1.2中性光5500K柔和的轮廓光——强轮廓光会掩盖玻璃的体积色调;更亮的补光能让透射色显现
'wood'
4:1:1.5暖光3000K更暖的主光凸显木材色调;减少轮廓光(木材不需要增强轮廓)
'fabric'
3:1:0.5中性光5500K柔和且平衡的灯光;补光能展现面料光泽
'skin'
4:1:1暖光3500K暖光呈现健康肤色;柔和的轮廓光(避免脸部边缘生硬)
'product'
5:1:1.5中性光5000K高对比度;商业/简洁风格
未指定回退至方案0b(默认)暖光3200K
python
import bpy, math
from mathutils import Vector

def aim_at(light_obj, target):
    target_pos = Vector(target.location) if hasattr(target, 'location') else Vector(target)
    direction = (target_pos - light_obj.location).normalized()
    light_obj.rotation_euler = direction.to_track_quat('-Z', 'Y').to_euler()

def apply_three_point(subject_class='metal'):
    """Configure 3-point lighting with subject-class-aware ratios.

    subject_class: 'metal' | 'glass' | 'wood' | 'fabric' | 'skin' | 'product' | 'metal' (default)
    """
    profiles = {
        'metal':   dict(ratio=(4.0, 1.0, 2.0), key_color=(1.0, 0.95, 0.85), fill_color=(0.85, 0.9, 1.0), rim_color=(0.7, 0.85, 1.0)),
        'glass':   dict(ratio=(3.0, 1.0, 1.2), key_color=(1.0, 0.98, 0.95), fill_color=(0.95, 0.95, 1.0), rim_color=(0.95, 0.95, 1.0)),
        'wood':    dict(ratio=(4.0, 1.0, 1.5), key_color=(1.0, 0.92, 0.78), fill_color=(0.95, 0.95, 1.0), rim_color=(0.85, 0.92, 1.0)),
        'fabric':  dict(ratio=(3.0, 1.0, 0.5), key_color=(1.0, 0.97, 0.92), fill_color=(0.92, 0.95, 1.0), rim_color=(0.95, 0.95, 1.0)),
        'skin':    dict(ratio=(4.0, 1.0, 1.0), key_color=(1.0, 0.93, 0.82), fill_color=(0.95, 0.96, 1.0), rim_color=(0.92, 0.92, 1.0)),
        'product': dict(ratio=(5.0, 1.0, 1.5), key_color=(1.0, 0.98, 0.95), fill_color=(0.98, 0.98, 1.0), rim_color=(0.98, 0.98, 1.0)),
    }
    p = profiles.get(subject_class, profiles['metal'])

    # Compute scene bbox
    subject_meshes = [o for o in bpy.data.objects if o.type == 'MESH' and o.name.startswith('GEO-')]
    deps = bpy.context.evaluated_depsgraph_get()
    all_verts = []
    for o in subject_meshes:
        eo = o.evaluated_get(deps); em = eo.to_mesh()
        for v in em.vertices: all_verts.append(o.matrix_world @ v.co)
        eo.to_mesh_clear()
    xs = [v.x for v in all_verts]; ys = [v.y for v in all_verts]; zs = [v.z for v in all_verts]
    center = Vector(((min(xs)+max(xs))/2, (min(ys)+max(ys))/2, (min(zs)+max(zs))/2))
    biggest = max(max(zs)-min(zs), max(xs)-min(xs))
    light_dist = max(biggest * 1.5, 1.0)

    # Energy scales with distance²
    base_energy = 100 * (light_dist / 1.5) ** 2
    key_e, fill_e, rim_e = (base_energy * r for r in p['ratio'])

    # Remove existing lights
    for o in list(bpy.data.objects):
        if o.type == 'LIGHT' and (o.name.startswith('LGT-key') or o.name.startswith('LGT-fill') or o.name.startswith('LGT-rim')):
            bpy.data.objects.remove(o, do_unlink=True)

    # KEY (warm, front-right above)
    key = bpy.data.objects.new('LGT-key', bpy.data.lights.new('LGT-key', type='AREA'))
    key.data.energy = key_e; key.data.size = 0.5; key.data.color = p['key_color']
    bpy.context.collection.objects.link(key)
    key.location = (center.x + light_dist*0.7, center.y - light_dist*0.7, center.z + light_dist*0.5)
    aim_at(key, center)

    # FILL (cool, opposite, weaker)
    fill = bpy.data.objects.new('LGT-fill', bpy.data.lights.new('LGT-fill', type='AREA'))
    fill.data.energy = fill_e; fill.data.size = 1.0; fill.data.color = p['fill_color']
    bpy.context.collection.objects.link(fill)
    fill.location = (center.x - light_dist*0.7, center.y - light_dist*0.5, center.z + light_dist*0.3)
    aim_at(fill, center)

    # RIM
    rim_type = 'AREA' if subject_class == 'glass' else 'SPOT'
    rim = bpy.data.objects.new('LGT-rim', bpy.data.lights.new('LGT-rim', type=rim_type))
    rim.data.energy = rim_e; rim.data.color = p['rim_color']
    if rim_type == 'AREA':
        rim.data.size = 1.5   # larger soft-source for glass
    else:
        rim.data.spot_size = math.radians(50)
    bpy.context.collection.objects.link(rim)
    rim.location = (center.x, center.y + light_dist, center.z + light_dist*0.5)
    aim_at(rim, center)

    print(f"lighting:{subject_class} key:fill:rim={p['ratio']} dist={light_dist:.2f}m")

Usage:

Usage:

apply_three_point('glass') # for the wine bottle

apply_three_point('glass') # for the wine bottle

apply_three_point('wood') # for the chair

apply_three_point('wood') # for the chair

apply_three_point('metal') # for the sword (or omit; 'metal' is default)

apply_three_point('metal') # for the sword (or omit; 'metal' is default)

undefined
undefined

Recipe 0b — Three-point lighting aimed at a subject (generic, no class hint)

方案0b —— 面向特定主体的三点布光(通用型,无类别提示)

Use this instead of Recipe 1 when you have a specific subject but the class doesn't matter. Lights are placed proportionally to the subject's largest dimension.
python
import bpy, math
from mathutils import Vector

def aim_at(light_obj, target):
    target_pos = Vector(target.location) if hasattr(target, 'location') else Vector(target)
    direction = (target_pos - light_obj.location).normalized()
    light_obj.rotation_euler = direction.to_track_quat('-Z', 'Y').to_euler()
当你有特定主体但无需考虑类别时,使用本方案替代方案1。灯光位置将根据主体的最大尺寸成比例设置。
python
import bpy, math
from mathutils import Vector

def aim_at(light_obj, target):
    target_pos = Vector(target.location) if hasattr(target, 'location') else Vector(target)
    direction = (target_pos - light_obj.location).normalized()
    light_obj.rotation_euler = direction.to_track_quat('-Z', 'Y').to_euler()

Determine subject and its scale

Determine subject and its scale

subject_meshes = [o for o in bpy.data.objects if o.type == 'MESH' and o.name.startswith('GEO-')] deps = bpy.context.evaluated_depsgraph_get() all_verts = [] for o in subject_meshes: eo = o.evaluated_get(deps); em = eo.to_mesh() for v in em.vertices: all_verts.append(o.matrix_world @ v.co) eo.to_mesh_clear() xs = [v.x for v in all_verts]; ys = [v.y for v in all_verts]; zs = [v.z for v in all_verts] center = Vector(((min(xs)+max(xs))/2, (min(ys)+max(ys))/2, (min(zs)+max(zs))/2)) extent = max(max(xs)-min(xs), max(ys)-min(ys), max(zs)-min(zs)) light_dist = max(extent * 1.5, 1.0)
subject_meshes = [o for o in bpy.data.objects if o.type == 'MESH' and o.name.startswith('GEO-')] deps = bpy.context.evaluated_depsgraph_get() all_verts = [] for o in subject_meshes: eo = o.evaluated_get(deps); em = eo.to_mesh() for v in em.vertices: all_verts.append(o.matrix_world @ v.co) eo.to_mesh_clear() xs = [v.x for v in all_verts]; ys = [v.y for v in all_verts]; zs = [v.z for v in all_verts] center = Vector(((min(xs)+max(xs))/2, (min(ys)+max(ys))/2, (min(zs)+max(zs))/2)) extent = max(max(xs)-min(xs), max(ys)-min(ys), max(zs)-min(zs)) light_dist = max(extent * 1.5, 1.0)

Energy values scale roughly inversely with squared distance from subject — recipe targets

Energy values scale roughly inversely with squared distance from subject — recipe targets

physically reasonable values for a ~1m subject at ~1.5m light distance.

physically reasonable values for a ~1m subject at ~1.5m light distance.

key_energy = 100 * (light_dist / 1.5) ** 2 fill_energy = key_energy * 0.3 rim_energy = key_energy * 0.8
key_energy = 100 * (light_dist / 1.5) ** 2 fill_energy = key_energy * 0.3 rim_energy = key_energy * 0.8

KEY (warm, front-right above)

KEY (warm, front-right above)

key = bpy.data.objects.new('LGT-key', bpy.data.lights.new('LGT-key', type='AREA')) key.data.energy = key_energy; key.data.size = 0.5; key.data.color = (1.0, 0.95, 0.85) bpy.context.collection.objects.link(key) key.location = (center.x + light_dist * 0.7, center.y - light_dist * 0.7, center.z + light_dist * 0.5) aim_at(key, center)
key = bpy.data.objects.new('LGT-key', bpy.data.lights.new('LGT-key', type='AREA')) key.data.energy = key_energy; key.data.size = 0.5; key.data.color = (1.0, 0.95, 0.85) bpy.context.collection.objects.link(key) key.location = (center.x + light_dist * 0.7, center.y - light_dist * 0.7, center.z + light_dist * 0.5) aim_at(key, center)

FILL (cool, opposite, weaker)

FILL (cool, opposite, weaker)

fill = bpy.data.objects.new('LGT-fill', bpy.data.lights.new('LGT-fill', type='AREA')) fill.data.energy = fill_energy; fill.data.size = 1.0; fill.data.color = (0.85, 0.9, 1.0) bpy.context.collection.objects.link(fill) fill.location = (center.x - light_dist * 0.7, center.y - light_dist * 0.5, center.z + light_dist * 0.3) aim_at(fill, center)
fill = bpy.data.objects.new('LGT-fill', bpy.data.lights.new('LGT-fill', type='AREA')) fill.data.energy = fill_energy; fill.data.size = 1.0; fill.data.color = (0.85, 0.9, 1.0) bpy.context.collection.objects.link(fill) fill.location = (center.x - light_dist * 0.7, center.y - light_dist * 0.5, center.z + light_dist * 0.3) aim_at(fill, center)

RIM (cool, behind, separates subject from BG)

RIM (cool, behind, separates subject from BG)

rim = bpy.data.objects.new('LGT-rim', bpy.data.lights.new('LGT-rim', type='SPOT')) rim.data.energy = rim_energy; rim.data.color = (0.7, 0.85, 1.0); rim.data.spot_size = math.radians(50) bpy.context.collection.objects.link(rim) rim.location = (center.x, center.y + light_dist, center.z + light_dist * 0.5) aim_at(rim, center)
print(f'lighting:three_point_aimed center={tuple(round(v,2) for v in center)} extent={extent:.2f}m dist={light_dist:.2f}m')
undefined
rim = bpy.data.objects.new('LGT-rim', bpy.data.lights.new('LGT-rim', type='SPOT')) rim.data.energy = rim_energy; rim.data.color = (0.7, 0.85, 1.0); rim.data.spot_size = math.radians(50) bpy.context.collection.objects.link(rim) rim.location = (center.x, center.y + light_dist, center.z + light_dist * 0.5) aim_at(rim, center)
print(f'lighting:three_point_aimed center={tuple(round(v,2) for v in center)} extent={extent:.2f}m dist={light_dist:.2f}m')
undefined

Recipe 0c — Practical lighting (scene contains its own emissive light source)

方案0c —— 实用光源照明(场景包含自发光光源)

When the subject IS or CONTAINS a light source — desk lamp with bulb, candle with flame, monitor with glowing screen, neon sign — the scene needs a different setup:
  1. Make the world background dark (Strength 0.10–0.20). Otherwise the bulb's contribution is drowned out by ambient.
  2. Reduce or remove the standard 3-point fill/rim. The practical light should dominate.
  3. Keep a subtle ambient fill (8-15W Area light from camera direction) so the lamp body itself is visible — pure practical-only renders make the lamp shape silhouette into shadow.
  4. Tune emission strength HIGH for small mesh emitters (see
    blender-materials
    Recipe 11b — bulb spheres need Strength 800-3000 to read like real bulbs).
  5. Cycles
    max_bounces
    ≥ 16
    for proper interior-shade lighting — the bulb's light needs to bounce inside the shade and out through the opening.
python
import bpy
from mathutils import Vector

def aim_at(light_obj, target):
    target_pos = Vector(target.location) if hasattr(target, 'location') else Vector(target)
    direction = (target_pos - light_obj.location).normalized()
    light_obj.rotation_euler = direction.to_track_quat('-Z', 'Y').to_euler()
当主体本身就是光源或包含光源时——如带灯泡的台灯、带火焰的蜡烛、发光屏幕的显示器、霓虹灯——场景需要不同的设置:
  1. 调暗世界背景(强度0.10–0.20),否则灯泡的光线会被环境光掩盖。
  2. 减少或移除标准三点布光的补光/轮廓光,让实用光源成为主导。
  3. 保留柔和的环境补光(从相机方向设置8-15W的AREA面光源),确保灯体本身可见——仅使用实用光源的渲染会让灯体变成阴影轮廓。
  4. 调高小型网格发光体的发射强度(参考
    blender-materials
    方案11b——灯泡球体需要800-3000的强度才能呈现真实灯泡效果)。
  5. Cycles渲染器的
    max_bounces
    ≥16
    ,以实现正确的内部灯罩照明——灯泡光线需要在灯罩内反弹并从开口射出。
python
import bpy
from mathutils import Vector

def aim_at(light_obj, target):
    target_pos = Vector(target.location) if hasattr(target, 'location') else Vector(target)
    direction = (target_pos - light_obj.location).normalized()
    light_obj.rotation_euler = direction.to_track_quat('-Z', 'Y').to_euler()

Dim world (let the practical dominate)

Dim world (let the practical dominate)

world = bpy.context.scene.world world.use_nodes = True nodes = world.node_tree.nodes for n in list(nodes): nodes.remove(n) output = nodes.new('ShaderNodeOutputWorld') bg = nodes.new('ShaderNodeBackground') bg.inputs['Color'].default_value = (0.02, 0.02, 0.03, 1.0) bg.inputs['Strength'].default_value = 0.15 world.node_tree.links.new(bg.outputs['Background'], output.inputs['Surface'])
world = bpy.context.scene.world world.use_nodes = True nodes = world.node_tree.nodes for n in list(nodes): nodes.remove(n) output = nodes.new('ShaderNodeOutputWorld') bg = nodes.new('ShaderNodeBackground') bg.inputs['Color'].default_value = (0.02, 0.02, 0.03, 1.0) bg.inputs['Strength'].default_value = 0.15 world.node_tree.links.new(bg.outputs['Background'], output.inputs['Surface'])

Single subtle ambient fill from camera direction

Single subtle ambient fill from camera direction

fill = bpy.data.objects.new('LGT-ambient_fill', bpy.data.lights.new('LGT-ambient_fill', type='AREA')) fill.data.energy = 8; fill.data.size = 1.0; fill.data.color = (0.85, 0.9, 1.0) bpy.context.collection.objects.link(fill) fill.location = (0.5, -0.8, 0.5) aim_at(fill, Vector((0, 0, 0.3)))
fill = bpy.data.objects.new('LGT-ambient_fill', bpy.data.lights.new('LGT-ambient_fill', type='AREA')) fill.data.energy = 8; fill.data.size = 1.0; fill.data.color = (0.85, 0.9, 1.0) bpy.context.collection.objects.link(fill) fill.location = (0.5, -0.8, 0.5) aim_at(fill, Vector((0, 0, 0.3)))

Cycles bounces

Cycles bounces

scene = bpy.context.scene scene.cycles.max_bounces = 16 print('lighting:practical_setup')

The practical light's emission shader (mesh-emissive bulb / candle flame / etc.) handles the rest. Scene appears like real photography of an illuminated subject — dark surroundings, warm pool of light from the practical, subject silhouette gently filled.

**Validation proof**: see `text-to-blender/assets/v1.1.0-validation/desk_lamp_emission.webp` for what this setup produces (desk lamp with visible bulb glow, warm light pool on desk surface, lamp body visible against the dark scene).
scene = bpy.context.scene scene.cycles.max_bounces = 16 print('lighting:practical_setup')

实用光源的发射着色器(如网格发光灯泡/蜡烛火焰等)负责其余效果。场景将呈现为真实摄影中的发光主体——环境昏暗,实用光源形成温暖的光区,主体轮廓被柔和补光照亮。

**验证示例**:参考`text-to-blender/assets/v1.1.0-validation/desk_lamp_emission.webp`,查看本设置的效果(台灯灯泡发光可见,桌面有温暖光区,灯体在暗场景中清晰可见)。

Recipe 1 — Three-point lighting (the canonical setup)

方案1 —— 三点布光(标准配置)

python
import bpy, math
python
import bpy, math

KEY LIGHT (warm, front-right)

KEY LIGHT (warm, front-right)

key_data = bpy.data.lights.new('LGT-key', type='AREA') key_data.energy = 1000 key_data.size = 1.0 key_data.color = (1.0, 0.95, 0.85) # warm tungsten ~3200K key = bpy.data.objects.new('LGT-key', key_data) bpy.context.collection.objects.link(key) key.location = (3, -3, 3.5) key.rotation_euler = (math.radians(35), math.radians(45), 0)
key_data = bpy.data.lights.new('LGT-key', type='AREA') key_data.energy = 1000 key_data.size = 1.0 key_data.color = (1.0, 0.95, 0.85) # warm tungsten ~3200K key = bpy.data.objects.new('LGT-key', key_data) bpy.context.collection.objects.link(key) key.location = (3, -3, 3.5) key.rotation_euler = (math.radians(35), math.radians(45), 0)

FILL LIGHT (cool, front-left, weaker)

FILL LIGHT (cool, front-left, weaker)

fill_data = bpy.data.lights.new('LGT-fill', type='AREA') fill_data.energy = 300 fill_data.size = 2.0 fill_data.color = (0.85, 0.9, 1.0) # cool sky ~6500K fill = bpy.data.objects.new('LGT-fill', fill_data) bpy.context.collection.objects.link(fill) fill.location = (-3, -2, 2.5) fill.rotation_euler = (math.radians(50), math.radians(-45), 0)
fill_data = bpy.data.lights.new('LGT-fill', type='AREA') fill_data.energy = 300 fill_data.size = 2.0 fill_data.color = (0.85, 0.9, 1.0) # cool sky ~6500K fill = bpy.data.objects.new('LGT-fill', fill_data) bpy.context.collection.objects.link(fill) fill.location = (-3, -2, 2.5) fill.rotation_euler = (math.radians(50), math.radians(-45), 0)

BACK / RIM LIGHT (cool, behind subject)

BACK / RIM LIGHT (cool, behind subject)

rim_data = bpy.data.lights.new('LGT-rim', type='SPOT') rim_data.energy = 600 rim_data.spot_size = math.radians(40) rim_data.color = (0.7, 0.85, 1.0) rim = bpy.data.objects.new('LGT-rim', rim_data) bpy.context.collection.objects.link(rim) rim.location = (0, 4, 3.0) rim.rotation_euler = (math.radians(120), 0, math.radians(180))
print('lighting:three_point')

**Standard ratios**:
- High-key (commercial): Key:Fill = 2:1
- Medium (portrait): 4:1
- Low-key (dramatic): 8:1+
- Rim: 50–100% of key
rim_data = bpy.data.lights.new('LGT-rim', type='SPOT') rim_data.energy = 600 rim_data.spot_size = math.radians(40) rim_data.color = (0.7, 0.85, 1.0) rim = bpy.data.objects.new('LGT-rim', rim_data) bpy.context.collection.objects.link(rim) rim.location = (0, 4, 3.0) rim.rotation_euler = (math.radians(120), 0, math.radians(180))
print('lighting:three_point')

**标准比例**:
- 高调(商业风格):主光:补光 = 2:1
- 中调(人像):4:1
- 低调(戏剧性):8:1+
- 轮廓光:主光强度的50–100%

Recipe 2 — HDRI environment

方案2 —— HDRI环境照明

python
import bpy

world = bpy.context.scene.world
world.use_nodes = True
nodes = world.node_tree.nodes
links = world.node_tree.links
python
import bpy

world = bpy.context.scene.world
world.use_nodes = True
nodes = world.node_tree.nodes
links = world.node_tree.links

Wipe existing world nodes

Wipe existing world nodes

for n in list(nodes): nodes.remove(n)
for n in list(nodes): nodes.remove(n)

Output → Background ← Environment Texture ← Mapping ← Texture Coordinate

Output → Background ← Environment Texture ← Mapping ← Texture Coordinate

output = nodes.new('ShaderNodeOutputWorld'); output.location = (300, 0) bg = nodes.new('ShaderNodeBackground'); bg.location = (100, 0) bg.inputs['Strength'].default_value = 1.0
env = nodes.new('ShaderNodeTexEnvironment'); env.location = (-100, 0) env.image = bpy.data.images.load('/path/to/your.hdr') # ← user provides path
mapping = nodes.new('ShaderNodeMapping'); mapping.location = (-300, 0) tex_coord = nodes.new('ShaderNodeTexCoord'); tex_coord.location = (-500, 0)
links.new(tex_coord.outputs['Generated'], mapping.inputs['Vector']) links.new(mapping.outputs['Vector'], env.inputs['Vector']) links.new(env.outputs['Color'], bg.inputs['Color']) links.new(bg.outputs['Background'], output.inputs['Surface']) print('lighting:hdri')

**Pro source**: free HDRIs at [polyhaven.com/hdris](https://polyhaven.com/hdris) (CC0).
output = nodes.new('ShaderNodeOutputWorld'); output.location = (300, 0) bg = nodes.new('ShaderNodeBackground'); bg.location = (100, 0) bg.inputs['Strength'].default_value = 1.0
env = nodes.new('ShaderNodeTexEnvironment'); env.location = (-100, 0) env.image = bpy.data.images.load('/path/to/your.hdr') # ← user provides path
mapping = nodes.new('ShaderNodeMapping'); mapping.location = (-300, 0) tex_coord = nodes.new('ShaderNodeTexCoord'); tex_coord.location = (-500, 0)
links.new(tex_coord.outputs['Generated'], mapping.inputs['Vector']) links.new(mapping.outputs['Vector'], env.inputs['Vector']) links.new(env.outputs['Color'], bg.inputs['Color']) links.new(bg.outputs['Background'], output.inputs['Surface']) print('lighting:hdri')

**优质资源**:[polyhaven.com/hdris](https://polyhaven.com/hdris)提供免费HDRI资源(CC0授权)。

Recipe 3 — Sunny outdoor

方案3 —— 户外日光场景

python
import bpy, math

sun_data = bpy.data.lights.new('LGT-sun', type='SUN')
sun_data.energy = 5.0
sun_data.color = (1.0, 0.95, 0.8)  # golden hour
sun_data.angle = math.radians(0.5)  # realistic sun size; bigger = softer
sun = bpy.data.objects.new('LGT-sun', sun_data)
bpy.context.collection.objects.link(sun)
sun.location = (0, 0, 10)
sun.rotation_euler = (math.radians(45), math.radians(15), 0)
print('lighting:outdoor_sun')
Combine with HDRI sky environment (Recipe 2) for natural ambient fill.
python
import bpy, math

sun_data = bpy.data.lights.new('LGT-sun', type='SUN')
sun_data.energy = 5.0
sun_data.color = (1.0, 0.95, 0.8)  # golden hour
sun_data.angle = math.radians(0.5)  # realistic sun size; bigger = softer
sun = bpy.data.objects.new('LGT-sun', sun_data)
bpy.context.collection.objects.link(sun)
sun.location = (0, 0, 10)
sun.rotation_euler = (math.radians(45), math.radians(15), 0)
print('lighting:outdoor_sun')
搭配方案2的HDRI天空环境照明,可获得自然的环境补光。

Recipe 4 — Indoor window light

方案4 —— 室内窗户光场景

python
import bpy, math
python
import bpy, math

Sun coming through window — cool blue, sharp

Sun coming through window — cool blue, sharp

sun_data = bpy.data.lights.new('LGT-window_sun', type='SUN') sun_data.energy = 3.0 sun_data.color = (0.85, 0.9, 1.0) sun_data.angle = math.radians(2.0) # softer than direct sun sun = bpy.data.objects.new('LGT-window_sun', sun_data) bpy.context.collection.objects.link(sun) sun.location = (5, -3, 4) sun.rotation_euler = (math.radians(60), math.radians(-30), 0)
sun_data = bpy.data.lights.new('LGT-window_sun', type='SUN') sun_data.energy = 3.0 sun_data.color = (0.85, 0.9, 1.0) sun_data.angle = math.radians(2.0) # softer than direct sun sun = bpy.data.objects.new('LGT-window_sun', sun_data) bpy.context.collection.objects.link(sun) sun.location = (5, -3, 4) sun.rotation_euler = (math.radians(60), math.radians(-30), 0)

Practical lamp — warm point light

Practical lamp — warm point light

lamp_data = bpy.data.lights.new('LGT-lamp', type='POINT') lamp_data.energy = 60 lamp_data.color = (1.0, 0.7, 0.4) # warm bulb lamp = bpy.data.objects.new('LGT-lamp', lamp_data) bpy.context.collection.objects.link(lamp) lamp.location = (-1, 2, 1.5) print('lighting:indoor_window')
undefined
lamp_data = bpy.data.lights.new('LGT-lamp', type='POINT') lamp_data.energy = 60 lamp_data.color = (1.0, 0.7, 0.4) # warm bulb lamp = bpy.data.objects.new('LGT-lamp', lamp_data) bpy.context.collection.objects.link(lamp) lamp.location = (-1, 2, 1.5) print('lighting:indoor_window')
undefined

Recipe 5 — Dramatic single-source

方案5 —— 单光源戏剧性场景

python
import bpy, math

spot_data = bpy.data.lights.new('LGT-drama', type='SPOT')
spot_data.energy = 800
spot_data.spot_size = math.radians(30)
spot_data.spot_blend = 0.3
spot_data.color = (1.0, 0.95, 0.85)
spot = bpy.data.objects.new('LGT-drama', spot_data)
bpy.context.collection.objects.link(spot)
spot.location = (2, -2, 6)
spot.rotation_euler = (math.radians(60), 0, 0)
print('lighting:dramatic')
For full noir: pair with strong volumetrics (atmosphere) — see
references/overview.md
for the volumetric setup.
python
import bpy, math

spot_data = bpy.data.lights.new('LGT-drama', type='SPOT')
spot_data.energy = 800
spot_data.spot_size = math.radians(30)
spot_data.spot_blend = 0.3
spot_data.color = (1.0, 0.95, 0.85)
spot = bpy.data.objects.new('LGT-drama', spot_data)
bpy.context.collection.objects.link(spot)
spot.location = (2, -2, 6)
spot.rotation_euler = (math.radians(60), 0, 0)
print('lighting:dramatic')
如需打造纯黑色电影风格:搭配强体积光(大气效果)——参考
references/overview.md
中的体积光设置。

Recipe 6 — Color-temperature cheat sheet

方案6 —— 色温速查表

SourceRGB
Candle (1850K)
(1.0, 0.6, 0.3)
Tungsten (3200K)
(1.0, 0.85, 0.6)
LED warm (3000K)
(1.0, 0.8, 0.6)
Sunset / golden (3500K)
(1.0, 0.85, 0.65)
Daylight noon (5500K)
(1.0, 1.0, 1.0)
Overcast sky (6500K)
(0.95, 0.95, 1.0)
Blue hour (8000K)
(0.8, 0.9, 1.0)
Pro mix: Warm key (tungsten) + cool fill (daylight) = the "golden/teal" Hollywood look.
光源RGB值
蜡烛(1850K)
(1.0, 0.6, 0.3)
钨丝灯(3200K)
(1.0, 0.85, 0.6)
暖光LED(3000K)
(1.0, 0.8, 0.6)
日落/黄金时刻(3500K)
(1.0, 0.85, 0.65)
正午日光(5500K)
(1.0, 1.0, 1.0)
阴天天空(6500K)
(0.95, 0.95, 1.0)
蓝色时刻(8000K)
(0.8, 0.9, 1.0)
专业搭配:暖光主光(钨丝灯)+ 冷光补光(日光)= 好莱坞“金蓝色调”风格。

Recipe 7 — Soft vs hard shadow tweak

方案7 —— 软阴影与硬阴影调整

python
import bpy, math

light_data = bpy.data.lights['LGT-key']
python
import bpy, math

light_data = bpy.data.lights['LGT-key']

Make shadows softer

Make shadows softer

light_data.size = 2.0 # Area: bigger size = softer shadow
light_data.size = 2.0 # Area: bigger size = softer shadow

Or for Sun:

Or for Sun:

light_data.angle = math.radians(5) # bigger angle = softer shadow

light_data.angle = math.radians(5) # bigger angle = softer shadow

Make shadows harder (crisp)

Make shadows harder (crisp)

light_data.size = 0.1

light_data.size = 0.1

Or:

Or:

light_data.angle = math.radians(0.5)

light_data.angle = math.radians(0.5)

undefined
undefined

Naming convention

命名规范

PrefixMeaning
LGT-key
Main / key light
LGT-fill
Fill light
LGT-rim
/
LGT-back
Rim or back light
LGT-sun
Sun lamp
LGT-{name}
Practical lights (lamp, candle, neon, etc.)
前缀含义
LGT-key
主光源
LGT-fill
补光源
LGT-rim
/
LGT-back
轮廓光/背光
LGT-sun
太阳光
LGT-{name}
实用光源(如台灯、蜡烛、霓虹灯等)

Common pitfalls

常见问题

SymptomFix
Half the model in pitch blackAdd fill (Area light or HDRI)
Render looks "flat"Increase key:fill ratio; add rim
Hard shadows everywhereIncrease Area size or Sun angle
Too dark overallBoost View Transform exposure or HDRI strength
No reflections on materialsAlways set a world environment (HDRI)
Light inside objectCheck world position; light must be visible from camera
Backlight blowing out subjectRim energy ≤ key energy
症状解决方法
模型一半区域完全变黑添加补光(AREA面光源或HDRI)
渲染画面“平淡”增大主光:补光比例;添加轮廓光
全是硬阴影增大AREA面光源尺寸或太阳光角度
整体过暗提高视图变换曝光度或HDRI强度
材质无反射务必设置世界环境(HDRI)
光源在模型内部检查世界位置;光源必须在相机可见范围内
背光过曝主体轮廓光强度 ≤ 主光强度

When to load
references/overview.md

何时加载
references/overview.md

Load when:
  • The user asks for a setup not in the recipes (volumetric god rays, light groups, light linking)
  • HDRI rotation / strength tuning is needed beyond defaults
  • Multi-light scenes (5+ lamps) require organization
  • Color science or color management gets specific (AgX, Filmic)
The reference covers: all 5 light types in depth, full HDRI workflow, light groups for re-lighting in compositor, recipes for product/portrait/architectural/character/animation looks.
在以下情况加载:
  • 用户需求不在上述方案中(如体积光上帝之光、灯光组、灯光链接)
  • 需要对HDRI的旋转/强度进行超出默认值的调整
  • 多光源场景(5盏及以上灯光)需要组织管理
  • 涉及特定色彩科学或色彩管理(如AgX、Filmic)
该参考文档涵盖:五种灯光类型的详细介绍、完整HDRI工作流程、用于合成器中重新打光的灯光组、产品/人像/建筑/角色/动画风格的配置方案。