细胞噪声(cellurlar_noise)

本文正在参加「金石计划」 

细胞噪声利用了圆形的距离场。把坐标到一个点的距离的关系表现出来就是一圈圈的圆,那么现在有多个点,我们每次都取距离当前坐标最近的那个点的距离,这个点就称之为特征点,这样就形成了网状。

单细胞

从科学(大胆猜想)的角度来看, 单个细胞没有强外力限制,各处受力均匀,结果就是一个圆嘛。之所以是圆,而不是方,应该是因为内部斗争,张力的结果。

言归正传,下面就是一个简单的圆形距离场效果。简单处理了一下uv,放大4倍是为了便于观察边界。 这个细胞的中心点是鼠标,可以看到距离中心越近,越红。 因为当前着色逻辑,是按距离的平方混合细胞色和背景色。

细胞噪声(cellurlar_noise)

细胞噪声(cellurlar_noise)

多细胞

上面的代码里已经预备了一个point, 二维向量数组。 现在用相同的逻辑,但是有多个点,我们就按就近原则,距离哪个细胞最近,就按哪个距离,也就是按最小距离

在上面的代码的基础上,加一个循环,遍历出最小的距离即可。 结果如下


    for(int i = 0; i <4 ; i++){
        delta  = st - point[i];
        dist3 = min(dist3, dot(delta, delta) ) ;
    }

细胞噪声(cellurlar_noise)

可以看到,那四个细胞挤在一起后的样子,左边这个是跟随鼠标点的细胞。仔细观察,可以发现,两个细胞的边界,到两个细胞的中心距离是一样的。 可以加上细胞中心方便观察。

float spot (vec2 st , vec2 center){ 
    vec2 d = st - center;
    return  step( dot(d,d),.001 ) ; 
} 
// 放到前面的代码的下面, 画家算法 后来居上
color = mix( color ,color2, spot(st, m_Pos));
        
    for(int i = 0; i <4 ; i++){
        color = mix( color,color2, spot(st, point[i]) ) ;
    }
    

细胞噪声(cellurlar_noise)

多彩细胞

前面所有的细胞都是一个颜色,现在把细胞的颜色和其中心关联起来。 如此一来,我们需要记下距离谁最近,而不仅仅是求最小距离了。 本来想直接把以坐标映射颜色,但是这样的话,鼠标跟随的那个点,靠近其他点的时候可能会看起来一模一样,不利于观察。所以,又来了一个颜色数组。 因为用了数组,所以直接记录下标即可。

细胞噪声(cellurlar_noise)

细胞噪声

终于进入主题部分,噪声。 之前所说的噪声是在随机的基础上,插值,以得到一个连续变化的效果。

其实上面已经是一个简单的细胞噪声了,去掉五颜六色。回到最初的单色,在每一个细胞内都是从0-1的某种(上面没求距离,都是距离的平方)均匀分布,这显然没有随机性。

它的随机性,主要体现在多个细胞下,这个距离的随机,单个细胞下这个距离,只和那一个点有关,多个细胞,就和多个点有关。 细胞噪声的连续性是天然的,就不用再插值了。

于是,我们就用随机来生成多个点。为了保证,这多个点不重合,每个点的随机范围都限制在一个方块里。 先看没有随机的,规规矩矩的网格。

这里循环了九个点,每个点都是当前1×1格子内的中心点。 其中有st加上.5的,是因为,这个循环是从-1开始到1, 而我之前把坐标处理到[-.5,.5];

vec2 nearPoint  = point[4] ;
    vec2 pos ;
    for(float x = -1.; x <2. ; x++){
         for(float y = -1.; y <2. ; y++){
            pos = vec2 (x,y) +.5 ;
            delta  = st - pos +.5;
        float d2=dot(delta, delta) ;
        if( dist3 > d2){ 
            dist3 = d2 ;
            nearPoint = pos ;
        } 

        dist3 = min(dist3, dot(delta, delta) ) ;
        }
    }
    color = mix(color, colors[0], max(0.,1.- sqrt(dist3)));
    color = mix( color ,color2, spot(st, m_Pos));
        
    for(float x = -1.; x <2. ; x++){
         for(float y = -1.; y <2. ; y++){
        color = mix( color,color2, spot(st+.5, vec2(x,y)+.5) ) ;
        }
    }
    

细胞噪声(cellurlar_noise)

整齐的完了之后,加上随机再看。 因为随机的结果就是[0,-1],所以直接加上去就行了,这个点还是牢牢的限制在小单元格里。
细胞噪声(cellurlar_noise)

最后, 让细胞动起来。在随机函数的参数里加上时间t即可。

//循环体内
            pos = vec2 (x,y) + random2d(vec2(x,y) ) ;
            pos = vec2(x,y) + .5 + .5*sin(t + pos);
            delta  = st - pos +.5;

无限细胞

前面已经实现了基础版的细胞噪声,但是如果我想增加细胞的数目至成百上千,难道要遍历这么多个点吗? 这样显然会增加性能负担, 我们可以试试纹理重复的手法。

还是老一套的放大坐标,然后取余。因为这里是九个格子,取模的话肯定没有负数了,所以稍微改了一下起始范围。

    st *= 10. ;
    st = mod(st,3.);

结果看上去不太好。

细胞噪声(cellurlar_noise)

所以这里,需要稍微改变一下思路。还是直接用fract函数, 这样就拿到了每个单位单元格的坐标, 然后计算这个坐标到它周围的九个点的最小距离, 这九个点位置已经加了随机。 但是,实际上我们这是伪随机,所以全部的特征点其实是固定了的。

这里最重要的一个思想就是, 距离当前片元最近的特征点,肯定是在九宫格的范围内的。 所以只需要遍历周围的九个点即可。

细胞噪声(cellurlar_noise)

    vec2 pos, grid;
    vec2 i_st = floor(st), f_st = fract(st);
    for(float x = -1.; x < 2.; x++) {
        for(float y = -1.; y < 2.; y++) {
            grid = vec2(x, y);
            pos = random2d(grid+ i_st);
            delta = grid + pos - f_st;
            float d2 = dot(delta, delta);
            if(dist3 > d2) {
                dist3 = d2;
                nearPoint = pos ;
            }

        }
    }

把坐标放大10倍,可以看到效果还不错。

细胞噪声(cellurlar_noise)

结束

最后,加点律动。 加偏移的时候,需要注意的是,不能加到随机函数内,因为它随机啊,不连续, 另外就是偏移之后,其值域要保持在[0,1]内。

pos = random2d(grid+ i_st )+ sin(t);
            pos = vec2( pos.x * sin(t) , pos.y* cos(t)) * .5 + .5;

理论上来说,细胞噪声扩宽到三维是完全没有问题的,问题只是开销多少而已。 所以,除了上文所说的九宫格,其实还有2X2的细胞噪声,只要偏移一下特征点分布即可。

原文链接:https://juejin.cn/post/7215233616405971003 作者:莫石

(0)
上一篇 2023年3月29日 下午4:22
下一篇 2023年3月29日 下午4:33

相关推荐

发表回复

登录后才能评论