3
\$\begingroup\$

I'm reading this tutorial and it's the first time I try something this new.

Here is my attempt:

import sys
import math
import pygame
import random

pygame.init()
windowwidth, windowheight = 900, 500
windowsurface = pygame.display.set_mode((windowwidth, windowheight), 0, 32)
clock = pygame.time.Clock()
fps = 30
MAX_INT = (1<<31) - 1
grey = (128, 128, 128)

def linear_interpolate(a, b, x):
    return a*(1-x) + b*x

def int_noise(x):
    # I'm pretty sure I can use random.uniform(-1, 1) as I need a value in the range (-1, 1)
    # I just saw this function on SO that is said to be faster that the build-in.
    x = int(x)
    x = ((x << 13) & MAX_INT) ^ x
    x = ( x * (x * x * 15731 + 789221) + 1376312589 ) & MAX_INT
    return 1.0 - x / 1073741824.0

def smooth_noise(x):
    return int_noise(x)/2 + int_noise(x-1)/4 + int_noise(x+1)/4

def noise_interpolate(x):
    int_x = int(x)
    fractional_x = x - int_x

    v1 = smooth_noise(int_x)
    v2 = smooth_noise(int_x+1)

    return linear_interpolate(v1, v2, fractional_x)

def perlin_noise(x):
    total = 0
    persistence = 1/4
    octaves = 10

    for i in range(octaves):
        frequency = pow(2, i)
        amplitude = pow(persistence, i)

    total = total + noise_interpolate(x*frequency) * amplitude

    return total

#---------------------------------------------------------------------------

def mainloop():
    scale = 50
    points = [(i, perlin_noise(0.6*i) * scale)
              for i in range(0, windowwidth, 10)]
    points.append((900, perlin_noise(0.5)))

    while True:
        clock.tick(fps)
        pygame.display.set_caption('fps: %.2f' % clock.get_fps())

        # handle events
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                pygame.quit()
                sys.exit()

        # update game state

        # draw
        for i in range(len(points) - 1):
            pygame.draw.line(windowsurface, grey,
                             (points[i][0], 350+points[i][1]),
                             (points[i+1][0], 350+points[i+1][1]), 1)
        pygame.display.update()

if __name__ == '__main__':
    mainloop()

I know the design is awful, but I'm more concerned if it is the correct way to write all this. From interpolating function, to the smoothing function, all the way to perlin.

I have the feeling that this Perlin noise function is completely wrong and very misleading, regardless the fact that I draw the wave onto the screen.

\$\endgroup\$

1 Answer 1

2
\$\begingroup\$

To begin with, you cannot use random.uniform() in your current design. The tutorial specifically emphasize that the noise function shall be seeded, that is, for the same argument it shall always return the same result.

Second, the tutorial suggests that each octave must have its own noise generator. I believe it is essential (again, the noise it seeded, so you'd get literally the same noise for each octave).

You can get away with random.uniform() by calculating noise tables (one per octave), and use them as noise functions. In fact, I'd recommend this approach.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.