教程:使用快照画布实现画笔、拖尾对象效果等

教程:使用快照画布实现画笔、拖尾对象效果等

gfx2-icon今天,我将向您展示如何使用快照来实现一些您可能在传统的基于 CPU 的计算机图形学中经常使用的技巧。

一个老派的技巧是能够直接触摸帧缓冲区上的像素,或者直接修改图像的像素。在 GPU 世界中,你不能这样做,因为在 CPU 和 GPU 之间传递内存非常昂贵。

快照的多功能性

快照来救援!

通常,您使用快照作为一次性操作来缓存单个渲染结果。它们的工作原理是将对象添加到快照的“group”属性,然后每次要将组的子对象渲染到纹理时调用“invalidate()”。这使您可以实现像这个 Mode7 演示 这样的酷炫效果。

我们最近添加了一些 新的画布功能,让您可以以新的和有趣的方式操作快照纹理。

这是一个视频,向您展示了一个渲染到快照上的拖尾画笔效果。快照下方是世界的背景图像

实现这种效果的一种方法是跟踪我们绘制的每个画笔图像,然后随着时间的推移淡化它。编写代码来实现这一点相当复杂和笨拙。

一种更容易的方法是在每次触摸事件时将画笔图像绘制到快照上。诀窍是在每次触摸之间绘制一个黑色的半透明矩形,从而使先前绘制的图像看起来逐渐消失。这是 CPU 时代的一种常见技术,但现在您可以在 GPU 上实现它了!

在 Corona 中,实现此目的的方法是使用快照的新画布功能,并结合对 Porter-Duff 混合模式的支持

顺便说一下,如果您删除每次触摸之间的黑色矩形,您还可以构建一个简单的画笔程序 (代码)

快照画布

新的 snapshot.canvas 允许您在快照纹理上进行绘制,而无需在每次失效之间清除。为了将这些对象渲染到快照中,您需要使用 “canvas” 参数进行失效,例如 snapshot:invalidate( "canvas" )

我们还添加了一个 snapshot.canvasMode 属性,让您可以控制在失效之间子对象会发生什么。通常,画布组会被清空,子对象会被添加到快照的主组中。这可以确保您的快照纹理不会丢失,这种情况有时会在您的应用被暂停时发生。如果您不希望保留您的编辑,您可以通过 "discard" 模式丢弃子对象。

Porter-Duff 混合模式

我们还添加了 Porter-Duff 混合模式。我们常用的普通混合模式对应于 “srcOver”,但还有很多其他模式,例如 "clear""xor""dstIn""src""dst" 等,让您可以实现很多惊人的效果。

在上面的示例中,我们可以使用普通的混合模式。但是,如果您这样做,快照的背景将变成黑色,您将无法看到快照后面的世界图像。关键是只淡化快照中已经不透明的部分。因此,在上面的示例中,我们使用 "dstIn",它将快照纹理与黑色矩形的 alpha 值相乘。

总结

正如您所见,快照是一个功能强大且用途广泛的工具。当然,快照还有一些明显的下一步发展方向(例如,将它们用作其他对象的纹理),因此我们已将其列入我们的路线图。我认为真正令人惊奇的是,这个示例只使用了几个功能,所以我们仅仅触及了其潜力的冰山一角!

