首页 > 解决方案 > python perlin 噪声程序图添加道路/河流

问题描述

您好,我创建了用于生成程序地图的程序(大陆、岛屿、湖泊......)

使用 perlin 噪声,我的程序生成 2 个映射,第一个映射 0 到 512 in x abs,第二个映射 512 到 1024 in x abs

用柏林噪音我生成有海滩、森林、山脉和湖泊的岛屿

现在我想添加道路和河流,但我不知道如何用 perlin 做到这一点,这可能吗?

我的代码:

import random
import noise
import numpy as np
from scipy.misc import toimage
import math

class Color:

    # 0 -> 255

    r = 0.0
    g = 0.0
    b = 0.0
    a = 1.0

    def __init__(self, r=0.0, g=0.0, b=0.0):
        self.r = r
        self.g = g
        self.b = b
        self.a = 1

    def GetTuple(self):
        return int(self.r), int(self.g), int(self.b)

    def SetColor(self, r, g, b):
        self.r = r
        self.g = g
        self.b = b

    def Copy(self, color):
        self.r = color.r
        self.g = color.g
        self.b = color.b

    def SetWhite(self):
        self.SetColor(1, 1, 1)

    def SetBlack(self):
        self.SetColor(0, 0, 0)

    def SetColorFromGrayscale(self, f=0.0):
        self.SetColor(f, f, f)


class GenerateMap:

    def __init__(self, size=(50, 50), color_range=10, color_perlin_scale=0.025, scale=350, octaves=6, persistance=0.6,
                 lacunarity=2.0, x_starting_pos=0, y_starting_pos=0):
        self.scale = scale
        self.octaves = octaves
        self.persistance = persistance
        self.lacunarity = lacunarity

        self.x_starting_pos=x_starting_pos
        self.y_starting_pos=y_starting_pos

        self.mapSize = size  # size in pixels
        self.mapCenter = (self.mapSize[0] / 2, self.mapSize[1] / 2)

        self.heightMap = np.zeros(self.mapSize)
        # self.colorMap = [[Color() for j in range(self.mapSize)] for i in range(self.mapSize)]

        self.randomColorRange = color_range
        self.colorPerlinScale = color_perlin_scale

        self.lightblue = [0, 191, 255]
        self.blue = [65, 105, 225]
        self.darkblue = [0, 0, 139]
        self.green = [34, 139, 34]
        self.darkgreen = [0, 100, 0]
        self.sandy = [210, 180, 140]
        self.beach = [238, 214, 175]
        self.snow = [255, 250, 250]
        self.mountain = [139, 137, 137]

        self.threshold = -0.01

    def return_initial_blank_map(self):
        return self.heightMap

    def get_map_corners(self):
        nw = self.heightMap[0][0]
        ne = self.heightMap[0][len(self.heightMap[0])-1]
        sw = self.heightMap[len(self.heightMap)-1][0]
        se = self.heightMap[len(self.heightMap)-1][len(self.heightMap[0])-1]
        return nw, ne, sw, se

    def get_map_start_position(self, start_position):
        pass

    def generate_map(self, map_type=""):
        random_nr = random.randint(0, self.mapSize[0])
        random_nr = 3
        for i in range(self.mapSize[0]):
            for j in range(self.mapSize[1]):

                new_i=i+self.y_starting_pos
                new_j=j+self.x_starting_pos


                self.heightMap[i][j] = noise.pnoise3(new_i / self.scale, new_j / self.scale, random_nr, octaves=self.octaves,
                                                     persistence=self.persistance, lacunarity=self.lacunarity,
                                                     repeatx=10000000, repeaty=10000000, base=0)
        print("monochrome map created")
        if map_type is "island":
            gradient = self.create_circular_gradient(self.heightMap)
            color_map = self.add_color(gradient)
        else:
            color_map = self.add_color(self.heightMap)
        return color_map

    def add_color(self, world):
        color_world = np.zeros(world.shape + (3,))
        # print(color_world)
        for i in range(self.mapSize[0]):
            for j in range(self.mapSize[1]):
                if world[i][j] < self.threshold + 0.02:
                    color_world[i][j] = self.darkblue
                elif world[i][j] < self.threshold + 0.03:
                    color_world[i][j] = self.blue
                elif world[i][j] < self.threshold + 0.058:
                    color_world[i][j] = self.sandy
                elif world[i][j] < self.threshold + 0.1:
                    color_world[i][j] = self.beach
                elif world[i][j] < self.threshold + 0.25:
                    color_world[i][j] = self.green
                elif world[i][j] < self.threshold + 0.6:
                    color_world[i][j] = self.darkgreen
                elif world[i][j] < self.threshold + 0.7:
                    color_world[i][j] = self.mountain
                elif world[i][j] < self.threshold + 1.0:
                    color_world[i][j] = self.snow
        print("color map created")
        return color_world

    def create_circular_gradient(self, world):
        center_x, center_y = self.mapSize[1] // 2, self.mapSize[0] // 2
        circle_grad = np.zeros_like(world)

        for y in range(world.shape[0]):
            for x in range(world.shape[1]):
                distx = abs(x - center_x)
                disty = abs(y - center_y)
                dist = math.sqrt(distx * distx + disty * disty)
                circle_grad[y][x] = dist

        # get it between -1 and 1
        max_grad = np.max(circle_grad)
        circle_grad = circle_grad / max_grad
        circle_grad -= 0.5
        circle_grad *= 2.0
        circle_grad = -circle_grad

        # shrink gradient
        for y in range(world.shape[0]):
            for x in range(world.shape[1]):
                if circle_grad[y][x] > 0:
                    circle_grad[y][x] *= 20

        # get it between 0 and 1
        max_grad = np.max(circle_grad)
        circle_grad = circle_grad / max_grad
        grad_world = self.apply_gradient_noise(world, circle_grad)
        return grad_world

    def apply_gradient_noise(self, world, c_grad):
        world_noise = np.zeros_like(world)

        for i in range(self.mapSize[0]):
            for j in range(self.mapSize[1]):
                world_noise[i][j] = (world[i][j] * c_grad[i][j])
                if world_noise[i][j] > 0:
                    world_noise[i][j] *= 20

        # get it between 0 and 1
        max_grad = np.max(world_noise)
        world_noise = world_noise / max_grad
        return world_noise

paperColor = Color(212, 161, 104)
waterColor = Color(0, 20, 28)

map_data = GenerateMap((512, 512), x_starting_pos=0, y_starting_pos=0)
print("Generator initiallized")
mono_map = map_data.generate_map("island")
print("map generated")
toimage(mono_map).show()
print("map displayed")

map_data = GenerateMap((512, 512), x_starting_pos=512, y_starting_pos=0)
print("Generator initiallized")
mono_map = map_data.generate_map("island")
print("map generated")
toimage(mono_map).show()
print("map displayed")

和输出: 输出 2 张图片

我想要这样: 世界地图与程序道路和河流

标签: pythonnumpydictionaryscipyprocedural

解决方案


看起来道路生成主要是使用 groth 算法完成的。为此,您不需要 perlin 噪声,而只需一个可以生成随机段的随机生成器。然后,您可以添加您喜欢的约束(限制与实际道路的角度,检查您不是在水上建造,等等)。

https://martindevans.me/game-development/2015/12/11/Procedural-Generation-For-Dummies-Roads/

如果您想使用 Perlin 噪声,您可以尝试计算与噪声值相关的高度图的水平集。但结果可能看起来不太好,我不会推荐这种方法。


推荐阅读