Maya Python地形生成器

时间:2018-12-05 12:59:54

标签: python maya terrain

对于一个学校项目,我正在尝试编写地形生成器。我是python的入门者,但是已经研究了很多有关地形生成的知识。到目前为止,这是我的代码:

#imports
import maya.cmds as cmds
import random
#variables
landsub = 100 #number of subdivisions on the plane
landsize = 400 #size of the plane
maxheight = 100
land = cmds.polyPlane( sx=landsub, sy=landsub, w=landsize, h=landsize)
vtxCount = cmds.polyEvaluate(v=True)
print land
values = [random.triangular(0,1,0) for i in xrange(10)]

#code
for x in range(vtxCount):
    cmds.select(cl=True)
    cmds.select(land[0] + '.vtx[' + str(x) + ']')
    cmds.move(values[x] * maxheight, y=True, absolute=True) 

cmds.select(land[0])
#cmds.polySmooth(dv = 1)

我正在尝试(并努力)使maxheight和vtxCount变量相互依赖;最大高度上的顶点数量应该最小,然后递增,直到最大数量的顶点都达到最小高度为止。 目前,在我的代码中,大多数顶点似乎都在中间高度,很少在最小高度上。 我一直在寻找解决方案很长时间,但是我不熟悉在python中可以做什么,因此很难为我的代码找到正确的命令。

我真的希望有人能帮助我,并指出正确的方向!谢谢您阅读

2 个答案:

答案 0 :(得分:0)

您可以做这样的事情

#imports
import maya.cmds as cmds
import random
#variables
landsub = 100 #number of subdivisions on the plane
landsize = 400 #size of the plane
maxheight = 100
land = cmds.polyPlane( sx=landsub, sy=landsub, w=landsize, h=landsize)
vtxCount = range(cmds.polyEvaluate(v=True))
random.shuffle(vtxCount)
values = [random.triangular(0,1,0) for i in xrange(10)]
values_count = len(values)
SEED = 448
random.seed(SEED)
optimize_setter = []

#code
for x in vtxCount:
    mod = x % values_count
    # cmds.move(values[mod-1] * maxheight, land[0] + '.vtx[' + str(x) + ']',y=True, absolute=True)
    optimize_setter += [float(0),values[mod-1]*maxheight,float(0)]
cmds.setAttr('pPlane1.vtx[:]', *optimize_setter)
cmds.select(land[0])

如果您希望更多的值在0附近,则可以更改值以具有更多的.1值。我不确定您的问题是什么。

values = []
high_values = [random.triangular(0,1,0) for i in xrange(3)]
low_values = [random.uniform(0, .2), for i in xrange(7)]
values = low_values + high_values

编辑

#imports
import maya.cmds as cmds
import random
#variables
landsub = 100 #number of subdivisions on the plane
landsize = 400 #size of the plane
maxheight = 15
land = cmds.polyPlane( sx=landsub, sy=landsub, w=landsize, h=landsize)
vtxnb = cmds.polyEvaluate(v=True)
vtxCount = range(vtxnb)

values = []
high_values = [random.triangular(0,1,0) for i in xrange(vtxnb/10)]
low_values = [random.uniform(0, .3) for i in xrange(vtxnb/2)]
values = low_values + high_values
values_count = len(values)
SEED = 448
random.seed(SEED)
random.shuffle(vtxCount)
random.shuffle(values)
optimize_setter = []

#code
for x in vtxCount:
    mod = x % values_count
    # cmds.move(values[mod-1] * maxheight, land[0] + '.vtx[' + str(x) + ']',y=True, absolute=True)
    optimize_setter += [float(0),values[mod-1]*maxheight,float(0)]
cmds.setAttr('pPlane1.vtx[:]', *optimize_setter)
cmds.select(land[0])

答案 1 :(得分:0)

地形产生是一个有趣的问题,不幸的是,它通常需要大量的摆弄才能获得令人满意的结果。

@drweeny的答案很好地解决了问题的“玛雅”部分:您只需要批量设置polyPlane的.vtx属性。这是一种类似的方法,主要区别在于此函数采用一个类似于位图的位置数组:这是一个列表,其内容为列表,表示高度图中的行和列,即:[ [0,1], [2,3] ]表示

[0, 1]
[2, 3]

作为网格。将其转换为多平面非常简单:

def array_to_plane (array):
    # this expects that array is an list-of-lists
    # arranged as  [ [0,1,2], [3,4,5], ... etc  ]

    h = len(array)
    w = len(array[0])

    # this makes a plane with the same number of samples as your array
    plane, _  = cmds.polyPlane(width = 1, height = 1, sx = w -1 , sy=h-1)
    # the zeros are here just so the final argument list is a set of xyz
    # positions       
    setter = []
    for row in array:
        for column in row:
            setter.append (0.0)
            setter.append (column)
            setter.append (0.0)
    cmds.setAttr(plane + '.vtx[:]', *setter, type='double3')
    return plane

