今年年初我在整理一组民俗手工艺的视觉资料时,起了个代号叫 artcraft 的小项目。起因特别具体:那些手工印染的布片、手工抄造的纸张、带颗粒感的木刻拓印,拍下来、扫进电脑之后,全都变成了“平的”。颜色还在,纹理也在,但那股“手气”——那种只有手工制作才会有的、既均匀又不均匀的微妙质感——彻底丢了。我试过各种滤镜、素材库,没有一样能还原那种感觉,于是开始琢磨:能不能自己做一套工具,把“手工感”变成可计算的参数。artcraft 就是这套工具的项目代号,它面向插画师、设计师、生成艺术爱好者,也适合本来就研究手工艺数字化的人参考。这篇文章先聊聊为什么会有它、中间踩了哪些坑,以及最后走通的工作流长什么样。
1. 为什么叫 artcraft:艺术与工艺在数字时代的错位
1.1 “工艺感”到底是一种什么东西
先定义一下我说的“工艺感”。不是指产品做工精良那种“工艺”,而是指视觉上的手作痕迹:一张手工纸上隐约可见的纤维纹路、一片扎染布料边缘的色晕过渡、一幅木刻版画里深浅不一的油墨堆积。这些痕迹放在工业品里会被当成瑕疵,但放在手工作品上,恰恰是辨识度和温度的来源。
这种痕迹最微妙的地方在于:它们既不是完全随机的噪声,也不是规则重复的图案,而是一种介于“有序”和“无序”之间的状态。太整齐就不是手作的,太混乱就是废品了。人眼对这种状态的判断其实非常敏锐,但你让一个程序去“理解”它,就特别难。因为程序擅长的是有明确规则的东西,而手工感偏偏是一种模糊规则。
这也是 artcraft 这个名字的来源。Art 是艺术表达,Craft 是工艺语言,合起来就是一套把工艺的“性格”注入数字创作的方法。它不打算替代手艺,只想当一个翻译层:把传统工艺里的视觉特征拆开、量化,再用程序重新组合出来。
1.2 数字工具为什么做不出“手气”
我一开始以为这是滤镜参数没调对,后来才意识到是根本思路错了。主流数字绘图工具追求的是可重复、可修正——画错了可以撤,滤镜参数是固定的,随机效果用的多半是一维均匀分布的白噪声。
均匀白噪声的问题在于,每一次采样都彼此独立,没有相关性,所以放大看是“沙沙”的颗粒,完全没有手工痕迹里那种“这里带一下、那里连一片”的连续感。
手工感的真正秘密,在统计上叫“局部相关”。水渍晕开一定是一圈一圈向外扩散的,墨色最深点周围邻近区域一定跟着变深,染布上某条纬线偏了,相邻的经纬线也会跟着松紧变化。这些偏差不是孤立像素,而是一块一块、一片一片出现的。好的数字生成纹理要复现的,正是这种空间上的相关性,而不是单纯的“加噪声”。
后来我看很多生成艺术教程,发现大家都用 Perlin 噪声做地形、做云,但很少深究噪声的“相关性”参数到底在还原什么。实际上 Perlin 噪声能形成连续起伏,就是因为它带空间相关性——这正是它比纯随机更适合模拟手作感的原因。但光有 Perlin 噪声还不够,后面我会讲到还需要域扭曲、多层叠加这类手段配合。
1.3 artcraft 的定位:不是滤镜,而是一套工艺翻译流程
所以 artcraft 从一开始定的调子就是:不是做一个滤镜,而是做一套流程。流程包括四段:样本分析、特征建模、程序生成、输出适配。把真实的手工样本拿来研究,提取亮度分布、纹理周期、色彩漂移等统计数据,再转换成参数,用算法生成出“性格相近”的数字纹理。
这个定位看起来简单,实际做起来牵扯到一堆问题。比如手工纸的纤维分布和木刻版的刀痕规律完全是两码事,你要是用一个噪声模型硬套,肯定出不来。我后面的做法是给不同工艺风格建立不同的生成策略,但统一走同样的管线流程,这样既灵活又可控。
2. 拆解工艺特征:把“感觉”变成可调参数
2.1 从一块手工纸开始的逆向分析
我花了大概两周时间收集素材。去文具店、旧货市场、朋友的工作室翻出各种手工制品:手工纸、扎染布、木版画、陶瓷釉面、编织筐……每一样都先做宏观记录,再做微距拍摄和扫描。
这里有个特别重要的经验:不要只采集“成功的好看样本”,失败样本反而更能暴露工艺的边界。染花了的手帕、压太重的版画、起毛的纸边,这些“出错样本”告诉你有多少种出错的可能,而这些可能性在生成算法里正好对应参数范围的边界。
比如手工纸,我看到的最典型的全局特征是纸浆分布不均匀。靠近模具边缘的部分纸浆更薄、透光更多,中心区域厚实、颜色偏暖。这种变化是缓慢的、渐进的,不是噪点式的。如果你拿一张扫描图直接提取亮度方差,你会得到一个数值,但把这个数值直接用在同一张图上,出来的效果可能很“花”——因为你丢掉了一个关键信息:偏差在空间上的分布趋势。
所以我又把数据分成了两类看:一类是低频趋势,描述整张图上亮度或色相的缓慢漂移;另一类是高频细节,描述局部的纤维颗粒、气泡、刀痕。分开统计之后,再合成,才得到可以用来复现的参数基础。
2.2 三个核心特征维度:全局不均、局部相关、随机瑕疵
观察多了之后,几乎所有工艺纹理都能归进三个维度。
第一个是全局不均。画面整体从左上到右下存在缓慢的亮度或色相漂移,比如手工纸的纸浆分布、扎染布的颜色沉降、釉面的厚薄变化。它和位置强相关,通常可以用一个低频噪声场或者多项式趋势面来描述。
第二个是局部相关。某像素的偏差会带动周围一片区域一起偏差,偏差值在空间上形成自相关。水渍的边缘、油墨的堆积、编织的松紧,都属于这一类。局部相关是“手工感”里最核心的一部分。
第三个是随机瑕疵。偶尔冒出来的意外痕迹:刀痕、纤维颗粒、气泡、墨点。它们分布稀疏,形态突兀,和周围环境没有平滑过渡,反而制造了“这件东西真被人手碰过”的心理暗示。
这三个维度不是互相独立的。一张真实的手工纸,往往全局不均很弱,局部相关很强,瑕疵密度中等;一块木口木刻的印张,全局不均来自于施压方向,局部相关体现在线条边缘,瑕疵则可能来自油墨中的杂质。因此,参数模型做成分层结构更准:先做全局层,再做局部层,最后叠加随机瑕疵层。
2.3 参数模型:如何量化“手工感”
有维度之后,我设计了一组参数,命名尽量直白,用起来不烧脑。
| 参数 | 作用 | 建议初始值(按 2K 分辨率) |
|---|---|---|
| hand_strength | 全局不平整强度,控制整体漂移幅度 | 8%–15% 的亮度偏差 |
| local_radius | 局部相关半径,决定偏差的影响范围 | 30–60 像素 |
| texture_scale | 特征纹理尺度,匹配具体工艺的颗粒尺寸 | 随工艺调整,纸纹偏小、水渍偏大 |
| defect_density | 随机瑕疵出现密度 | 每万像素 0–2 处 |
| variation_seed | 随机种子,复现同一组手感的钥匙 | 任意整数 |
这些参数里,local_radius 是最容易被忽略也最关键的。调小了是细碎噪点,调大了是团块污渍,而手工感通常落在 30 到 60 像素这个区间,具体取决于最终输出分辨率。一旦你把这个参数想明白了,生成出来的纹理立刻从“电脑画的”变成“有点像手工的”。
另外一个心得是:参数要有“可复现性”。每一个调好的组合都应当连同一张种子快照一起存下来,下次想再做出类似风格,直接载入快照就行。做参数记录这个习惯,让 artcraft 的迭代效率高了很多。
2.4 采集与预处理的实操建议
参数分析的前提是素材本身干净可靠。这里有几个我自己吃过亏才总结出来的要点:
光源用色温 5000K 左右的匀光板,避免直射硬光造成不可逆的高光过曝。相机一定用 RAW 格式,后期校准白平衡的空间会大很多。每次拍摄前用中性灰卡现场校正白平衡,否则同一批素材会因为光线微变出现系统性色偏。
扫描仪虽然便捷,但自带的自动锐化、自动对比度一定要关闭。那些“增强”功能对工艺纹理分析来说全是污染。锐化会把纤维边缘变成一条条生硬的亮线,对比度增强则直接改变亮度分布统计,你辛辛苦苦采集的数据会被哪一步悄悄改掉都不知道。
3. 生成管线的架构与实现
3.1 总体流程:分析、建模、生成、输出
artcraft 的整体流程分成四段。第一段是样本分析,用 Python 加 OpenCV 从素材里提取统计口径;第二段是特征建模,把提取到的统计量映射成参数;第三段是程序生成,我用 Python 加 NumPy 做核心算法,不依赖重型框架;第四段是输出适配,生成 16 位 PNG 或 TIFF 作为母版,再按下游需求导出。
选 Python 而不是 C++ 或 JavaScript,理由很实际:纹理处理这块生态最顺。OpenCV 处理图像、NumPy 做数组运算、matplotlib 做可视化预览,迭代速度非常快。C 系性能更好,但我在这个项目里更需要快速试错,而不是一开始就堆性能。后面遇到性能瓶颈,再做局部优化也不迟,语言本身不是障碍。
流程设计上还有一点经验:每个阶段都要有可视化输出。分析阶段要把统计图表画出来,建模阶段要能直接预览中间层,生成阶段当然要出图,输出阶段要自动生成网格预览。人眼只有在“每一步都看到”的情况下,才能快速发现是哪一步出了问题。脚本跑完才给一张最终图,排错成本会高好几倍。
3.2 核心算法:白噪声到相关噪声的跨越
生成阶段的核心是“相关噪声”。我第一次用 NumPy 直接生成随机数组叠加亮度,结果惨不忍睹,那更像电视雪花。后来换成“多层平滑噪声 + 域扭曲”的组合,才算找对方向。
平滑噪声先在高斯滤波下获得空间相关性,模拟“局部相关”的手工偏差;再通过域扭曲把坐标偏移,模拟水流、风力、手劲这类外力造成的扰动感。核心逻辑可以简化成下面这段代码:
import numpy as np from scipy.ndimage import gaussian_filter def layered_noise(shape, base_scale=0.15, smooth_radius=2, warp_strength=30, seed=None): rng = np.random.default_rng(seed) # 随机噪声作为输入信号 noise = rng.random(shape) # 高斯平滑产生空间相关性,模拟“局部相关”的手工偏差 smooth = gaussian_filter(noise, sigma=smooth_radius) # 用另一个平滑噪声作为域扭曲偏移,模拟外力扰动 offset_x = (gaussian_filter(rng.random(shape), sigma=warp_strength) - 0.5) offset_y = (gaussian_filter(rng.random(shape), sigma=warp_strength) - 0.5) y, x = np.indices(shape) warped_y = np.clip((y + offset_y).astype(int), 0, shape[0] - 1) warped_x = np.clip((x + offset_x).astype(int), 0, shape[1] - 1) return smooth[warped_y, warped_x]代码本身不复杂,关键是几个参数的配合。base_scale 控制特征的整体起伏,smooth_radius 决定偏差的凝聚范围,warp_strength 控制“拧”的程度。实际调试时,我建议先固定 warp_strength,调 smooth_radius 到手感合适,再回头调 base_scale。两个参数同时动,很容易迷失方向,一次只调一个效率反而最高。
3.3 调参界面:让非程序员也能玩
算法阶段用脚本调试没毛病,但项目继续往下做,交互必须要下来。我后来给 artcraft 加了一个很轻的调参面板:左侧是参数滑杆,右侧是实时预览,参数词典直接和生成函数绑定。
一个细节是,预览一定用低分辨率渲染,比如 512 乘 512,调到位再在后台生成 4K 大图。这个交互改进救了我非常多次。参数在脚本里靠猜,在可视化界面里靠眼睛,眼睛的反馈速度比 print 输出高一个量级,而且能直接看出“哦,这个纹路还是偏平”、“那个边缘还是太锐”。
面板界面上,我还放了“随机种子”的刷新按钮。因为生成艺术里经常遇到“这一版大体满意,但某一块特别奇怪”的情况,这种时候如果没法快速换种子,就会反复手动改参数,效率极低。加一个按钮,十秒钟就能遍历二十个方案。
3.4 输出适配:别让成品卡在管线的最后一米
生成完纹理不是终点,还得能让别人用起来。我的输出做了三层适配:
母版是 16 位非线性 TIFF,保留最大动态范围,方便后期处理。游戏或实时渲染场景输出无缝平铺版,平铺方向做边界镜像混合,避免接缝。三维软件则导出颜色、粗糙度、法线一组贴图,直接进材质流程。
这里提醒一点:不要只在 RGB 里调色。很多工艺纹理的真实感来自高光响应,比如木刻油墨的堆积在斜光下有明显凹凸,单靠颜色贴图永远还原不出来,必须生成法线贴图。我一开始忽略了这个,拿去渲染后怎么看都“假”,后来补上法线才解决。
4. 实战案例:三个工艺风格的复现过程
4.1 水墨晕染:纸纤维分布与水的扩散
第一个案例做的是纸上水墨晕染。观察参考样本后,我意识到晕染效果由两层合成:纸面纤维分布造成的底色不均匀,以及水携带墨粒向外扩散留下的边缘环。
生成时,我先用大尺度平滑噪声模拟纸浆薄厚度,得到底色的明暗起伏;再用两层值噪声加域扭曲模拟墨的扩散路径。关键参数是 warp_strength,我把它调到让墨迹边缘呈不规则“毛边”,又不能让毛边碎到看不出形状。
一组参数快照摘录如下:hand_strength 拉到 12%,local_radius 设为 48 像素,texture_scale 用两层(一层 16 像素模拟纤维,一层 64 像素模拟水渍),defect_density 设为每万像素 0.5 处。
打印出来对比真实样本后,我发现最接近的反而是一组边缘环出现概率略高的参数。手工晕染的“失败样本”往往比平均态更有辨识度,这个现象让我意识到:追求完美的平均态,反而会丢掉手作感。
4.2 木刻版画:印刷压力与套色偏差
木刻版画是另一个风格,特点在于刀痕和印刷压力。每道刻线都带方向性,印出来深浅又受压力影响。
我先用贝塞尔曲线生成一组平行或放射状刀痕,再给每条曲线叠加随机位移和高斯扰动,模拟手工运刀的不稳。印刷压力的不均匀,则用一个低频噪声场调制油墨厚度:压力大的地方墨厚、颜色深,压力小的地方露出纸纹。
第二个关键点是套色偏差。多版套印不可能完全对齐,我在不同颜色的图层上分别做了 1 到 3 像素的随机偏移。这个操作效果好到超出预期——错位带来的“手工感”比任何纹理滤镜都直接。我甚至觉得,套色偏差才是版画风格里最值得保留的“算法基因”。
4.3 手工编织纹理:经纬交错的周期性变异
编织纹理结构性强,单靠随机噪声会毁掉经纬关系。我换了个思路:先建立规则的经纬网格,再在网格节点上施加相关噪声作为位移和粗细扰动。横向的纬线整体偏一个趋势,纵向的经线偏另一个趋势,这样保留了编织的周期性,又破坏了工业织物的“完美重复”。
实际操作上还有一个技巧:纱线粗细要在渲染阶段做邻近插值,不能只改线宽。否则放大后边缘会糊成一团,失去编织那种根根分明的触感。
编织这个案例让我明白了一件事:不同工艺的“手工感来源”不一样。纸张靠纤维分布,版画靠压力与错位,编织靠张力和周期变形。参数模型是统一的,生成策略必须分家,不能偷懒用一套算法打天下。
5. 踩坑记录:从“像”到“是”之间隔着的那些坑
5.1 扫描纹理的摩尔纹与明暗不均
第一个大坑来自素材采集环节。用手持扫描仪扫描大张手工纸,出来总带着一排一排的细条纹。我开始以为是样本脏了,后来才发现是扫描仪的周期性噪声。
解决办法比较简单:同一个位置扫三到五次,对齐后取逐像素中值。大多数周期性条纹会被大幅削弱。要是实在去不干净,可以转去做频域滤波,在傅里叶谱上把对应频率的峰值压平,再反变换回来。这个操作我用 OpenCV 写过,核心不超过二十行,但前置条件是要保证多次扫描的几何对齐,不然中值滤波会把边缘磨花。
5.2 随机参数失控:自然感与噪点感的分界线
翻车率最高的操作是调大随机强度。早期我总嫌生成的纹理太平,于是把 hand_strength 一路往上加,结果得到的是让人眩晕的雪花噪点,完全不是手工感。
后来我才彻底想明白:手工感的强度来自“偏差在相邻区域的连续性”,而不是偏差的绝对值。好的做法是保持中等偏差幅度,把 local_radius 调大,让偏差大面积地、缓慢地变化。
打个比方,一条线画直了不算手工感,画得“整体微微弯、弯得均匀”才像人手画的。这个道理说起来简单,但我是在反复翻车之后才真正内化的。
5.3 性能优化:4K 纹理生成的耐心考验
第三阶段的问题出在性能。基础版算法在 4K 分辨率下,多层平滑噪声加域扭曲一跑就是几十秒到几分钟,迭代调试根本没法忍。
我做了三件事。第一,预览一律走 512 分辨率,参数调整实时反馈。第二,把噪声场预先计算一次并缓存,之后任何一次参数微调都不需要重建底层场。第三,最终输出分块生成,块的边界做叠合平均,避免出现拼缝。
优化之后,常见尺寸的最终生成时间控制在几秒内,预览保持在几十毫秒级别。这个改进让调试节奏从一个上午调三版,变成十分钟调三版,直接决定了我能把项目做到什么程度。
5.4 色彩管理:屏幕上的手工感与纸上的手工感是两回事
最后一个坑最隐蔽,它不在算法里,而在显示环境。我在显示器上调出的颜色,一旦打印或者第三方设备输出,效果经常判若两物。手工纸的米白、油墨的暖黑,在广色域屏幕上看起来柔和,到普通打印上就发灰、发青。
解决起来其实就一条:全程用统一色彩环境。显示器按 sRGB 工作,打印前用软打样模拟目标介质。纹理生成再准,颜色管理不到位,最终还是会打回原形。
我把这一节的踩坑情况整理成一个表,方便遇到同样问题时自查:
| 踩坑 | 根因 | 我的解法 |
|---|---|---|
| 扫描出现细密条纹 | 扫描仪周期性噪声 | 多次扫描取中值 / FFT 压平频峰 |
| 随机过度,画面像雪花 | 忽略了局部相关性 | 小强度 + 大 local_radius |
| 4K 生成太慢 | 多层噪声重复计算 | 低分辨率预览 + 噪声场缓存 + 分块生成 |
| 打印色偏严重 | 色彩环境不一致 | 统一 sRGB + 软打样 |
6. 后续扩展:artcraft 还能怎么玩
6.1 自定义笔刷底座:把工艺纹理带进日常绘画
做完生成管线后,我最常做的事是把 artcraft 的输出转成绘图软件的自定义笔刷纹理底座。一颗笔刷的质感好不好,很大程度上取决于它的噪点形态是不是“相关噪声”。规则噪点画出来是一层死板的颗粒,相关噪声画出来是带着呼吸感的痕迹。
因此我做了一组小脚本,把生成纹理直接量化成笔刷库。画插画的时候随手就能刷出纸纹、布纹和版画颗粒。这大概是项目里使用频率最高的功能。
6.2 与生成式 AI 的协作方式
现在生成式 AI 很常用,artcraft 和它的关系不是替代,而是互补。我会先用 artcraft 生成带有明确工艺特征的结构图——这部分程序控制力强,能保证构图、纹理周期和色彩倾向。再把结构图作为条件,交给 AI 模型补足细节层次。
这种“程序定骨架、模型填肉”的方式,比直接让 AI 空想“手工纸质感”要稳得多。AI 的特点是发散性和想象力,但在精确的结构控制上容易跑偏,programmatic generation 恰好补上了这块短板。
6.3 建立自己的工艺数据库
最后是积累层面的建议。artcraft 运行到现在,已经积累了几十类样本,每一类样本对应一套参数快照,点击就能复现当时的生成效果。我称它为“工艺数据库”。
做这件事最大的价值,是让“感觉”变成可以检索、复制、演进的记录。以后想做某个风格,不用重新调参,翻数据库里的快照即可。也可以拿两组风格做参数插值,创造出边界模糊的新质感。这个玩法还在快速发展,但方向已经明确。
最后再分享一点个人体会。做 artcraft 这段时间,最大的收获不是工具本身,而是我被逼着去重新观察那些平常熟视无睹的“手工痕迹”。当你开始分解纹理、测量色偏、调节噪声尺度之后,再看任何一块手工织物、一张老照片,眼光都会不一样。那种“观察—拆解—重建”的习惯,是这个项目留给我最珍贵的东西。