Как сделать Perlin Noise / цветовой градиент в Python похожим на шум в учебнике по C ++? - PullRequest
0 голосов
/ 22 января 2020

Я сейчас пытаюсь использовать шум для генерации случайных фантазийных карт в Python. Я нашел учебник онлайн, который действительно помог мне. К сожалению, это на C ++, поэтому мне пришлось поиграть с моим кодом, чтобы получить его как можно ближе. Очевидно, невозможно заставить все быть один на один. Например, он использует случайный шум, а я использую perlin. Я все еще хочу попробовать.

Хотя я в целом доволен тем, как это получается, меня беспокоит серьезная проблема.

В программах, как моей, так и учебной, число от 0 до 1 (в моем случае числа становятся меньше от центра оси y, а затем меняются на 0 и 1) определяет цвет каждого элемента градиента. На картинке урока красный является основным цветом в центре, но остальные цвета более или менее равны друг другу. Мой близок к этому, но некоторые цвета определенно больше, с темно-синим (все цифры менее 0,05) просто маленькая полоска. Я попытался изменить размер градиента, но это заставило темно-синий обогнать все остальные цвета. Я в тупике. Является ли проблема в том, что градиент урока изначально переходит от красного в центре к синему, а у моего - от белого к черному? Это размер моего градиента? Должен ли я просто изменить числа, которые я использовал в учебнике?

Тепловая карта градиента учебника

Моя тепловая карта градиента

Мой код

import numpy as np
import math
import noise
from PIL import Image

shape = (500,500)
width = shape[0]
height = shape[1]
scale       = 100 # Number that determines at what distance to view the noisemap
octaves     = 6 # the number of levels of detail you want you perlin noise to have
persistence = 0.5 # number that determines how much detail is added or removed at each octave (adjusts frequency)
lacunarity  = 2.0 # number that determines how much each octave contributes to the overall shape (adjusts amplitude)

def add_color(arr):
    color_world = np.zeros(arr.shape+(3,))
    for x in range(width):
        for y in range(height):
            if arr[x][y] < .20:
                color_world[x][y] = [70,130,180]
            elif arr[x][y] < .40:
                color_world[x][y] = [100,149,237]
            elif arr[x][y] < .50:
                color_world[x][y] = [154,205,50]
            elif arr[x][y] < .70:
                color_world[x][y] = [0,128,0]
            elif arr[x][y] < .80:
                color_world[x][y] = [192,192,192]
            elif arr[x][y] < 1.0:
                color_world[x][y] = [255,255,255]
    return color_world

world = np.zeros(shape)
for x in range(width):
    for y in range(height):
        world[x][y] = noise.pnoise2(x/scale, 
                                    y/scale, 
                                    octaves     = octaves, 
                                    persistence = persistence, 
                                    lacunarity  = lacunarity, 
                                    repeatx     = 500, 
                                    repeaty     = 500, 
                                    base        = 0)
heat_noise = np.zeros(shape)
for x in range(width):
    for y in range(height):
        heat_noise[x][y] = noise.pnoise2(x/scale, 
                                    y/scale, 
                                    octaves     = 4, 
                                    persistence = persistence, 
                                    lacunarity  = lacunarity, 
                                    repeatx     = 500, 
                                    repeaty     = 500, 
                                    base        = 0)
# get world between 0 and 1         
max_grad = np.max(world)
min_grad = np.min(world)
world = (world-min_grad)/(max_grad-min_grad)

# get heat_noise between 0 and 1         
max_grad = np.max(heat_noise)
min_grad = np.min(heat_noise)
heat_noise = (heat_noise-min_grad)/(max_grad-min_grad)


# Show noise
##im = Image.fromarray(np.uint8(world*255),"L")
##im.show()

# make world height colorful
color_world = add_color(world)

# Create colored world height map image to view
im = Image.fromarray(color_world.astype("uint8"), "RGB")
im.show()

# create gradient starting from center going up and down
center_y = width/2
heat_grad = np.zeros_like(world)
for y in range(world.shape[0]):
    for x in range(world.shape[1]):
        disty = abs(y - center_y)
        dist =(disty*disty)
        heat_grad[y][x] = dist

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

# shrink gradient down
##for x in range(width):
##    for y in range(height):
##        if heat_grad[x][y] > 0:
##            heat_grad[x][y] *= 20

# Multiply world height by gradient
for x in range(width):
    for y in range(height):
        heat_grad[x][y] = (heat_grad[x][y] * heat_noise[x][y])

# get gradient between 0 and 1
max_grad = np.max(heat_grad)
min_grad = np.min(heat_grad)
heat_grad = (heat_grad - min_grad)/(max_grad - min_grad)

# show gradient in image
im = Image.fromarray(np.uint8(heat_grad*255),"L")
im.show()

# add color to heat_grad
for x in range(width):
    for y in range(height):
        if heat_grad[x][y] < 0.05:
            color_world[x][y] = [0,0,255] 
        elif heat_grad[x][y] < 0.20:
            color_world[x][y] = [100,100,255] 
        elif heat_grad[x][y] < 0.40:
            color_world[x][y] = [0,255,0] 
        elif heat_grad[x][y] < 0.60:
            color_world[x][y] = [255,246,0]
        elif heat_grad[x][y] < 0.80:
            color_world[x][y] = [255,140,0]
        else:
            color_world[x][y] = [255,0,0]

#create heat map based on noise
im = Image.fromarray(color_world.astype("uint8"),"RGB")
im.save("./imagetest.png")
im.show()

...