blender-lighting
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseBlender Lighting
Blender灯光设置
Light scenes the way pros do: with structure, intent, and physically reasonable values.
以专业方式为场景打光:遵循结构化流程、明确设计意图,并使用符合物理规律的参数值。
The five light types
五种灯光类型
| Type | Behavior | Use for |
|---|---|---|
| AREA | Light from a rectangular surface; soft shadows automatic | 80% of cases. Window, softbox, fluorescent panel |
| SUN | Parallel rays from "infinity" | Sunlight, moonlight, distant directional |
| POINT | Omnidirectional from a point | Bulbs, candles, small omnis |
| SPOT | Cone with falloff | Stage lights, headlights, focused beams |
| HDRI/World | 360° environment image | Realistic 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/World | 360°环境图像 | 逼真环境光、户外场景、产品摄影 |
默认规则:除模拟太阳光外,几乎所有场景都优先使用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 . 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如果目标是匹配某张参考图的效果,而非制作通用美观的渲染图,请调用。该技能负责测量色调/饱和度/明度、物体范围、光晕颜色以及前后效果对比指标。本技能将在该校准目标范围内应用所需的材质/灯光/渲染调整。
reference-look-calibrationRecipes
配置方案
Helper: aim_at(light, target)
— required for subject-aware lighting
aim_at(light, target)辅助工具:aim_at(light, target)
—— 面向主体的打光必备
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, extentpython
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, extentRecipe 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 hint to tune the setup:
subject_class| Class | Key:Fill:Rim ratio | Key color temp | Reason |
|---|---|---|---|
| 4:1:2 (default) | warm 3200K | Standard 3-point reads metallic well |
| 3:1:1.2 | neutral 5500K | Soft rim — strong rim WASHES OUT volume tint; brighter fill so transmission shows colour |
| 4:1:1.5 | warm 3000K | Warmer key brings out wood tones; less rim (wood doesn't need silhouette boost) |
| 3:1:0.5 | neutral 5500K | Soft and balanced; sheen reads in fill light |
| 4:1:1 | warm 3500K | Warm key for healthy tone; subtle rim (avoids harsh edges on faces) |
| 5:1:1.5 | neutral 5000K | Higher 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| 类别 | 主光:补光:轮廓光比例 | 主光色温 | 原因 |
|---|---|---|---|
| 4:1:2(默认) | 暖光3200K | 标准三点布光能很好地展现金属质感 |
| 3:1:1.2 | 中性光5500K | 柔和的轮廓光——强轮廓光会掩盖玻璃的体积色调;更亮的补光能让透射色显现 |
| 4:1:1.5 | 暖光3000K | 更暖的主光凸显木材色调;减少轮廓光(木材不需要增强轮廓) |
| 3:1:0.5 | 中性光5500K | 柔和且平衡的灯光;补光能展现面料光泽 |
| 4:1:1 | 暖光3500K | 暖光呈现健康肤色;柔和的轮廓光(避免脸部边缘生硬) |
| 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)
undefinedundefinedRecipe 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')
undefinedrim = 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')
undefinedRecipe 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:
- Make the world background dark (Strength 0.10–0.20). Otherwise the bulb's contribution is drowned out by ambient.
- Reduce or remove the standard 3-point fill/rim. The practical light should dominate.
- 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.
- Tune emission strength HIGH for small mesh emitters (see Recipe 11b — bulb spheres need Strength 800-3000 to read like real bulbs).
blender-materials - Cycles ≥ 16 for proper interior-shade lighting — the bulb's light needs to bounce inside the shade and out through the opening.
max_bounces
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()当主体本身就是光源或包含光源时——如带灯泡的台灯、带火焰的蜡烛、发光屏幕的显示器、霓虹灯——场景需要不同的设置:
- 调暗世界背景(强度0.10–0.20),否则灯泡的光线会被环境光掩盖。
- 减少或移除标准三点布光的补光/轮廓光,让实用光源成为主导。
- 保留柔和的环境补光(从相机方向设置8-15W的AREA面光源),确保灯体本身可见——仅使用实用光源的渲染会让灯体变成阴影轮廓。
- 调高小型网格发光体的发射强度(参考方案11b——灯泡球体需要800-3000的强度才能呈现真实灯泡效果)。
blender-materials - Cycles渲染器的≥16,以实现正确的内部灯罩照明——灯泡光线需要在灯罩内反弹并从开口射出。
max_bounces
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, mathpython
import bpy, mathKEY 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 keyrim_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.linkspython
import bpy
world = bpy.context.scene.world
world.use_nodes = True
nodes = world.node_tree.nodes
links = world.node_tree.linksWipe 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, mathpython
import bpy, mathSun 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')
undefinedlamp_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')
undefinedRecipe 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 for the volumetric setup.
references/overview.mdpython
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.mdRecipe 6 — Color-temperature cheat sheet
方案6 —— 色温速查表
| Source | RGB |
|---|---|
| Candle (1850K) | |
| Tungsten (3200K) | |
| LED warm (3000K) | |
| Sunset / golden (3500K) | |
| Daylight noon (5500K) | |
| Overcast sky (6500K) | |
| Blue hour (8000K) | |
Pro mix: Warm key (tungsten) + cool fill (daylight) = the "golden/teal" Hollywood look.
| 光源 | RGB值 |
|---|---|
| 蜡烛(1850K) | |
| 钨丝灯(3200K) | |
| 暖光LED(3000K) | |
| 日落/黄金时刻(3500K) | |
| 正午日光(5500K) | |
| 阴天天空(6500K) | |
| 蓝色时刻(8000K) | |
专业搭配:暖光主光(钨丝灯)+ 冷光补光(日光)= 好莱坞“金蓝色调”风格。
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)
undefinedundefinedNaming convention
命名规范
| Prefix | Meaning |
|---|---|
| Main / key light |
| Fill light |
| Rim or back light |
| Sun lamp |
| Practical lights (lamp, candle, neon, etc.) |
| 前缀 | 含义 |
|---|---|
| 主光源 |
| 补光源 |
| 轮廓光/背光 |
| 太阳光 |
| 实用光源(如台灯、蜡烛、霓虹灯等) |
Common pitfalls
常见问题
| Symptom | Fix |
|---|---|
| Half the model in pitch black | Add fill (Area light or HDRI) |
| Render looks "flat" | Increase key:fill ratio; add rim |
| Hard shadows everywhere | Increase Area size or Sun angle |
| Too dark overall | Boost View Transform exposure or HDRI strength |
| No reflections on materials | Always set a world environment (HDRI) |
| Light inside object | Check world position; light must be visible from camera |
| Backlight blowing out subject | Rim energy ≤ key energy |
| 症状 | 解决方法 |
|---|---|
| 模型一半区域完全变黑 | 添加补光(AREA面光源或HDRI) |
| 渲染画面“平淡” | 增大主光:补光比例;添加轮廓光 |
| 全是硬阴影 | 增大AREA面光源尺寸或太阳光角度 |
| 整体过暗 | 提高视图变换曝光度或HDRI强度 |
| 材质无反射 | 务必设置世界环境(HDRI) |
| 光源在模型内部 | 检查世界位置;光源必须在相机可见范围内 |
| 背光过曝主体 | 轮廓光强度 ≤ 主光强度 |
When to load references/overview.md
references/overview.md何时加载references/overview.md
references/overview.mdLoad 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工作流程、用于合成器中重新打光的灯光组、产品/人像/建筑/角色/动画风格的配置方案。