ArtCraft:用Python实现程序化纹理生成与笔刷模拟的完整指南

发布时间:2026/10/21 18:35:59
ArtCraft:用Python实现程序化纹理生成与笔刷模拟的完整指南
不必从“这是一个多么宏大的想法”说起。这段时间我一直在打磨一个叫ArtCraft的个人项目它是一款把“绘画手感”和“代码确定性”揉在一起的小型程序化艺术生成工具。简单说你给它一组参数、一个随机种子它就能在本地批量产出风格统一、但细节完全不同的纹理、贴图、图案底稿输出成 PNG 序列。它不是要替代 Photoshop、也不跟 AI 绘画抢饭碗它的定位更像是一个“讲逻辑的艺术助手”或“能做主的草稿机”适合独立游戏开发者、程序化美术爱好者、想做材质素材库的设计师甚至只是对“用数学画图”感兴趣的人拿来找灵感。我最初做它是因为做某跨平台小游戏时需要在短时间内凑出一批风格相近的场景纹理手工一张张画太慢直接套 AI 工具风格又飘忽不定而且很多生成平台是“黑盒”这礼拜出的图下周再出可能就完全不是一回事。而 ArtCraft 走的是另一条路所有生成逻辑都跑在本地代码里每种纹理都由可解释的算法决定相同种子加相同参数必然得到相同图像。这种确定性对做项目的人来说太重要了。这篇文章我会把整个项目的定位、技术拆解、核心实现、踩坑记录和后续扩展思路完整聊一遍中间会给出大量可以直接抄作业的代码和配置希望对你在做的同类工具有实际帮助。1. 项目概述与核心设计思路1.1 为什么需要一个“代码化绘制”工具先聊聊做 ArtCraft 之前我面临的真实场景。某跨平台系统的美术风格定的是“低多边形 手绘颗粒感”但正式开发时发现风格一致的场景纹理想靠人手堆几乎不现实。一个大地图的岩石纹理、苔藓纹理、木质纹理放大到 2048×2048 时手绘一张需要三四个小时画完还要反复调对比度让它在引擎里不显得突兀。而如果用纯随机噪声去生成出来的东西往往是一团棉花没有结构也没有笔触感更谈不上“风格”。后来我意识到问题的核心是我们需要的不是一个“会画图的 AI”而是一个“懂得执行绘画逻辑的工具”。就像 3D 建模里用 Houdini 做程序化资产一样2D 纹理也完全可以程序化先定义风格规则笔刷类型、运笔方向、颜色映射规律再交给代码批量执行。ArtCraft 就是按这个思路落地的。它能解决的痛点很明确风格可控、批量产出、结果可复现。而这个“可控”和“可复现”恰恰是传统手动绘制和纯 AI 生成都很难同时满足的。1.2 核心需求拆解把“画风”翻译成“参数”要做一个能落地的艺术生成工具不能靠感觉得把需求一条条拆开。ArtCraft 的项目核心我拆成了四层可复现性每次用相同随机种子和参数必须得到完全相同的图。这是程序化资产生成的地基否则没法批量生产也没法回到几天前调过的某个效果。参数语义化不能让我面对一堆“intensity0.7482”这种不明所以的魔法数字得把参数组织成“笔刷密度”“色彩和谐度”“颗粒粗糙度”这类美术人能看懂的概念。风格约束生成结果必须在一定范围内浮动。ArtCraft 用“基础场 扰动场”的方式来约束表达——底层结构由少数几个大尺度的噪声骨架决定细节只在小范围内波动这样一批图拿出来像是一个系列而不是十个不同画师画的。批量生产能力支持 YAML 配置批量任务一个配置跑出几十张候选图之后再人工挑选和精修。我把生成工具的几种常见路线做了对比工具路线可控性风格一致性批量效率上手成本纯手工绘制最高看画师状态很低高通用 AI 绘画中等一般需反复抽卡中低纯数学噪声生成控制力强但抽象高但等于没风格高高ArtCraft算法 调色 笔刷模拟高高很高中所以说ArtCraft 不是天马行空做了一个“生成图片”的东西它是在“艺术创作工具”这个交叉地带里选了“用确定性算法做可控风格”这个位置。如果你也想搭一个类似的工具不妨先把自己的需求也这样拆一拆别急着写代码。1.3 适合与不适合的场景边界ArtCraft 能做好的事我测试下来集中在三类程序化纹理贴图岩石、木纹、锈迹、布料、墙面、风格化底图概念设计前的快速气氛图、像素画地图的地形色块、序列化素材一套有变化但风格一致的图标背景、卡牌底纹。它可以作为创作的第一站把粗糙但方向上正确的底图快速铺开再进美术软件精修。同时我也得说清楚它的边界它不适合做需要精确形状控制的角色人物插画、不适合做有复杂透视关系的场景叙事图、不适合做必须对标注确语义的图像比如“一只戴帽子的狗坐在窗台上”这种描述性生成。原因很简单ArtCraft 的底层是基于噪声场和笔刷叠加的数学绘画它对“抽象纹理”和“有机细节”有优势对“具象形状”没有语义理解能力。别拿它当全能的生成引擎用那是给自己找麻烦。2. 技术架构与核心模块拆解2.1 技术选型为什么用 Python 而不是 JavaScript 或者 CArtCraft 的渲染核心我用的是 Python 3.10 NumPy Pillow配置管理用 PyYAML进度反馈用 tqdm。这组合在业内做图像原型非常常见不是最炫的但绝对是最省心的。很多人会问Python 性能不是不行吗对纯 Python 循环来处理图像确实不行但核心渲染逻辑只要落到 NumPy 向量化操作上2048×2048 的单张图在普通电脑上也就是秒级到十几秒的量级这个速度对“生成一批素材”的工作流来说完全够了。而且 Python 的优势在于调试迭代极快你调一个笔刷算法改完代码立刻跑图肉眼观察结果来回改几十次也不会烦躁这个体感对开发一个艺术工具来说太重要。如果用 C 写性能确实能上去但每调一次效果的编译、调试成本会把人拖垮。JavaScript 的 Canvas 方案我也考虑过它的生态方便做实时预览但要做复杂的浮点计算和批量任务管理时反而不如 Python 顺手。2.2 随机引擎与种子系统随机性是艺术生成工具的灵魂但也是最容易出问题的部分。ArtCraft 里我没有用 Python 内置的random模块而是全程用numpy.random.default_rng(seed)。这里有个坑我先说一下早期版本我图省事用过numpy.random.seed()这类全局接口结果发现只要代码里任何一个地方多调用了一次随机数整张图的输出就会剧烈变化。这导致我调一个无关参数时画面结构莫名其妙变了排查了半天才发现是随机序列的消费顺序被打乱了。后来我改成两个层面的种子管理全局种子任务级别决定整个批次的风格基准。子模块种子由全局种子通过哈希派生比如rng_brush default_rng(global_seed * 1000 17)每个模块拥有独立随机流互不干扰。这样做的思想可以理解为“按职能隔离随机性”。就像乐队里每个乐手各看各的谱子而不是一群人盯着一页总谱。不同模块之间的随机数不会互相抢位置改笔刷密度参数时就只影响笔刷模块不会把色彩场搅乱。这个设计我强烈建议你在做类似项目时一开始就实现后面会少掉无数烦恼。2.3 核心算法值噪声、FBM 分形叠加与笔刷模拟ArtCraft 生成图像的基础不是什么神经网络而是经典的程序化噪声体系。最核心的是三类算法值噪声一种基于网格顶点随机值做平滑插值的噪声比 Perlin 噪声更快更容易理解适合做基础结构层。FBM分形布朗运动把多个频率、幅度不同的噪声叠加起来得到有层次感的细节。价值就在于它能让一张图既有大的走势又有中等尺度的结构再加上细碎的点缀这是自然纹理“耐看”的关键。笔刷模拟这是 ArtCraft 区别于“普通噪声生成器”的关键。它不是直接拿噪声值当颜色而是先用噪声决定“在哪里下笔”再以模拟笔刷的方式在画布上“按压”出颜料的痕迹。笔刷模拟的实现我用了“圆形渐变遮罩叠加”的思路来模拟油画刷和粉笔刷。想象一下你手里拿着一枚软橡皮章章面上颜色从中心到边缘逐渐变淡然后你用这枚橡皮章在画布上不规则地盖印盖印的位置由噪声场决定盖印的密度和大小由参数控制。每一枚“章”就是一次画笔 dab叠加多了就有了手绘那种一层层堆颜色的质感。这个方案的优点是既不用引入复杂的流体模拟又能得到较强的“手绘”感受计算开销也很可控。核心的笔刷遮罩就是一个半径从中心到边缘渐变的径向梯度配合随机的旋转和压感扰动出来的效果已经能骗过不少人的眼睛了。2.4 调色板与色彩约束方案做艺术生成工具颜色是最容易翻车的一环。早期我犯过一个典型的错误——直接在 RGB 空间里对噪声值做线性映射生成出来的图要么像劣质滤镜要么颜色之间互相“打架”。后来我把颜色处理挪到了 HSV / HSL 空间来做。思路是这样的先把用户提供的一组主色转成 HSV 表示然后通过调整色相偏移、饱和度曲线、明度梯度自动扩展到一组和谐色板。举个例子如果用户输入一个主色我希望生成“同类色”风格就让色相在主色附近小幅波动如果需要“互补色”风格就让色相在另一个区域生成一组对比色。整体映射逻辑是噪声值先映射到 0~1 区间然后用这个值去索引色板上的渐变而不是简单取色。这种“渐变映射”的方式可以类比成给黑白的手绘图加一个渐变色图层涂上去之后阴影和高光关系天然就能保持住。为了让颜色更有“艺术感”我还在明度通道上做了非线性调整。普通线性映射容易让画面中间灰一片缺乏重点我把明度映射曲线调成 S 形让暗部和亮部的层次都更分明中间灰阶被压缩。这是我从摄影后期里借来的思路但用在程序化生成里效果立竿见影。3. 实操过程与核心环节实现3.1 环境搭建与依赖准备ArtCraft 的依赖很少原则上只需要这些包pip install numpy pillow pyyaml tqdm建议用虚拟环境隔离我用的是 Python 3.10理论上 3.9 也能跑。项目目录结构我习惯这样组织artcraft/ ├── configs/ │ └── rock_texture.yaml ├── output/ │ └── runs/ ├── src/ │ ├── main.py │ ├── renderer.py │ ├── noise.py │ ├── brush.py │ ├── palette.py │ └── io_utils.py这样分模块的好处是后面你想加新的噪声类型、新的笔刷模式不用动主流程改或新增模块文件就行。模块之间的调用关系尽量保持单向main.py读配置 → 初始化各模块 → 调用渲染器。每一步的输入输出都很明确出了问题也容易定位。3.2 配置文件设计示例配置文件用 YAML 是因为注释方便、嵌套结构读起来清楚。核心配置长这样# configs/rock_texture.yaml task: name: rocky_grunge base_seed: 20250317 resolution: [2048, 2048] structure: noise_type: value scale: 4.0 octaves: 6 persistence: 0.5 lacunarity: 2.0 brush: type: soft_circle density: 18000 size: [12, 48] opacity: 0.18 rotation_randomize: true pressure_jitter: 0.25 palette: base_color: [68, 76, 82] harmony: analogous hue_span: 12 saturation_range: [0.1, 0.5] value_range: [0.2, 0.9] output: format: png gamma: 2.2 add_grain: true grain_strength: 0.06配置里的每一项都对应渲染器里一个明确的逻辑分支。这里特别说一下base_seed它是整个批次生成的核心别写成动态时间戳否则失去可复现性。调试时从固定种子开始效果满意了再把它变成一个种子列表批量生成系列图。3.3 噪声模块的代码实现接下来是核心代码我先把噪声模块贴出来。这段代码生成的是值噪声和 FBM 函数# src/noise.py import numpy as np from numpy.random import default_rng def value_noise_map(width, height, scale, rng): 生成一张值噪声图范围在 0~1 之间。scale 控制基础网格的大小。 cell_w max(1, int(width / scale)) cell_h max(1, int(height / scale)) grid rng.random((cell_h 1, cell_w 1)) # 双线性插值放大到目标分辨率 x np.linspace(0, cell_w, width, endpointFalse) y np.linspace(0, cell_h, height, endpointFalse) # 这里用 numpy 的网格插值取整坐标和小数坐标 x0 x.astype(int) x1 np.minimum(x0 1, cell_w) y0 y.astype(int) y1 np.minimum(y0 1, cell_h) tx x - x0 ty y - y0 top grid[y0][:, None] * (1 - tx) grid[y1][:, None] * tx return top * (1 - ty) grid[y0 1][:, None] * ty说句实话上面的写法为了展示原理做了简化实际工程里我更推荐用scipy.ndimage.zoom来做插值速度更快、边缘处理更稳。FBM 就是在这个基础上做多层叠加def fbm_map(width, height, scale, octaves, persistence, lacunarity, rng): result np.zeros((height, width), dtypenp.float32) amplitude 1.0 frequency 1.0 total_amp 0.0 for _ in range(octaves): result amplitude * value_noise_map( int(width * frequency), int(height * frequency), scale * frequency, rng ) total_amp amplitude amplitude * persistence frequency * lacunarity return result / total_ampFBM 的叠加逻辑可以理解成把几层“起伏”叠在一起第一层是大的山丘走势第二层是中等起伏第三层是细碎石子每一层细节越来越小、占的权重越来越低。persistence0.5意味着后一层的影响力是前一层的一半这是最常用的配置细节衰减刚好。lacunarity2.0意味着频率每次翻倍比较符合自然纹理的尺度分布。3.4 笔刷系统的实现思路有了噪声场之后最关键的问是“怎么把它变成画面”。ArtCraft 的笔刷系统用了一个挺取巧的办法——先预生成一张“笔刷掩码”图然后通过向量化操作把掩码叠加到画布上。掩码本质上是一个半径范围内的径向渐变中间不透明度高边缘快速衰减。# src/brush.py import numpy as np def soft_circle_mask(radius, hardness0.6): 生成一个软边圆笔刷掩码范围 0~1。hardness1 时边缘最硬。 r int(radius) y, x np.ogrid[-r:r1, -r:r1] dist np.sqrt(x*x y*y) / r mask np.clip(1.0 - dist, 0.0, 1.0) mask np.power(mask, 1.0 2.0 * (1.0 - hardness)) return mask在画布上叠加时不是给每个像素写一个颜色而是把笔刷掩码乘以一个颜色向量再与画布按“Alpha 合成”的方式相加。渲染器主循环里随机选下笔位置的方法是在噪声图上随机采样一个点然后判断该位置的噪声值是否高于某个阈值高于才落笔。这样噪声值高的区域笔触密集、低的区域稀疏就有了“聚散”的节奏感。这在效果上接近中国画里“疏可走马、密不透风”的构图理念——只是现在由算法自动完成。这里有一点要特别注意笔刷叠加次数的数量级。配置里density: 18000意味着要在画布上做一万八千次叠加。如果用 Python 的for循环逐个叠加一幅图画到天荒地老。所以实际做法是把一次批次的笔刷全部预计算出来先生成一个形状为(N, 9, 9, 4)的张量把 N 次笔刷的掩码和颜色一次堆在一起然后用一层循环把积累的掩码渲染上去。即使这样18000 次小掩码叠合在 Python 层也需要优化我的做法是把所有笔刷的绘制位置拼成一个数组分块计算局部区域的叠加结果这样能跑进十几秒。3.5 调色板与颜色映射的工程实现调色板模块我按 HSV 空间来做核心逻辑是“输入一个基色 一个和谐策略输出一组色板”。# src/palette.py import colorsys def build_palette(base_rgb, harmonyanalogous, hue_span12, sat_range(0.1, 0.5), val_range(0.2, 0.9)): base_h, base_s, base_v colorsys.rgb_to_hsv( base_rgb[0]/255, base_rgb[1]/255, base_rgb[2]/255 ) if harmony analogous: hue_offsets [-hue_span, 0, hue_span] elif harmony complementary: hue_offsets [0, 180, 20] elif harmony triadic: hue_offsets [0, 120, 240] else: hue_offsets [0] palette [] for offset in hue_offsets: h (base_h offset / 360.0) % 1.0 palette.append(colorsys.hsv_to_rgb(h, base_s, base_v)) # 再沿饱和度和明度梯度扩展一组 for factor in np.linspace(0.0, 1.0, 5): s sat_range[0] factor * (sat_range[1] - sat_range[0]) v val_range[0] factor * (val_range[1] - val_range[0]) palette.append(colorsys.hsv_to_rgb(base_h, s, v)) return np.array(palette)画面上的映射关系是这样的噪声值为 0.0 时取色板第一行暗部为 1.0 时取最后一行亮部中间值通过色板做近邻插值或线性插值。由于色板的条目在明度上已经做过 S 形拉伸出图后直接就有立体的体积感不用再额外拉对比度。我记得测试这个模块时最有成就感的一刻是把所有参数固定只把harmony从analogous换成complementary同一张噪声结构图立刻从灰蒙蒙的岩石感变成了有明确冷暖对比的戏剧风格。那一刻我确信调色板模块绝对值得做厚实。3.6 输出与风格化后处理生成的核心图完成后不能直接存盘还得做两步后处理Gamma 校正和颗粒叠加。Gamma 校正是为了抵消显示器偏亮的问题。渲染器内部在线性空间计算但显示器输出带了一个 2.2 的伽马曲线所以保存前要把像素值做value ** (1/2.2)的反向校正否则图会看起来比设计时暗很多。这一步别省略否则你会反复怀疑为什么自己调的颜色到了看图软件里就变了样。颗粒叠加用的是高斯噪声强度由grain_strength控制目的是让大片纯色区域有一点摄影质感的“呼吸感”而不是死板的光滑块面。颗粒要在 Gamma 校正之后加否则会被校正曲线改变强度。这算是我踩过的小坑如果你也碰到了“画面颗粒怎么调都不自然”的情况十有八九就是这个顺序搞错了。输出的时候使用 Pillow注意把float32的数组转成uint8再保存否则会报错或者写出奇怪的文件。# src/io_utils.py from PIL import Image import numpy as np def save_image(array, path, gamma2.2, add_grainTrue, grain_strength0.06): array np.clip(array, 0.0, 1.0) if add_grain: rng np.random.default_rng(42) grain rng.normal(0.0, grain_strength, array.shape).astype(np.float32) array np.clip(array grain, 0.0, 1.0) array np.power(array, 1.0 / gamma) img Image.fromarray((array * 255).astype(np.uint8), modeRGB) img.save(path)最终批量生成的架构很简单外层用 YAML 种子列表循环每个种子生成独立目录目录里除了 PNG 图还会自动落一份params.yaml来记录本次渲染的参数快照。这个快照文件在后期回溯是救命级的三个月后你想知道某张图是怎么弄出来的直接看目录里的 YAML 就行。4. 常见问题与排查技巧实录4.1 随机数不稳定全局种子被“污染”最早期我遇到过一个极其恼火的问题给完全相同的配置和种子连续跑两次渲染输出结果却不一样。查到最后发现是用了numpy.random.seed()后又混用了random标准库。两套随机数系统互不通气但代码里某个工具函数偷偷消费了random的序列就把整个随机流的节奏打乱了。解决方法是彻底禁用全局随机接口统一走default_rng生成实例并显式传给需要随机数的模块。代码里定期检查凡是没有接收rng参数的模块一律不允许调用随机函数。这个规则比较严格但换来的确定性非常值得。另一个相关问题是多进程并行渲染时如果只是简单地把种子列表传给每个进程进程内共享的numpy初始随机状态会导致部分进程产生重复结果。解决方式是每个子进程都通过“父种子 子进程编号”的哈希派生一个独立种子确保各自有独立随机流。4.2 性能瓶颈循环太深跑不动第一次用纯 Python 循环写笔刷叠加一幅 1024×1024 的图跑了两分多钟完全没法用。排查性能问题时我用cProfile查了一下发现热点全部集中在笔刷位置的随机采样和掩码叠加的嵌套循环上。优化的思路是“同样的事情尽量向量化实在向量化不了就分块”。具体来说我把整个画布分成若干 64×64 的瓦片区域预先计算每个瓦片内要落多少笔、落在什么位置然后一个瓦片一个瓦片地批量渲染。这样避免了全局随机访问带来的 CPU 缓存频繁失效速度提升了好几个数量级。目前 2048×2048、18000 笔刷的配置在普通笔记本上大概 8~15 秒出图已经进入可接受的范围。4.3 画面“发灰”没精神一类很常见的翻车现场是明明调色板很好看噪声结构也有层次但出图后整体灰蒙蒙的像隔了一层雾。这通常是两个原因叠加导致的。一个是噪声值分布不均匀大量值堆积在 0.45~0.55 附近映射过去自然是一团中灰。另一个是调色板明度梯度的中段太宽导致中灰度占比过高。解决办法有两个第一在噪声最后输出前套一个直方图均衡化或正态化变换让噪声值的分布铺满整个 0~1 区间第二在调色板的明度曲线里做“去中段”处理让暗部和亮部占更大的权重灰阶只在很窄的区间过渡。这两个手段叠加之后画面的体积感和光感会立刻出来。实际上我当时生成岩石纹理时仅调这一项效果就从“像水泥”变成了“像带高光的岩石”。4.4 批量生成时风格跳变严重刚开始批量产出时我盯着十来张图看发现单看每张都还可以但放在一起明显“不是一套”有的偏冷有的偏暖有的细节多到发碎有的又光滑得过了头。问题出在基础结构层——每张图都从完全独立的随机噪声重新采样导致骨架差异太大。解决思路是引入“锚定结构”先生成一张固定的基础噪声场然后把每个种子的随机性变为“在这张基础场上的扰动”。可以类比为先用石膏倒了一个固定的模然后往模子里浇不同颜色的颜料翻出来的形状大体一致但表面颜色和纹理细节各不相同。这样一来系列的风格一致性问题彻底解决ArtCraft 也才算真正能用于游戏批量资产生产。4.5 渲染出的图像和屏幕上预览色差明显最后提一个跟代码无关但非常坑的问题在显示器上预览的成图颜色和拿到游戏引擎或手机里打开往往颜色会差一截。这大概率是色彩空间没做转换——程序里处理的线性颜色输出后没有嵌上 sRGB 标签很多看图器会按默认配置重新解译。解决办法说来也简单保存 PNG 时通过 Pillow 的ImageCms加上 sRGB 色彩配置信息或者直接在程序内做完 Gamma 校正再保存。ArtCraft 选择了后者因为省事且对绝大多数场景足够。如果之后你做了更专业的 HDR 流程或大幅面输出再研究完整的色彩管理也不迟。5. 经验总结与后续扩展方向5.1 项目复盘ArtCraft 做对了什么回看这个项目的开发过程最值钱的决定是把“可复现性”放在了第一位。没有这一步后续所有的调参、批量生成、风格控制都是空中楼阁。也正因如此ArtCraft 在我后续好几个需要批量素材的项目里成了靠谱的生产力工具——我不需要靠运气出图而是把参数调好后让工具按计划产出再进行人工筛选和精修。第二个有价值的决定是笔刷系统的引入。如果没有模拟笔触只是纯颜色分层出图再干净也逃脱不了“塑料感”。但笔刷带来了手绘的痕迹、留下了运笔方向的暗示、营造了颜色叠加的厚度这些微小的不完美恰恰是“艺术感”的来源。这让我更加确信程序化艺术工具的价值不在于把东西生成得“完美”而是生成得有“特征”。5.2 短中期扩展计划从静态纹理到动态创作ArtCraft 现在的版本只做了静态纹理输出后续我计划扩展三个方向。动画支持把噪声场的演化函数引入到时间维度上用连续变化的种子序列生成连贯的纹理流动动画类似流动熔岩、漂浮云雾的效果。这需要把 FBM 叠加一个三维时间切片。实时预览目前调参数还要改配置文件后跑脚本刷图效率不够。后续打算加一个简单的 Tkinter 或 PySide 界面左边调参、右边实时刷新缩略图上手友好度会直线上升。更丰富的笔刷模式增加方向性笔刷用噪声梯度引导笔触走向、厚涂笔刷通过叠代次数的概率分布模拟颜料堆积、刮刀效果算法擦除部分区域破坏整齐感。这些是短期内最能直接做出新风格的方向。如果你也想在类似工具里扩展功能我的建议是别一开始就做“大而全”先把手上的核心流程跑通跑稳然后一次只加一个能明显改变效果的功能。这样每次迭代都能有肉眼可见的新变化比憋个大版本再发布有意思得多。5.3 给同类爱好者的实用建议最后给对程序化艺术生成感兴趣的朋友几个建议吧。第一多看真实纹理而不是只看算法效果。我经常拿手机拍路边的老墙、树皮、生锈的铁皮放大看它们的纹理是怎么分层、怎么分布然后把观察到的规律翻译成噪声参数。算法永远只是工具眼睛和审美才是决定作品上限的东西。第二调试时每次只改一个参数。ArtCraft 的参数维度很多一次改两三个变量就很难判断是哪个导致了画面的变化很容易陷入瞎试的泥潭。我习惯准备一个调试日志种子、改了什么、前后效果对比结论整理几轮之后对参数的理解会深入很多。第三一定要把“失败”也存档。有些当时觉得“好像不太对”的图过两周回头看反而是很有趣的风格雏形。艺术生成工具最大的魅力之一在于可控范围内的意外把不同种子的输出统一归档偶尔翻一翻灵感有可能就藏在当时被你嫌弃的那一格里。