Metaballs解析与运用(2D元球融合)

一.Metaball定义

Metaball(元球)技术是由Blinn于1982年开发一种适用于建立可变形表面的技术。此技术利用Metaball建立能量场,然后通过标量域的等势面来建立3D模型来表现软体或者隐式曲面。简单的说,就是在空间里布置一些Metaball,每个Metaball都有一个能量场,通常用势函数来表示。设空间里均布着无数个点。在其中某一点,它的能量为每个Metaball对它的势的叠加。然后在空间的所有点找出势能相同的点,就得到一个由这些点组成的曲面。

二.对Metaball的理解

metaball可以理解为3D空间的等值面。通常来说, 它是3D空间里定义的一个方法:输入为(x,y,z)的坐标,输出为一个float数。输出取决于阈值,阈值之上返回1,小于阈值返回0.这样就把3D空间分成了两部分,一部分是实心的一部分是空心的。当两部分连续的表面相遇时,我们把它叫做等值面。这个等值面会看起来和其他部分不一样,即我们需要处理的就是元球连接的位置

三.2D Metaball核心算法

空间中一点的势能计算公式,先只在场景中设两个元球,A和B,A的球心为(x1,y1,z1), 能量为E1 , B为球心为(x1,y2,z2) , 能量为E2 , 计算空间点(x',y',z')势能的势函数都为:

E'(x', y', z') = E² / (x-x')² + (y-y')² + (z-z')²

这样对于每个空间点,设坐标为(x',y',z'),则它的势能可以用以下公式表示:

E'(x', y', z') = E² / (x1-x')² + (y1-y')² + (z1-z')² + E² / (x2-x')² + (y2-y')² + (z2-z')²

计算示例:

场景中有20个元球,如何绘制元球融合效果

1.计算空间点的势能

如前面所说,某一点的势能为每个Metaball对它的势的叠加,那么我们可以在片元Shader中这样计算:

//根据20个元球 计算每个点的势能
float v = 0.0;
for ( int i = 0; i < 20; i++ ) {
    vec4 mb = u_metaballs[i];
    float dx = st.x - mb.x;  
    float dy = st.y - mb.y;
    float r = mb.z;           //半径
    v += r * r / (dx * dx + dy * dy); //按照上面给的公式叠加20个Metaball的势
}

u_metaballs是uniform变量,存放小球的位置和半径信息,由javascript传入。

注:这里因为是2D元球所以没有空间Z方向的坐标,即dz=0

2.根据阈值来绘制

vec4 color = vec4(1.0);
float rangeMax = 5.2;   //最高阈值
float rangeMin = 4.7;   //最低阈值
if (v > rangeMax) {
    color = vec4(0.0, 0.0, 0.0, 1.0);         //势能大于rangMax 则黑色
} else if (v > rangeMin) {
    color = vec4(0.0, 0.0, 0.0, smoothstep(1.0, 0.0, (rangeMax - v) / (rangeMax - rangeMin)));    //势能处于大小阈值之间,则设置平滑的透明度
} else {                                   //势能小于rangeMin 则透明
    color = vec4(1.0, 1.0, 1.0, 0.0);
}

这里smoothstep就实现了融合部分的处理,补充介绍一下GLSL内置函数smoothstep函数

genType smoothstep (genType edge0,genType edge1,genType x)

如果x <= edge0,返回0.0 ;如果x >= edge1 返回1.0;

如果edge0 < x < edge1,则执行0~1之间的平滑埃尔米特插值。

如果edge0 >= edge1,结果是未定义的。

下图将根据时间顺序比较 smoothstep 和 linstep 返回的值:

图片

四.让小球动起来

动态改变Metaball的坐标即可,这里直接将dx,dy计算改成:

float dx = st.x + cos(u_time + mb.w) * mb.x;  //u_time 和 w 属性变化来使小球运动
float dy = st.y + sin(u_time + mb.w) * mb.y;

那么mb的坐标会根据u_time一直发生变化,周期为2Π/mb.w,振幅为2

其中u_time由javascript传入,u_time一直在累加

五.扩展变换

由上面的一些公式

改变u_metaballs中的x,y可以改变metaball的初始位置

改变u_metaballs中的z可以改变metaball的半径,即元球的大小

改变u_metaballs中的w可以改变metaball位置变换的周期

我们还可以再将cos(u_time + mb.w) * mb.x 乘以一个系数,即可以改变变化的振幅

再通过gsap动作可以创建更多更平滑有趣的动作

例如:

//x,y位置范围变远 z增大即球半径增大 w值增大则周期变短
this.balls.forEach(ball => {
    TweenLite.killTweensOf(ball);
    TweenLite.to(ball, 3, {
        delay: Utils.random(0, 0.5),
        x: Utils.random(-0.5, 0.5),
        y: Utils.random(-0.5, 0.5),
        z: Utils.random(0.04, 0.3),
        w: ball.x + Utils.random(4, 15),
        ease: Power2.easeInOut, //'power2.inOut',
    });
});

 

  • 26
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值