对于一个学校项目,我正在尝试编写地形生成器。我是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中可以做什么,因此很难为我的代码找到正确的命令。
我真的希望有人能帮助我,并指出正确的方向!谢谢您阅读
答案 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)
更改为其他公式会怎样?
*您可以对位图进行哪些操作以模拟水位?