模拟瓶子中的液体
介绍
大家好,我叫Gil Damoiseaux,我是比利时那慕尔HEAJ(Haute Ecole Albert Jacquard)的讲师。我主要教授技术美术,特别是着色器开发,使用Substance Designer和Houdini等技术美术工具,并提供项目指导。我还与JangaFX合作,担任技术顾问和研发开发人员,我们目前正在开发Embergen,这是一款用于游戏和电影的实时体积流体模拟软件。最初,我在那慕尔当地大学攻读硕士学位期间学习计算机科学,通常是数据库和操作系统相关的问题。
然后我在一家名为Appeal的游戏公司工作,在那里我们发布了几款游戏,包括 1999 年的 Outcast,一款基于体素的冒险游戏。我还在NeuroTV工作了 8 年,为用于虚拟场景、化身动画或仅展示品牌的电视开发实时渲染引擎。我回到了 AMA 工作室的游戏开发,在那里我参与了 Fighter Within,这是一款 Kinect 格斗游戏,是 Xbox One 发布游戏的一部分。除此之外,我已经在开设一些课程,但我当时决定专注于这一点,并全职担任电子游戏讲师。六年前,我开始以自由职业者的身份工作,并与Grand Designer一起创建了一个实时程序行星生成器,我已经为此工作了 4 年,一直在添加功能并对其进行改进。一年半前我加入 JangaFX,因为我们在工具开发方面有着共同的目标,与他们合作很有意义,因为我自己正在构建类似的产品。
灵感
我一直对艺术创作和一般的创作过程着迷,所以当我有空闲时间时,我从我的艺术同事那里学习了不同的 DCC 工具,但一直以来,我一直回到编程。因此,很自然地,随着技术的发展,我将着色器开发作为我的主要兴趣中心。
我过去和现在仍然受到整个演示场景的启发,它在许多方面总是极具创意和令人印象深刻的技术。30 年前,当我用汇编语言编写代码时,我参与了 Archimedes 场景(第一台使用 ARM 处理器的台式计算机)……一般来说,生成艺术是一个巨大的灵感来源。当然,所有的游戏行业也给了我灵感,我会情不自禁地分析我在屏幕上看到的效果,有时会尝试重现它们。
Alyx VFX 实验:它是如何开始的
和我们中的许多人一样,我一看到 VFX 就对它印象深刻,想知道它是如何看起来如此自然和真实的。然后,我在 Twitter 和 Discord 上到处看到人们试图重现它,并接受了重现效果的挑战,作为一个有趣的周末项目。尝试重现一个好的效果总是一个很好的练习,并且总是一个机会进一步推动事物,学习新技术,并看看你可以走多远。我(看最后的效果完成先前与其他一些效果,比如在战神根动画这里,击穿这里),或在蜘蛛侠内部映射在PS4(可在我Github上)。我总是通过这种练习来学习新事物。
以前关于瓶中液体效应的工作
前段时间,我做了一个瓶子效果的液体,但要简单得多,没有任何物理,只是一个你可以改变的水平,但即使这样也有局限性。这个想法是渲染容器的内部并折叠液体表面的最高顶点。与用于切割物体的常用 alpha 测试技术相比,这具有很大的优势:您可以获得液体的实际表面,并具有适当的照明。为此,我简单地使用了使用噪声纹理的平面投影,该纹理与该噪声匹配的正常纹理。该着色器可以在Amplify Shader Editor的社区示例中找到。
但是这种技术也有一个很大的限制,你不能有一个底部较窄的容器,因为液体会从容器中流出。您可以在以下示例中看到,我没有将液体降低太多以避免出现问题。
您可以在此处查看原始效果,使用线框版本观看折叠过程:
2 中的1
去年,我还在学校举办的一次研讨会上制作了一些着色器,它们使用计算缓冲区来存储每个顶点的值。着色器(在本例中为顶点着色器)的主要问题是它们是无状态的,您无法从一帧中存储任何内容以将其返回到下一帧。因此,例如,在单个着色器中进行一些物理处理并不是很简单……除非您在读取和写入模式下使用计算缓冲区。这将允许您存储和调用每个顶点所需的尽可能多的数据。着色器也可能每帧被多次调用,因为 Z 预传递或阴影或任何其他原因,所以我不得不在计算缓冲区上实现双缓冲:一个处于读取模式,另一个处于写入模式。
这是在研讨会期间完成的着色器,在每个顶点上都有一个简单的钟摆效果,法线指向下方:
所以,最初的想法是将之前的两个实验混合成一个新的实验。液体表面基本上像弹簧网络一样反应。为了更好地控制它,我决定使用沿着一个轴移动的弹簧,上下一个,因为它是液体最明显的运动,它也将防止液体表面失控,在外面容器。它取代了我之前使用的噪波纹理并添加了物理效果,尽管它不是真正的液体模拟。我还期待我可以得到一些不错的东西,而不必在表面上传播弹簧运动,但只是保持局部运动……确实如此。因此,当对象平移或旋转时,我只需将弹簧的位置向上或向下平移,具体取决于它们相对于网格枢轴的位置:如果我将对象向右移动,左侧的顶点将升高,左侧的顶点将降低,以模拟液体在运动的相反方向上的积聚。在那之后,弹簧物理将启动,它会尝试回到其静止位置。弹簧离其静止位置越远,将其拉回的力就越强。除此之外,每帧都会衰减当前速度,以避免永远停止弹簧。这是表面物理的首次实现,导致以下结果:弹簧物理将启动,它会尝试回到其静止位置。弹簧离其静止位置越远,将其拉回的力就越强。除此之外,每帧都会衰减当前速度,以避免永远停止弹簧。这是表面物理的首次实现,导致以下结果:弹簧物理将启动,它会尝试回到其静止位置。弹簧离其静止位置越远,将其拉回的力就越大。除此之外,每帧都会衰减当前速度,以避免弹簧静止。这是表面物理的首次实现,导致以下结果:
到目前为止,我在计算缓冲区中存储: 1)顶点的前一个位置,从中我可以得到顶点的局部速度(平移和旋转);2) 弹簧的位置,在液面之上或之下;3)弹簧的速度以及它是向上还是向下移动。
还有一些问题,第一个是我受到容器形式的限制,防止液体像前面提到的那样挂出,因此在球形容器中进行了第一次测试。但是我通过将顶点投影到容器内部而不是简单地投影在平面上来部分地减少了这个问题。这不是一个完美的解决方案,但如果您明智地选择容器形状,这将是不可见的。
另一个重要的问题是,我当时无法在液体表面设置适当的法线,只能使用法线指向上方,因此缺乏良好的镜面反射。
由于我仍在尝试将所有这些保留在单个着色器中,我遇到了顶点不知道几何形状并且不知道它们的邻居的问题。除非您向他们提供信息,否则这是正确的:您可以在顶点、法线、切线或贴图坐标中存储大量静态信息。因此,我制作了一个小脚本,通过将其两个邻居的索引存储在未使用的纹理坐标通道中来改进网格。由于顶点的位置也存储在计算缓冲区中,因此任何顶点现在都能够获得两个邻居的位置,因此能够重新计算其法线。事实上,这是前一帧的正常情况,只是无法分辨。
为了改善液体表面的形状和运动,我根据顶点的世界位置用一些正弦波调制了注入的力。这立即增加了许多细节,并创造了一个更可信的液体表面。我当时还增加了网格密度以获取更多细节。
之后,添加了通常的屏幕空间折射,以获得一些半透明的液体。我还在折射过程中多次采样,以在半透明上产生一些粗糙度并避免纯粹的半透明液体。您可以在此处看到常规折射和多重采样折射。请注意,玻璃也将这种技术用于磨砂玻璃带。
2 中的1
气泡和泡沫
在发布第一个版本的液体后,我从 Twitter 社区得到了很棒的反馈,但我经常收到的评论是“你什么时候添加气泡?”。
为了制作气泡和泡沫,我需要跟踪液体的搅拌程度。要做的最简单的事情是添加一个存储在计算缓冲区中的变量,该变量将累积本地施加的所有外力,通过将前面提到的脉冲的绝对值添加到每一帧。这个变量也会减少每一帧,让我模拟泡沫和气泡的消散,我用它作为我的面具。
对于气泡的渲染部分,我计算了一个向上滚动的 3D 噪声,它根据蒙版使用一些平滑步骤来使气泡变大或变小。对于泡沫,在液体表面使用遮罩以在其顶部添加一些额外的位移,并为表面着色不同的颜色并修改局部半透明度。
这是气泡和泡沫的第一个版本:
由于最初的效果已经用更大的活气泡做了一些非常有说服力的事情,我必须改进我所拥有的东西,特别是当来自比利时这个啤酒之乡时。我在 Fusion 360 中模拟了一个啤酒瓶,并找到了一个很好的假品牌纹理来正确完成它。
这次我在顶点着色器上使用了另一个 3D 噪声来随机化泡沫的消散,这起初有点过于人为。
处理粘性液体
我想要更多这样的东西,所以在挖掘了可能性之后,我想到了更浓稠的液体,比如糖浆或血液。我禁用了泡沫,为了跟踪容器内部的粘性部分,我添加了另一个变量,保持液体的局部粘性。当顶点位于液面下方时,这只是设置为最大值,并且在液面上方时缓慢且随机地(使用与以前相同的 3D 噪声)逐渐消失。
但是渲染必须多做几次,因为液体的顶点已经用于显示表面。因此,为此,我必须进行 5 次传递:容器的后玻璃、液体粘性部分的后部、液体本身、粘性液体的前部和容器的前玻璃。粘性通道非常轻量级,只读取顶点着色器上的蒙版值,但无论如何它仍然是 2 个额外的通道。
这是该技术的早期版本:
着色器非常灵活,并具有一些参数,例如弹簧强度:值越高,表面返回原位的速度就越快。这里另一个有用的参数是弹簧的阻尼,它定义了前一帧的速度的剩余部分:阻尼越高,感觉液体越稠,因为移动需要更多时间,因为速度主要在帧之间丢失.
这是一种更稠密和粘稠的液体:
液体太粘稠可能是前面提到的投影校正的一个问题,因为您有更多的机会让液体从容器中流出。
理论:一瓶香槟
如果我要制作一瓶香槟,主要问题将是液体再次从容器中流出,或者您必须将瓶子盖得尽可能低。这就是我对啤酒瓶所做的,使用瓶子颈部的标签来隐藏它。
从瓶子里倒出液体会引发更多问题:第一个是瓶子容量的一半以下会强调液体的假物理,第二个是你需要一些更好的物理来处理倒出,至少像粒子或充其量是适当的液体模拟。Ryan Brucks最近在这个方向上做了一些非常酷的实验,但他的方法与我的完全不同。因此,这将是一个更具挑战性的设置,我可能会采用与此不同的技术来做到这一点。
理论:变色液体
如果您想在摇晃液体时改变液体的颜色,有一种简单的方法可以通过使用上面提到的搅拌变量来做到这一点。越激动,可见的二次色就越多。正如您在以下测试中看到的那样,它正在工作,但有点粗糙。
多种颜色的适当混合可能意味着一些颜色扩散,甚至充其量只是一些低清晰度的实时流体模拟。
在大多数情况下,简单的解决方案可能就足够了,就像在着色器开发中一样,在大多数情况下,让它看起来很复杂并且保持简单和快速是可取的。
后记
这种效果的挑战主要与使用和您的最终平台有关。就我而言,我没有对我正在做的事情施加任何限制,我只是想让它看起来令人信服和酷。如果您接受它带来的限制,则此技术在项目中当然可用。给你一个粗略的想法,当前的实现,包括所有的花里胡哨,在我的 2080RTX Ti 上花费大约 1.5 毫秒,使用 50K 三角形模型。这个实验并不是为了生产就绪,所以我没有花时间进行优化,但是有很多可以改进的地方。
我已经制作并改进了它,看看是否可以使用该特定技术来获得令人信服的东西,并且我还有许多其他技术想要尝试以使其成本更低或限制更少。总是有改进的空间,新的想法,给定技术的新用法。总会有富有创造力的人会做出更好看或更快速的实现,这就是总是驱使我尝试新事物并继续到处寻找灵感的原因。一旦您沉迷于创意编程或任何其他领域,您将永远不会停止要求更多并学习和尝试新事物以打开新的大门。