14 KiB
打光功能实施文档
Version: 0.1
Status: Draft
Scope: popiart-node-canvas
Target area: bottom composer, image node controls only
1. 目标
在底部 composer 的图片节点控制区中新增「打光」入口,尺寸、布局、交互状态与现有「风格」「标记」「聚焦」「参考」保持一致。
该功能是图片节点专有能力,只在当前选中图片节点时出现或可用。这里的图片节点包含 imageInput 图片输入节点和 nanoBanana 图片生成节点。用户必须先上传图片,或当前图片节点已有可编辑图片素材后,才能点击「打光」按钮。点击后弹出「打光效果」面板,用户可以调整全局打光参数、选择主光源方向、开启轮廓光,并在智能模式中输入描述、上传打光参考图、选择预设打光效果。
当前版本不扩展到视频、音频、3D 或其它媒体节点。
2. 用户体验
2.1 底部入口
在底部 composer 顶部功能格子区域新增「打光」按钮:
- 位置建议:放在「聚焦」之后、「参考」之前。
- 尺寸:与现有格子一致,当前实现约为
h-14 w-16。 - 可见范围:
- 仅当前 composer 处于图片节点模式时显示,包括
imageInput和nanoBanana。 - 非图片节点、流程处理节点、多选状态、空创建状态不显示。
- 仅当前 composer 处于图片节点模式时显示,包括
- 可点击条件:
- 当前图片节点已经有至少一张上传图片或可编辑图片素材。
imageInput判断data.image是否存在。nanoBanana判断draft.inputImages.length > 0,并兼容当前节点已有outputImage或上游连接图片的场景。- 如果没有图片,按钮保持禁用,不弹窗。
- 状态:
- 默认:深色背景、灰色文字、灰色描边。
- Hover:提高描边和文字亮度。
- 激活:使用与其它 active chip 一致的 cyan 高亮。
- 不可用:没有上传图片时禁用,title 提示
请先上传图片。
- 文案:
- 中文:
打光 - 图标:优先使用 lucide 图标,如
Sun,Lightbulb,Sparkles或Flashlight;如果项目当前没有 lucide,可先沿用现有符号风格,后续统一替换。
- 中文:
2.2 弹窗基础模式
点击可用状态下的「打光」打开居中的「打光效果」弹窗。基础模式内容对应示意图三:
- 标题:
打光效果 - 关闭按钮:右上角。
- 左侧预览区:
- 顶部为视角切换:
透视/正面 - 中间为球形/空间光照预览图。
- 底部为
重置参数。
- 顶部为视角切换:
- 右侧参数区:
- 分组标题:
全局 智能模式开关,默认关闭。亮度slider。- 亮度百分比输入或只读显示,默认
50%。 颜色色块选择,默认白色。主光源方向按钮:左侧顶部右侧前方底部后方
轮廓光开关,默认关闭。
- 分组标题:
- 底部右侧:
- 成本提示,如
14 - 生成/应用按钮,沿用 composer 的上箭头视觉。
- 成本提示,如
2.3 智能模式
开启 智能模式 后弹窗横向扩展,对应示意图四:
- 保留左侧预览区。
- 中间保留
全局参数区。 - 右侧新增智能模式区:
- 标题:
智能模式 - 文本输入框,占一整行宽度,placeholder:
简单描述你想实现的打光效果,或者情绪风格
打光参考图上传按钮。预设网格。
- 标题:
- 预设建议首批 8 个:
过曝胶片蓝色逆光伦勃朗光赛博朋克落日迷幻神秘暗调黄金时刻清晨冷灰
- 每个预设包含:
- id
- label
- thumbnail
- prompt fragment
- structured lighting patch
3. 数据模型
新增打光参数类型,建议放在 src/types/nodes.ts 或单独 src/types/lighting.ts 后再导出。
export type LightingViewMode = "perspective" | "front";
export type LightingDirection = "left" | "top" | "right" | "front" | "bottom" | "back";
export interface LightingPreset {
id: string;
label: string;
thumbnail: string;
prompt: string;
settings: Partial<LightingSettings>;
}
export interface LightingSettings {
enabled: boolean;
viewMode: LightingViewMode;
smartMode: boolean;
brightness: number;
color: string;
mainLightDirection: LightingDirection;
rimLight: boolean;
smartPrompt: string;
referenceImage: string | null;
selectedPresetId: string | null;
}
在 NanoBananaNodeData 增加:
lighting?: LightingSettings;
默认值建议放在 src/store/utils/nodeDefaults.ts:
export const DEFAULT_LIGHTING_SETTINGS: LightingSettings = {
enabled: false,
viewMode: "perspective",
smartMode: false,
brightness: 50,
color: "#ffffff",
mainLightDirection: "front",
rimLight: false,
smartPrompt: "",
referenceImage: null,
selectedPresetId: null,
};
4. 文件拆分建议
4.1 新增组件
新增目录:
src/components/lighting/
建议文件:
LightingEffectModal.tsx- 弹窗容器和整体布局。
LightingPreview.tsx- 左侧光照预览。
- 第一版可用 CSS/SVG/Canvas 静态模拟,不必接真实 3D。
LightingControls.tsx- 亮度、颜色、方向、轮廓光。
LightingSmartPanel.tsx- 智能模式输入、参考图、预设网格。
lightingPresets.ts- 预设配置。
index.ts- 统一导出。
4.2 修改现有文件
src/components/composer/GenerationComposer.tsx- 增加 active chip 类型:
"lighting"。 - 在图片节点模式的功能格子区域插入「打光」按钮。
- 增加是否允许打光的派生状态,例如
canOpenLightingPanel。 - 当没有上传图片或可编辑图片素材时禁用按钮。
- 管理
LightingEffectModal打开/关闭。 - 从当前 draft 或当前节点数据中读取 lighting。
- 应用弹窗保存结果到当前 composer draft。
- 增加 active chip 类型:
src/types/nodes.ts- 新增
LightingSettings类型或从types/lighting.ts导入。 NanoBananaNodeData增加lighting?: LightingSettings。
- 新增
src/store/utils/nodeDefaults.ts- 给图片节点补默认
lighting。
- 给图片节点补默认
src/store/execution/nanoBananaExecutor.ts- 将
lighting转成 prompt 增强或 provider 参数。
- 将
src/components/__tests__/...- 增加 composer 入口和弹窗交互测试。
5. 与生成链路的关系
第一阶段不要改 provider 协议,先采用 prompt 增强方式。该增强只作用于图片节点生成:
function buildLightingPrompt(settings: LightingSettings): string {
if (!settings.enabled) return "";
// 根据方向、亮度、颜色、轮廓光、智能描述、预设 prompt 拼接自然语言。
}
图片生成执行时:
- 读取
NanoBananaNodeData.inputPrompt。 - 读取
NanoBananaNodeData.lighting。 - 确认当前节点有可编辑图片输入;没有图片时不启用 lighting。
- 生成 lighting prompt fragment。
- 将 fragment 附加到最终 prompt。
示例:
Lighting: front key light, 50% brightness, white color temperature, soft cinematic illumination, rim light disabled.
如果智能模式开启:
Lighting style: {smartPrompt}. Use the selected lighting preset: {presetPrompt}.
如果有参考图:
- 第一版:把 reference image 加入
inputImages或dynamicInputs前要谨慎,避免改变原始参考图语义。 - 推荐第一版只保存,不传给 provider。
- 第二阶段再支持「参考图作为打光参考」的 provider-specific 参数。
6. 分阶段实施计划
Phase 1: 文档与类型
目标:先定义功能边界,不改变运行行为。
任务:
- 新增本文档。
- 新增
LightingSettings类型。 - 在图片节点数据结构中加入可选
lighting。 - 新增默认 lighting 配置。
验收:
- TypeScript 类型通过。
- 旧 workflow 不需要迁移也能正常打开,因为
lighting是可选字段。
Phase 2: 底部 composer 入口
目标:图片节点底部出现「打光」格子,并且只有上传图片后才能打开弹窗。
任务:
- 扩展
activeChip类型。 - 添加「打光」按钮,尺寸与其它按钮一致。
- 仅在图片节点模式显示「打光」按钮。
- 计算当前图片节点是否已经上传图片。
- 没有图片时禁用按钮并显示
请先上传图片提示。 - 新增
LightingEffectModal的基础 shell。 - 点击可用按钮打开弹窗。
- 关闭按钮可关闭弹窗。
验收:
- 底部「打光」按钮和其它格子同宽同高。
- 非图片节点看不到「打光」按钮。
- 图片节点没有上传图片时,「打光」按钮不可点击。
- 图片节点上传图片后,「打光」按钮可点击。
- 按钮 active 状态可见。
- 弹窗打开和关闭稳定。
Phase 3: 基础参数 UI
目标:完成示意图三的基础模式。
任务:
- 实现标题、关闭按钮、左右布局。
- 实现视角切换。
- 实现静态光照预览。
- 实现亮度 slider。
- 实现颜色选择。
- 实现主光源方向按钮组。
- 实现轮廓光开关。
- 实现重置参数。
- 实现保存/应用按钮。
验收:
- 所有控件都有受控状态。
- 重置后恢复默认值。
- 应用后写入当前图片节点的
data.lighting。 - 关闭再打开能读回当前节点保存的值。
- 如果上传图片被移除,弹窗不能再次打开。
Phase 4: 智能模式 UI
目标:完成示意图四的智能模式完整面板。
任务:
- 开启智能模式后扩展弹窗宽度。
- 添加智能描述输入框。
- 添加打光参考图上传按钮。
- 添加 8 个预设卡片。
- 点击预设后:
- 设置
selectedPresetId - 合并预设
settings - 写入或追加
smartPrompt
- 设置
- 上传参考图后显示缩略状态。
验收:
- 智能模式开关不会丢失基础参数。
- 预设选中状态清晰。
- 参考图可上传、替换、清除。
Phase 5: 生成执行接入
目标:打光参数实际影响图片生成。
任务:
- 新增
buildLightingPrompt(settings)工具函数。 - 在
nanoBananaExecutor或 prompt 构造点追加 lighting fragment。 - 确保未启用 lighting 时生成结果与旧逻辑一致。
- 对智能模式、预设、亮度、方向分别加单元测试。
验收:
enabled: false不改变请求 prompt。enabled: true会附加明确的 lighting 描述。- 智能模式描述会进入最终 prompt。
- 预设 prompt 会进入最终 prompt。
Phase 6: 测试与回归
目标:确保不会破坏 composer、节点编辑和保存加载。
建议测试:
GenerationComposer.test.tsx- 渲染打光按钮。
- 点击打开弹窗。
- 应用参数调用
updateNodeData。
LightingEffectModal.test.tsx- 控件初始值。
- 亮度/颜色/方向/轮廓光交互。
- 智能模式展开。
- 预设选择。
nodeDefaults.test.ts- 图片节点默认包含 lighting。
nanoBananaExecutor.test.ts- lighting prompt 拼接。
workflowStore.integration.test.ts- 保存/加载旧 workflow 不报错。
7. 实现细节
7.1 Composer 状态同步
现有 composer 有本地 draft 和 dirty fields。打光弹窗建议不要直接每次改控件就写 store,而是:
- 打开弹窗时从当前节点读取 lighting。
- 弹窗内部维护临时状态。
- 点击应用后调用 composer 的 patch/update。
- 如果当前没有图片节点,或当前图片节点没有上传图片,则不允许打开弹窗,也不写入 draft。
这样可以避免用户在弹窗里试参数时频繁污染 undo history。
7.2 旧工作流兼容
读取时始终使用:
const lighting = data.lighting ?? DEFAULT_LIGHTING_SETTINGS;
保存时只有用户打开并应用后才写入,避免旧工作流文件无意义膨胀。
7.3 预览实现策略
第一版建议用轻量 CSS/SVG:
- 一个圆形背景模拟空间球。
- 一张竖向图片卡片作为主体。
- 根据
mainLightDirection改变光束渐变角度。 - 根据
brightness改变光束 opacity。 - 根据
color改变光束颜色。 rimLight开启时给主体卡片增加边缘高光。
后续如果需要真实 3D 预览,再引入 Three.js,但第一版不建议增加复杂度。
8. 风险与注意事项
- 不要把打光功能做成隐藏的新节点。当前需求是图片节点的 composer 参数入口,不是图节点类型。
- 不要在空创建模式、视频节点、音频节点、3D 节点或流程处理节点上开放打光入口。
- 不要在没有上传图片时弹出打光面板。
- 不要让弹窗直接调用生成接口。生成仍然由现有 composer/节点执行按钮触发。
- 不要把参考图默认混入
inputImages,否则可能改变图片生成语义。 - 不要一次性重构整个
GenerationComposer.tsx。这个文件已经很大,应先最小接入,后续再拆。 - 注意中文文案编码,当前仓库已有部分乱码,新增文件必须使用 UTF-8。
9. 推荐提交顺序
docs: add lighting effect implementation planfeat: add lighting settings types and defaultsfeat: add lighting chip to generation composerfeat: add lighting effect modal basic controlsfeat: add lighting smart mode presetsfeat: apply lighting settings to image generation prompttest: cover lighting composer and executor behavior
10. 最小可交付版本
MVP 包含:
- 图片节点底部「打光」格子。
- 未上传图片时按钮禁用。
- 上传图片后点击按钮弹出打光面板。
- 点击打开弹窗。
- 亮度、颜色、主光源方向、轮廓光。
- 参数保存到当前图片节点。
- 生成时把打光参数拼入 prompt。
智能模式、预设缩略图、参考图上传可以作为第二个增量交付。