Skip to content

Instantly share code, notes, and snippets.

@celeron55
Created July 12, 2025 11:21
Show Gist options
  • Select an option

  • Save celeron55/3de43f6871fd150dd9e970ef231ae5c1 to your computer and use it in GitHub Desktop.

Select an option

Save celeron55/3de43f6871fd150dd9e970ef231ae5c1 to your computer and use it in GitHub Desktop.
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D # For 3D projection
# Perlin noise functions from pvigier's implementation
def generate_perlin_noise_2d(shape, res, seed=0):
np.random.seed(seed)
def f(t):
return 6 * t**5 - 15 * t**4 + 10 * t**3
delta = (res[0] / shape[0], res[1] / shape[1])
d = (shape[0] // res[0], shape[1] // res[1])
grid = np.mgrid[0:res[0]:delta[0], 0:res[1]:delta[1]].transpose(1, 2, 0) % 1
# Gradients
angles = 2 * np.pi * np.random.rand(res[0] + 1, res[1] + 1)
gradients = np.dstack((np.cos(angles), np.sin(angles)))
g00 = gradients[0:-1, 0:-1].repeat(d[0], 0).repeat(d[1], 1)
g10 = gradients[1:, 0:-1].repeat(d[0], 0).repeat(d[1], 1)
g01 = gradients[0:-1, 1:].repeat(d[0], 0).repeat(d[1], 1)
g11 = gradients[1:, 1:].repeat(d[0], 0).repeat(d[1], 1)
# Ramps
n00 = np.sum(grid * g00, 2)
n10 = np.sum(np.dstack((grid[:, :, 0] - 1, grid[:, :, 1])) * g10, 2)
n01 = np.sum(np.dstack((grid[:, :, 0], grid[:, :, 1] - 1)) * g01, 2)
n11 = np.sum(np.dstack((grid[:, :, 0] - 1, grid[:, :, 1] - 1)) * g11, 2)
# Interpolation
t = f(grid)
n0 = n00 * (1 - t[:, :, 0]) + t[:, :, 0] * n10
n1 = n01 * (1 - t[:, :, 0]) + t[:, :, 0] * n11
return np.sqrt(2) * ((1 - t[:, :, 1]) * n0 + t[:, :, 1] * n1)
def generate_fractal_noise_2d(shape, res, octaves=1, persistence=0.5, seed=0):
np.random.seed(seed)
noise = np.zeros(shape)
frequency = 1
amplitude = 1
for i in range(octaves):
noise += amplitude * generate_perlin_noise_2d(shape, (frequency * res[0], frequency * res[1]), seed + i)
frequency *= 2
amplitude *= persistence
return noise
# Generate grid (for plotting, x and y range from 0 to 1 for simplicity)
size = 512
xx, yy = np.meshgrid(np.linspace(0, 1, size), np.linspace(0, 1, size))
# Generate fractal noise; adjust base_res for number of base features (lower = larger features)
base_res = (4, 4) # Reverted for larger features
native = generate_fractal_noise_2d((size, size), base_res, octaves=6, persistence=0.5, seed=0)
# Normalize to [0, 1]
native = (native - native.min()) / (native.max() - native.min())
# Adjust native for ~50% water relative to blue threshold (0.25), preserving variation in lows
blue_th = 0.25
sea_level = np.percentile(native, 50)
adjusted_native = native.copy()
water_mask = native < sea_level
land_mask = ~water_mask
# Scale water to [0, blue_th] preserving variation
if np.any(water_mask):
water_min = native.min()
water_max = sea_level
adjusted_native[water_mask] = (native[water_mask] - water_min) / (water_max - water_min) * blue_th
# Scale land to [blue_th, 1]
if np.any(land_mask):
land_min = sea_level
land_max = native.max()
adjusted_native[land_mask] = (native[land_mask] - land_min) / (land_max - land_min) * (1.0 - blue_th) + blue_th
# Define interpolated table with flats for oceans and plains (11 points)
x_points = np.array([0.0, 0.15, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0])
y_points = np.array([0.0, 0.0, 0.05, 0.1, 0.2, 0.35, 0.35, 0.35, 0.5, 0.75, 1.0]) # Flats at 0 (oceans) and 0.35 (plains)
# Apply interpolation for modified
modified = np.interp(native, x_points, y_points)
# Create figure with 2 rows, 2 columns
fig = plt.figure(figsize=(12, 10))
# 3D plot for adjusted native
ax1 = fig.add_subplot(2, 2, 1, projection='3d')
ax1.plot_surface(xx, yy, adjusted_native, cmap='terrain', rstride=8, cstride=8, antialiased=True, shade=True, linewidth=0)
ax1.set_title('Adjusted Native (3D View)')
ax1.set_zlim(0, 1)
# Histogram for adjusted native
ax2 = fig.add_subplot(2, 2, 3)
ax2.hist(adjusted_native.flatten(), bins=20, color='blue', alpha=0.7)
ax2.set_title('Adjusted Native Height Distribution')
# 3D plot for modified
ax3 = fig.add_subplot(2, 2, 2, projection='3d')
ax3.plot_surface(xx, yy, modified, cmap='terrain', rstride=8, cstride=8, antialiased=True, shade=True, linewidth=0)
ax3.set_title('Modified (3D View)')
ax3.set_zlim(0, 1)
# Histogram for modified
ax4 = fig.add_subplot(2, 2, 4)
ax4.hist(modified.flatten(), bins=20, color='green', alpha=0.7)
ax4.set_title('Modified Height Distribution')
plt.tight_layout()
plt.savefig('terrain_comparison_3d.png') # Save the image
plt.show() # Display interactively
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment