在2D中以程序方式生成"blob"图形的好方法

Nek*_*ios 15 language-agnostic graphics procedural-generation noise

我想以计算速度快的方式创建一个"blob".这里的blob被定义为像素的集合,可以是任何形状,但都是连接的.例子:

.ooo....  
..oooo..  
....oo..  
.oooooo.
..o..o..  

...ooooooooooooooooooo...  
..........oooo.......oo..  
.....ooooooo..........o..  
.....oo..................  


......ooooooo....  
...ooooooooooo...  
..oooooooooooooo.  
..ooooooooooooooo  
..oooooooooooo...  
...ooooooo.......  
....oooooooo.....  
.....ooooo.......  
.......oo........  
Run Code Online (Sandbox Code Playgroud)

在哪里 是死空间,o是标记的像素.我只关心"二进制"生成 - 像素是ON还是OFF.因此,例如,这些看起来像一些想象中的番茄酱或虚构细菌或任何有机物质.

什么样的算法可以实现这一目标?我真的很茫然

小智 20

David Thonley的评论是正确的,但我会假设你想要一个具有'有机'形状和光滑边缘的斑点.为此你可以使用metaballs.Metaballs是一个在标量场上工作的幂函数.使用行进立方体算法可以有效地渲染标量字段.通过改变球的数量,它们的位置和半径可以形成不同的形状.

有关2D元球的介绍,请参阅此处:http://www.niksula.hut.fi/~hkankaan/Homepages/metaballs.html

这里是对行进立方体算法的介绍:http://local.wasp.uwa.edu.au/~pbourke/geometry/polygonise/

请注意,3D中交叉点的256种组合在2D中仅为16种组合.它很容易实现.

编辑:

我用GLSL着色器一起编写了一个快速示例.这是使用50个blob的结果,具有来自hkankaan主页的能量函数. 替代文字

这是实际的GLSL代码,虽然我评估了这个每片段.我没有使用行进立方体算法.你需要渲染一个全屏四边形才能工作(两个三角形).vec3均匀阵列只是通过的各个斑点的2D位置和半径glUniform3fv.

/* Trivial bare-bone vertex shader */
#version 150
in vec2 vertex;
void main()
{
    gl_Position = vec4(vertex.x, vertex.y, 0.0, 1.0);
}

/* Fragment shader */
#version 150
#define NUM_BALLS 50
out vec4 color_out;
uniform vec3 balls[NUM_BALLS]; //.xy is position .z is radius

bool energyField(in vec2 p, in float gooeyness, in float iso)
{
    float en = 0.0;
    bool result = false;
    for(int i=0; i<NUM_BALLS; ++i)
    {
        float radius = balls[i].z;
        float denom =  max(0.0001, pow(length(vec2(balls[i].xy - p)), gooeyness));
        en += (radius / denom);
    }
    if(en > iso)
        result = true;
    return result;
}
void main()
{
    bool outside;
    /* gl_FragCoord.xy is in screen space / fragment coordinates */
    outside = energyField(gl_FragCoord.xy, 1.0, 40.0);
    if(outside == true)
        color_out = vec4(1.0, 0.0, 0.0, 1.0);
    else
        discard;
}
Run Code Online (Sandbox Code Playgroud)

  • 如果您喜欢计算机图形和程序生成的数据,请随时访问我们的IRC/FreeNode.我在#algorithms,## opengl和## opengl3上 (2认同)

Evg*_*eev 13

这是一种方法,我们首先生成分段仿射马铃薯,然后通过插值对其进行平滑处理。插值思想基于采用 DFT,然后保留低频原样,在高频处填充零,并采用逆 DFT。

这是仅需要标准 Python 库的代码:

import cmath
from math import atan2
from random import random

def convexHull(pts):    #Graham's scan.
    xleftmost, yleftmost = min(pts)
    by_theta = [(atan2(x-xleftmost, y-yleftmost), x, y) for x, y in pts]
    by_theta.sort()
    as_complex = [complex(x, y) for _, x, y in by_theta]
    chull = as_complex[:2]
    for pt in as_complex[2:]:
        #Perp product.
        while ((pt - chull[-1]).conjugate() * (chull[-1] - chull[-2])).imag < 0:
            chull.pop()
        chull.append(pt)
    return [(pt.real, pt.imag) for pt in chull]


def dft(xs):
    pi = 3.14
    return [sum(x * cmath.exp(2j*pi*i*k/len(xs)) 
                for i, x in enumerate(xs))
            for k in range(len(xs))]

def interpolateSmoothly(xs, N):
    """For each point, add N points."""
    fs = dft(xs)
    half = (len(xs) + 1) // 2
    fs2 = fs[:half] + [0]*(len(fs)*N) + fs[half:]
    return [x.real / len(xs) for x in dft(fs2)[::-1]]

pts = convexHull([(random(), random()) for _ in range(10)])
xs, ys = [interpolateSmoothly(zs, 100) for zs in zip(*pts)]   #Unzip.
Run Code Online (Sandbox Code Playgroud)

这会生成如下内容(初始点和插值):

分段仿射马铃薯和平滑版本

这是另一个尝试:

pts = [(random() + 0.8) * cmath.exp(2j*pi*i/7) for i in range(7)]
pts = convexHull([(pt.real, pt.imag ) for pt in pts])
xs, ys = [interpolateSmoothly(zs, 30) for zs in zip(*pts)]
Run Code Online (Sandbox Code Playgroud)

例子

这些偶尔会有扭结和凹陷。这就是这个斑点家族的性质。

请注意,SciPy 具有凸包和 FFT,因此可以将上述函数替换为它们。