在SVG中创建铅笔效果

我的游戏Dragons Abound创建了SVG矢量图形格式的地图。矢量图形具有许多功能(例如,无损缩放),这对于地图十分方便。此外,矢量图形还可以用于创建清晰的线条,例如墨水轮廓:


另一方面,矢量图形不能很好地创建具有非重复小细节的纹理。在矢量图形中,每个渲染的元素均由其大小,形状,位置,颜色等的描述表示。为了呈现许多小的非重复细节,需要描述很多元素。例如,对于铅笔线


数以万计的不同元素将是必需的。实际上,图像中的每个灰点都将单独设置。其他元素,例如模糊的图像,甚至更成问题。

这是对矢量图形的相当严重的限制,因此已向SVG添加了技巧,使您可以更有效地重现其中的某些纹理效果。我将探索其中的一些SVG功能,以创建铅笔线条效果。当然,有许多更复杂的解决方案可以重新创建铅笔线条。有关该主题的所有科学文章都已撰写。但我只是希望创建一个相当简单的过滤器,以提供可接受的结果。

与往常一样,我更喜欢借用或修改由比我更有才能的人创建的过滤器,但是在这种情况下,可用于灵感的矢量铅笔过滤器严重短缺。 (听起来不祥的警告音乐:可能有原因。)因此,实际上,我只能依靠自己。

之前,写了关于《龙腾飞腾》的可能性创建“手写”行。基本上,然后,我考虑了避免数学上的直线并创建具有细微偏差的线条(看起来更接近人的手的线条)的可能性。在本文的开头,显示了一些山峰的示例。要创建墨水插图样式,这就足够了,但不适合创建铅笔线条,因为它缺少铅笔纹理。

如果您查看上方和下方显示的铅笔线:


您会注意到许多功能,它们将铅笔笔划与墨水线(或计算机绘制的线)区分开。最重要的区别是它们具有铅笔与纸相互作用的方式所产生的纹理。纸是颗粒状的,铅笔通常将石墨留在颗粒的较高部分,而较低的部分则保持白色。在粗糙的纸上,纹理更明显。拉制纸板非常精细,几乎不会产生纹理。其次,铅笔线的边缘比较模糊。同样,这在很大程度上是由于纸张和铅笔尖的不平整所致。这导致沿线的边缘保留了不同数量的石墨。

(当然,还有其他的效果。铅笔痕迹自身重叠,因此它在笔划的交叉点变得更暗。本身在压力变化的冲程,这可以改变沿着它的长度的行程的黑暗。在这篇文章中,我将主要集中在重建所述纸张纹理。)

对于我从准备简单的灰线开始:


我画了“手写”线,其粗细为4、2和1像素。不幸的是,SVG效果对于不同长度的线通常看起来不同,因此我想比较不同尺寸的效果。

SVG提供的添加纹理效果的主要功能称为滤镜。渲染矢量效果并更改其外观后,将应用滤镜。常规滤镜可以执行诸如更改对象颜色,为其添加噪音等操作。过滤器是一个使用复杂语法的相当令人困惑的主题,因此,我不会提供有关如何使用它们的完整教程,但是我将详细解释,以使我清楚自己做了什么。在文章的结尾,我将提供一个带有过滤器的Codepen链接,以便您可以自己尝试。

首先,我将尝试更改线条的边缘,使它们不光滑,但具有纸张纹理的不规则性。我将通过移动线条的像素来实现。执行此任务的过滤器元素称为“ feDisplacementMap”;它根据另一幅图像中的值移动每个像素。由于我们希望每个像素以随机但均匀的方式移动,因此我们需要将噪声传递给feDisplacementMap来控制移动。幸运的是,SVG还具有另一个称为“ feTurbulence”的过滤器元素,该过滤器专门设计用于产生噪声。因此,我们可以结合使用两个滤镜来粗化线条的边缘。

线的大小和粗糙度可以通过位移图和噪声生成的参数来控制。不幸的是,偏移量以绝对单位表示,而不是相对于行大小。我选择了参数,试图找到适合所有线宽的参数,但是随着偏移比例的增加,您会注意到问题:


现在,位移是如此之大,以至于它可以移动整条线,而不仅仅是改变其边缘。这种效果在细线上更加明显。在该示例中,宽度为4像素的线基本上看起来好像边缘是粗糙的,并且在厚度为2像素的线中已经很明显地出现了失真。也就是说,我需要选择一个不会在细线上产生扭曲的值。

在游戏中以标准增量缩放时,效果如下所示(在选择参数以改善效果之后):


