Language agnostic 一个很好的程序化生成;blob";二维图形
我希望以计算快速的方式创建一个“blob”。blob在这里被定义为像素的集合,这些像素可以是任何形状,但都是相互连接的。示例:Language agnostic 一个很好的程序化生成;blob";二维图形,language-agnostic,graphics,noise,procedural-generation,Language Agnostic,Graphics,Noise,Procedural Generation,我希望以计算快速的方式创建一个“blob”。blob在这里被定义为像素的集合,这些像素可以是任何形状,但都是相互连接的。示例: .ooo.... ..oooo.. ....oo.. .oooooo. ..o..o.. ...ooooooooooooooooooo... ..........oooo.......oo.. .....ooooooo..........o.. .....oo.................. ......ooooooo....
.ooo....
..oooo..
....oo..
.oooooo.
..o..o..
...ooooooooooooooooooo...
..........oooo.......oo..
.....ooooooo..........o..
.....oo..................
......ooooooo....
...ooooooooooo...
..oooooooooooooo.
..ooooooooooooooo
..oooooooooooo...
...ooooooo.......
....oooooooo.....
.....ooooo.......
.......oo........
在哪里。是死区,o是标记的像素。我只关心“二进制”生成——一个像素要么开要么关。例如,这些看起来像是一团想象中的番茄酱或虚构的细菌或任何有机物质
什么样的算法可以实现这一点?我真是不知所措你也许可以设计一些算法来实现这一点,这些算法是一系列随机迷宫生成算法的小变种。我会根据这个方法推荐一个 联合查找的基本思想是,给定一组被划分为不相交(非重叠)子集的项,快速识别特定项属于哪个分区。“并集”是将两个不相交的集合组合在一起形成一个更大的集合,“查找”是确定特定成员属于哪个分区。其思想是,集合的每个分区都可以由集合的特定成员标识,因此可以形成树结构,其中指针从一个成员指向另一个成员,指向根。通过首先查找每个分区的根,然后修改一个根的(以前为空)指针以指向另一个,可以合并两个分区(每个分区都有一个任意成员) 你可以把你的问题表述为一个不相交的并集问题。最初,每个单独的单元都是它自己的一个分区。您想要的是合并分区,直到获得连接单元的少量分区(不一定是两个)。然后,您只需选择一个(可能是最大的)分区并绘制它 对于每个单元格,您需要一个用于联合的指针(最初为null)。您可能需要一个位向量来充当一组相邻单元。最初,每个单元将有一组四个(或八个)相邻单元 对于每个迭代,您随机选择一个单元格,然后按照指针链查找其根。在根的细节中,你可以找到它的邻居。从中选择一个随机成员,然后找到该成员的根,以标识相邻区域。执行并集(将一个根指向另一个根等)以合并两个区域。重复以上步骤,直到你对其中一个区域满意为止 合并分区时,新根的新邻居集将是前两个根的邻居集的集对称差(异或) 在每个根元素中增加分区(例如大小)时,可能需要维护其他数据。你可以用它来选择一个特定的工会,并帮助决定何时停止。分区中单元散射的某些度量可能是相关的-例如,小偏差或标准偏差(相对于大单元计数)可能表示密集的大致圆形斑点 完成后,只需扫描所有单元格,以测试每个单元格是否都是所选分区的一部分,从而构建单独的位图
在这种方法中,当您在迭代开始时随机选择一个单元时,会强烈倾向于选择较大的分区。当您选择一个相邻分区时,也会倾向于选择一个更大的相邻分区。这意味着你很快就会得到一个明显占主导地位的斑点。David Thonley的评论是正确的,但我假设你想要一个“有机”形状和光滑边缘的斑点。为此,您可以使用元球。Metaballs是一个在标量字段上工作的幂函数。使用marching cubes算法可以高效地渲染标量场。通过改变球的数量、位置和半径,可以形成不同的形状 有关二维元球的介绍,请参见此处: 下面介绍marching cubes算法: 请注意,3D中交叉点的256个组合在2D中仅为16个组合。它很容易实现 编辑: 我用GLSL着色器拼凑了一个快速示例。以下是使用50个水滴的结果,能量函数来自hkankaan的主页。 这是实际的GLSL代码,尽管我对每个片段都进行了计算。我没有使用marching cubes算法。您需要渲染一个全屏四边形,它才能工作(两个三角形)。vec3均匀阵列只是通过
glUniform3fv
传递的单个水滴的2D位置和半径
/* 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;
}
/*普通裸骨顶点着色器*/
#版本150
在vec2顶点;
void main()
{
gl_位置=vec4(顶点x,顶点y,0.0,1.0);
}
/*片段着色器*/
#版本150
#定义球数50
输出vec4颜色输出;
均匀vec3球[NUM_球];/。xy是位置,z是半径
布尔能量场(在vec2 p中,在浮动粘性中,在浮动iso中)
{
浮动en=0.0;
布尔结果=假;
对于(int i=0;i iso)
结果=真;
返回结果;
}
void main()
{
在外面;
/*gl_FragCoord.xy位于屏幕空间/碎片坐标中*/
外部=能量场(gl_FragCoord.xy,1.0,40.0);
如果(外部==真)
color_out=vec4(1.0,0.0,0.0,1.0);
其他的
丢弃;
}
这里有一种方法,我们首先生成一个分段仿射马铃薯,然后通过插值使其平滑。插值思想的基础是采用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.
它们偶尔会有扭结和凹陷。这就是这个水滴家族的本质
请注意,SciPy具有凸包和FFT,因此可以用它们替换上述函数。对blob有什么约束?一个产生一个像素的程序会根据您的规格创建一个blob,而且效率很高。如果你不说什么
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)]