walter
15 条评论
  • David Condolora
    发布于 06:52, 11 月 01 日

    对于琐事爱好者来说,“Porter-Duff 混合模式”名称中的 “Porter” 部分是指 Tom Porter,他曾在 80 年代在卢卡斯电影计算机部门工作,该部门最终成为了……皮克斯。时至今日他仍然在那里,是皮克斯高级领导团队的一员。

    这是他合著的论文的链接:http://graphics.pixar.com/library/indexAuthorPorter.html

  • Jacques
    发布于 18:33, 11 月 01 日

    我很好奇所有这些新的 API 函数需要多少处理能力。它们看起来很棒,但是如果你最终使用大量它们来获得一个看起来不错的实时结果,那么一旦你有其他事情要处理,它会不会因为延迟而扼杀应用程序?如果大多数这些新的 2.0 效果可以有效地用于不像绘画程序的游戏/应用程序中,那么希望任何尝试过的人能够分享一下经验。

  • Jen
    发布于 20:13, 11 月 01 日

    这真的很棒,开始掌握新的图形引擎了。这个教程特别给我带来了很大的帮助。谢谢!

  • Ed Johnson
    发布于 05:12, 11 月 03 日

    一篇关于高级图形技术和用法,在其他地方很少见的精彩文章!很喜欢阅读 David 建议的皮克斯链接上的 2 个参考资料。感谢并继续推进 Corona SDK 的前沿能力。

  • Andy
    发布于 13:10, 11 月 04 日

    这非常酷,但我注意到如果你在快照上有一个效果(滤镜),它只会在你使快照失效时应用,而不是在你使画布失效时应用……是否有可能将效果添加到画布?这样你就可以拥有很酷的迭代效果,比如高斯模糊或泛光。

    • Walter
      发布于 13:31, 11 月 04 日

      是的,您可以像在普通矩形上一样,在快照的填充上添加效果。尝试在声明函数监听器之前添加以下内容

      snapshot.fill.effect = “filter.sepia”

      如果您不想要快照范围的效果,您也可以在每个对象的基础上设置效果。

      • Andy
        发布于 19:16, 11 月 04 日

        没错,但只有当你调用时才会运行效果
        snapshot.invalidate()
        而不是当你调用时
        snapshot.invalidate(“canvas”)
        是这样吗?

        如果可以在不清除画布的情况下迭代运行画布上的效果,那将会非常酷,例如,当你使用矩形技巧淡化它们时,逐渐模糊效果……

        • Andy
          发布于 19:27, 11 月 04 日

          另外,:), 在您的示例代码中,快照淡化永远不会完全变为零不透明度,但似乎会停止在一个较小的不透明度值……这在黑色背景上没问题,但我正尝试将其用作游戏顶部的效果层,并且它会留下效果的重影。如果你认为这是错误而不是特性,我可以向您发送图像并提交报告 :-)

        • Walter
          发布于 15:24, 11 月 05 日

          我认为我们说的是不同的事情。

          如果您按照我展示的方式设置效果,您不需要调用 invalidate,因为该效果会作为对整个纹理的后处理步骤应用。

          如果您想将效果应用于添加到快照组的单个对象,只需在每个对象的基础上设置它们。在这种情况下,是的,您需要调用 invalidate 以确保您添加的对象在下一个渲染过程中被绘制到快照纹理中。

          • Andy
            发布于 16:27, 11 月 05 日

            我希望在每帧的快照上应用效果,而不清除先前效果的结果。导致复合效果……

            例如,我希望一些粒子开始时是清晰的图像,然后逐渐淡化并越来越模糊。

            第 1 帧
            我绘制一些粒子
            第 2 帧
            模糊快照 –
            *绘制一个 5% 透明度的矩形 * – 进行淡化
            绘制更多粒子
            第 3 帧
            模糊快照
            *绘制一个 5% 透明度的矩形 *
            绘制更多粒子

            第 1 帧的粒子现在被模糊和淡化了两次,效果是复合的。

            这有道理吗?

            在您建议的设置中,滤镜的结果不会被传递回快照,因此模糊不会是累加的。

          • Tom
            发布于 03:10, 12 月 05 日

            这个问题解决了吗?我看到了同样的问题。我正在使用该技术来渲染烟花轨迹,但它们永远不会完全消失。

  • Tony
    发布于 15:43, 11 月 04 日

    感谢您在这里发布这些东西,我期待着深入挖掘 2.0 的强大功能。在这个示例中,是否存在任何类型的内存累积?如果没有,为什么?再次感谢!

    • Walter
      发布于 15:22, 11 月 05 日

      绘制发生在单个纹理上,因此影响很小。在这个特定的示例中,我通过 “discard” 模式进一步减少了内存影响。通常(discard 关闭),显示对象会保存到组中。当 discard 打开时,这些对象会在每次渲染到纹理后被丢弃。

  • Alex
    发布于 15:53, 11 月 05 日

    我在尝试重现上面的代码时一直收到错误(运行今天的版本)。知道为什么吗?

    main.lua
    第 6 行

    尝试调用字段 ‘newSnapshot’ (一个 nil 值)

  • Rajat
    发布于 06:07, 12 月 03 日

    很棒的教程,它确实帮助我们构建了一个完整的绘画应用程序,但是 Android 上的快照对象似乎存在一个问题。一旦您在应用程序被暂停后返回,快照数据就会丢失(这在 iOS 上不会发生)。
    这个问题在您的演示中也存在。

    这里唯一的解决方案是监听系统事件(applicationSuspend,applicationResume)并在 “applicationSuspend” 上捕获 png 文件,并在 “applicationResume” 触发后将该 png 加载到快照中。但是,无论何时您尝试在 “applicationSuspend” 上保存快照或包含快照的组并返回应用程序,您都会返回一个空白的黑色屏幕。

    有人知道如何解决这个问题吗?