在这种比例下,许多粗糙的边缘成为斑点和点。这不是一个非常不愉快的效果,并且有点让人联想到铅笔线。(但是,通常,SVG滤镜似乎在缩放方面存在问题-在许多情况下,当它们放大时,它们看起来不错,但是在移开时它们的尺寸调整算法很差。)

这是绘制山峰时的效果(左侧的铅笔效果):


并不是那么可怕,但是这条生产线上有一些看起来有些奇怪的尖锐的工件。当将效果应用于细间距较小的线条时,它们会重叠并合并:


我再说一遍,这并不是那么可怕,如果您只需要这样的效果,铅笔山上的阴影看起来与铅笔绘制的阴影非常相似。

该解决方案增加了铅笔笔划的轮廓的粗糙度,但不会改变笔划的均匀颜色。真实的铅笔笔触内部也存在纹理,因为石墨会在纸张上不均匀地留下斑点。

为了在笔触的内部添加纹理,我使用了SVG滤镜,然后将噪点与笔触结合在一起:


放大后,您可以看到每个笔触的内部现在都充满了伪石墨纹理。这是正常规模的样子:


很好,尤其是在粗线上。这是绘制山脉时的样子:


在这个规模上,一切都不是很好。请注意,此滤镜还可以减少线条的暗度。这是在线路上添加白噪声的自然结果。在一定程度上,可以通过增加噪声的对比度来消除此问题,从而使补偿线的某些部分变暗:


但是,由于线条的颜色已经很暗,因此大大破坏了“铅笔”的效果。因此,如果使用此选项,则需要选择参数以创建令人满意的平衡。

显然,两种解决方案都可以结合使用。随着增加,结果看起来不错:


从铅笔线条来看,我们希望既有粗糙的边缘又有内部纹理。在标准规模上,一切都不尽如人意:


由于出现了这种规模的尖锐文物。

改善此纹理的一个好技巧是在背景中添加纸张纹理:


现在,眼睛可以感觉到整个图像的纹理均匀。即使在非常隐蔽的水平上,这也有助于欺骗眼睛并确信线条的纹理是由与纸张的交互作用引起的。

这是在地图(具有纸张纹理)上使用此过滤器的示例:


总的来说,一切都还不错,但是仔细研究似乎到处都增加了噪音。随着200%的增加,伪影变得更加明显:


创建铅笔线条的粗糙边缘的另一种方法是用略有不同的偏差和降低的不透明度绘制线条数次。在线条的多个版本相交的线的中心,密度将接近原始线的密度。在外边缘(有时仅部分线条)处,不透明度会降低,并且边缘的清晰度会降低。

通常,要使用SVG过滤器实现这种解决方案,您需要一起使用feTurbulence和feDisplacementMap来创建该行的变形版本。但是,要多次执行此操作并最后合并所有行,则需要一组feBlend原语。例如,如果我们混合三个副本,则需要适当降低线条的不透明度。 (我不太确定如何具体计算相应的不透明度,但我认为它可以是线条的光度的立方根。)

这会产生效果(三个线条增加):


这种方法有两个缺点。滤光片具有固定的偏差,因此对窄线的影响更大,在某些点上可以看出单像素线已被完全分割。其次,它是一个相当复杂的过滤器,它会创建三个单独的偏差并将其合并;在诸如Dragons Abound卡之类的复杂图像上速度可能非常慢

这是标准比例的样子:


在我看来,这不太像铅笔轮廓,但是可以更好地消除以前解决方案中的尖锐瑕疵。

这种方法可以与上述内部纹理过滤器结合使用,并在铅笔线条内添加纹理以及纸张纹理:


这是在上山之后的样子:


与其他过滤器相比,此过滤器倾向于更强地分布线条,从而使它们更粗。有时,它会生成带有多条线的草图效果,还不错。

这是在地图上使用此过滤器的示例:


它比第一个滤镜更好地保留了原始的暗度,尽管我认为它看起来并不完全像铅笔,但效果却令人愉悦。增加200%:


放大后,此滤镜不会获得第一个滤镜的清晰伪像。相交的线条对细线的影响(例如,在森林中的图像)开始看起来很人为,但是较宽的线(山和河)仍然看起来不错。但是,在诸如河流的黑线内,内部噪声几乎完全消失了。

我将这两个过滤器都发布在了Codepen上,因此您可以自己尝试。第一个过滤器在这里,第二个过滤器这里我建议尝试使用它们并尝试对其进行改进,如果您得到的比我更好的东西,请告诉我!我想要一个很好的铅笔过滤器!

All Articles