Perlin Noise in Python: Noise Values Compacted, Need Help Forming Sinusoidal Pattern

134 views Asked by At

I'm currently working on a project involving Perlin noise generation in Python. I've implemented the Perlin noise generation using the noise library and generated different types of noise values (cave, land, hill, mountain) within a chunk using a combination of these noise types.

However, I'm facing an issue with the generated noise values. Instead of the noise values forming a smooth, sinusoidal pattern, they seem to be compacted and lacking the desired variation. As a result, the final noise representation doesn't display the expected terrain features I'm aiming for.

Here's a snippet of my code for the block_perlin_noise method:

import numpy as np
import noise
from typing import List, Dict

# ... unsolicited code

# Define a class to represent a Chunk
class Chunk:
    def __init__(self, geometry: List[int] = (16, 16, 16), position: List[int] = (0, 0, 0),
                 rotation: List[int] = (0, 0, 0)):
        # Validate the input parameters
        if not isinstance(geometry, tuple) or len(geometry) != 3 or all(value == 0 for value in geometry):
            raise TypeError("geometry must be a tuple of length 3 with non-zero values.")
        if not all(isinstance(value, int) for value in position) and all(isinstance(value, int) for value in rotation):
            raise TypeError("position must be a tuple of floats.")
        # Initialize instance variables
        self.geometry = geometry
        self.position = position
        self.rotation = rotation

    # Method to generate Perlin noise for different types of blocks in the chunk
    def block_perlin_noise(self) -> np.ndarray:
        chunk_width, chunk_height, chunk_depth = self.geometry

        # Generate noise values for different types of blocks in the chunk
        # and store them in separate 3D arrays
        cave_noise = np.zeros((chunk_width, chunk_height, chunk_depth), dtype=int)
        land_noise = np.zeros((chunk_width, chunk_height, chunk_depth), dtype=int)
        hill_noise = np.zeros((chunk_width, chunk_height, chunk_depth), dtype=int)
        mountain_noise = np.zeros((chunk_width, chunk_height, chunk_depth), dtype=int)
        # Initialize the chunk noise array
        chunk_noise = np.zeros((chunk_width, chunk_height, chunk_depth, 3), dtype=int)

        for x in range(chunk_width):
            for y in range(chunk_height):
                for z in range(chunk_depth):
                    # Generate Perlin noise for caves
                    cave_noise[x, y, z] = int(noise.pnoise3(
                        (x + self.position[0]) / chunk_width,
                        (y + self.position[1]) / chunk_height,
                        (z + self.position[2]) / chunk_depth,
                        octaves=2,
                        persistence=0.8,
                        lacunarity=1.2,
                        repeatx=chunk_width,
                        repeaty=chunk_height,
                        repeatz=chunk_depth,
                        base=1
                    ))

                    # Generate Perlin noise for land blocks
                    land_noise[x, y, z] = int(noise.pnoise3(
                        (x + self.position[0]) / chunk_width,
                        (y + self.position[1]) / chunk_height,
                        (z + self.position[2]) / chunk_depth,
                        octaves=4,
                        persistence=0.5,
                        lacunarity=1.0,
                        repeatx=chunk_width,
                        repeaty=chunk_height,
                        repeatz=chunk_depth,
                        base=0
                    ))

                    # Generate Perlin noise for hills
                    hill_noise[x, y, z] = int(noise.pnoise3(
                        (x + self.position[0]) / chunk_width,
                        (y + self.position[1]) / chunk_height,
                        (z + self.position[2]) / chunk_depth,
                        octaves=6,
                        persistence=0.5,
                        lacunarity=1.5,
                        repeatx=chunk_width,
                        repeaty=chunk_height,
                        repeatz=chunk_depth,
                        base=1
                    ))

                    # Generate Perlin noise for mountains
                    mountain_noise[x, y, z] = int(noise.pnoise3(
                        (x + self.position[0]) / chunk_width,
                        (y + self.position[1]) / chunk_height,
                        (z + self.position[2]) / chunk_depth,
                        octaves=8,
                        persistence=0.5,
                        lacunarity=2.0,
                        repeatx=chunk_width,
                        repeaty=chunk_height,
                        repeatz=chunk_depth,
                        base=1
                    ))

                    # Combine different noise types to get the final chunk noise
                    chunk_noise[x, y, z] = np.sum([
                        cave_noise[x, y, z],
                        land_noise[x, y, z],
                        hill_noise[x, y, z],
                        mountain_noise[x, y, z]
                    ])

        return chunk_noise
    # ... continuation of my previous code

    def render(self):
        figure = plt.figure()
        axes = figure.add_subplot(111, projection='3d')

        chunk_noise = self.block_perlin_noise()
        for x, y, z in np.ndindex(*self.geometry):
            block_x_value, block_y_value, block_z_value = chunk_noise[x, y, z]
            # You can adjust the size of the points based on the noise values
            axes.scatter(x + self.position[0], y + self.position[1], z + self.position[2], marker='o', s=5)

        axes.set_xlabel('X')
        axes.set_ylabel('Y')
        axes.set_zlabel('Z')
        plt.show()

enter image description here

I'm aiming for a Minecraft chunk terrain-like representation with gradual variations similar to a sin wave, but the noise values are not providing that smoothness and appear to be compressed into one place.

Could anyone help me understand why my noise values are compacted and not forming the expected pattern? Are there any adjustments I can make to the Perlin noise generation parameters or my approach to achieve a smoother, sinusoidal-like pattern in my terrain representation?

1

There are 1 answers

0
ApaxPhoenix On BEST ANSWER

I made sure, this code segment encapsulates the rendering process of the chunk noise in a 3D plot using Matplotlib. When you call the render() method on an instance of the Chunk class, it will generate the noise, create the plot, and display it using Matplotlib.

    def render(self):
        import matplotlib.pyplot as plt
        from mpl_toolkits.mplot3d import Axes3D

        # Generate chunk noise
        chunk_noise = self.get_block_mapping_grid()

        # Create a meshgrid for the 3D plot
        chunk_width, chunk_height, chunk_depth = self.geometry
        X, Y = np.meshgrid(np.arange(chunk_width), np.arange(chunk_height))

        # Create a 3D figure
        fig = plt.figure()

        ax = fig.add_subplot(111, projection='3d')  # Use Axes3D
        # Plot the chunk noise as a surface
        ax.plot_surface(X, Y, chunk_noise[:, :, 0, 0], cmap='terrain')

        # Set labels and title
        ax.set_xlabel('X')
        ax.set_ylabel('Y')
        ax.set_zlabel('Noise Value')
        ax.set_title('Chunk Noise')
        plt.show()