有趣且棘手的部分实际上是在填充该网格,以使地形看起来不太像随机噪声。技术很多,但是基础是:

随机化

您确实想将随机数应用于身高图。幸运的是,Python的random module对于具有不同分布的噪声有很多不错的选择。这是一个将随机数插入地图网格的简单函数:

def randomize(map, scale):
    rows = len(map)
    columns = len(map[0]

    # loop over the map and add random values
    for r in  range(rows): 
        for c in range(columns):
            map[r][c] += (random.gauss(0,1) * scale)

一个随机数网格看起来并不像地形-太混乱了。一种非常常见的技术是将随机化应用于不同的物理大小,以模拟大小特征的混合。最简单的方法是先从一张小地图开始,然后逐步将其放大-这将为您提供大小不同的功能组合。

这是获取高度图网格并将其内容在每个维度上加倍的一种廉价方法。如果您这样做几次,并在顶部添加一些随机化功能,那么您会得到从非常大到非常小的特征的混合。

def upsize(heightmap):
    output = []
    for row in heightmap:
        new_row = []
        for each_value in row:
            # first double the entries in the horizontal direction
            new_row.append(each_value)
            new_row.append(each_value)

        # then add two copies of the row to the output.
        # don't add it twice -- add a copy to keep
        # the data independent
        output.append(new_row)
        output.append(new_row[:])

    return output

[ [0,1], [2,3] ]这样的地图上运行会产生一个 [ [0,0,1,1], [0,0,1,1], [2,2,3,3], [2,2,3,3]]

过滤

您不希望高度图中的每个样本都独立于其邻居:这在自然界中是罕见的,而且看起来很糟糕。在诸如photoshop之类的程序中,您使用'kernel'生成模糊效果,该效果基本上只是像素及其相邻像素的加权平均值。这里有很多选择(这是一个为您的项目进行研究的好地方)。这是一个非常简单的模糊内核,它使用一个我们的地图网格并返回一个新的模糊内核。您会看到在添加了不同的像素权重之后,我们还使用线性插值来控制模糊的强度:

def blur(map):
    output = []
    for row in map:
        output.append(row[:])

    width = len(map[0])
    height = len(map)

    for row in range (1, height-1):
        for column in range (1, height-1):
            v = 0
            v += map [row-1][column - 1]
            v += map [row-1][column] * 2
            v += map [row-1][column + 1]
            v += map [row][column - 1] * 2
            v += map [row][column] * 4
            v += map [row][column + 1] * 2
            v += map [row+1][column - 1]
            v += map [row+1][column]  * 2
            v += map [row+1][column + 1]
            v = v / 16.0

            # lerp the final output based on amount; 0 = no blur, 1 = full blur)
            output[row][column] =  v

    return output

此步骤有很多选项,它们会影响最终输出的外观。您可以使用Photoshop's custom filter(实际上只是一个简单的内核编辑器)来轻松地对过滤器内核进行试验。

放在一起

使用随机化,缩放和过滤,您足以制作一个有趣的地形生成工具包。共同的思路是,您要对以列表形式的“像素”的“像素”的“位图”进行所有艰苦的工作,完成后将其交给maya制成最终对象。

这是将它们组合在一起的非常基本的方法。我鼓励您将其分解并以满足您的创意的方式重新组合。将这些片段编写为可以在高度图列表上使用的独立函数,可以轻松添加或调整行为,而不会破坏整个过程。

def fractalize(iterations, scale=1.0,  seed = None):

    # this lets you make it repeatable if you want
    if seed:
        random.seed(seed)

    # make a 2-2 starter map
    map = [ [0.0, 0.0], [0.0, 0.0] ] 

    randomize(map, scale)

    for n in range(1, iterations):
        map = upsize(map)
        map = blur(map)

        # this turns down the randomization
        # in each successive step... you could
        # use many formulas here for different effects
        randomize(map, scale / 2**n)

    return array_to_plane(map)

fractalize(7, 0.125, seed=1234)

要小心 重复次数:每次重复 quadruples 顶点数,因此将其设置得过高将使您的Maya沉没。值8产生具有255 x 255个顶点的平面;小心使用。

尝试将其设置为您自己的一些方法:  *您何时以及要多久添加一次模糊处理?  *如果使用其他模糊内核会怎样?  *如果将randomize(map, scale / 2**n)更改为其他公式会怎样?  *您可以对位图进行哪些操作以模拟水位?