Perlin Noise算法网格单元间无法平滑过渡的问题求助
Hey there, let's break down why your Perlin Noise isn't showing that smooth, connected look between grid cells— I've wrestled with the exact same bugs when building my own Perlin implementations! Looking at your code, there are a couple of key issues throwing off the interpolation logic:
1. Incorrect Coordinate Normalization for Interpolation
The biggest problem is how you're calculating the normalized position for SmoothStep in your BilinearInterpolation function. Right now, you're passing the global screen position of the subcell and dividing by CELL_SIZE, which gives values way outside the 0-1 range that Perlin Noise's interpolation expects.
Instead, you need to calculate the local position of the subcell relative to its parent grid cell (not the entire screen). For example, if a subcell is at (50, 50) and its parent cell starts at (40,40), the local offset is (10,10)— divide that by CELL_SIZE (40) to get (0.25, 0.25), which is the correct 0-1 normalized value for interpolation.
2. Redundant Offset Vector Normalization
In your PerlinNoise function, you're normalizing the offset vector twice: once when calculating offset_vec = (subcell_pos - corner_pos) / CELL_SIZE, and then again implicitly by passing the wrong position to BilinearInterpolation. This double normalization skews the dot product values, breaking the smooth transition between cells.
3. Noise Value Mapping Is Slightly Off
Perlin Noise's output ranges roughly from -√2 (~-1.414) to √2 (~1.414). Your current mapping (noise_value + 1.5) /3 doesn't perfectly fit this range, which can lead to clipped values or inconsistent brightness that makes transitions look jarring.
Fixed Code Snippets
Let's adjust the problematic parts:
Updated BilinearInterpolation Function
We'll modify this to accept the local normalized position (0-1) instead of the global screen position:
def BilinearInterpolation(d1, d2, d3, d4, local_norm_pos): # local_norm_pos is already normalized to 0-1 relative to the grid cell return lerp( lerp(d1, d2, SmoothStep(local_norm_pos.x)), lerp(d3, d4, SmoothStep(local_norm_pos.x)), SmoothStep(local_norm_pos.y) )
Updated PerlinNoise Function
Fix the offset calculation and pass the correct local position to interpolation:
def PerlinNoise(grid_cells, grid_map): noise_values = [] CELL_SIZE = 40 # Make sure this matches your global CELL_SIZE constant for cell in grid_cells: cell_topleft = Vector2(cell.GetPos()) cell_noise_values = [] # Get corner positions and their gradients (cleaned up syntax) corners = [ (cell_topleft, grid_map[tuple(cell_topleft)]), (cell_topleft + Vector2(CELL_SIZE, 0), grid_map[tuple(cell_topleft + Vector2(CELL_SIZE, 0))]), (cell_topleft + Vector2(0, CELL_SIZE), grid_map[tuple(cell_topleft + Vector2(0, CELL_SIZE))]), (cell_topleft + Vector2(CELL_SIZE, CELL_SIZE), grid_map[tuple(cell_topleft + Vector2(CELL_SIZE, CELL_SIZE))]) ] for subcell in cell.GetSubCells(): dot_values = [] subcell_pos = Vector2(subcell.GetPos()) # Calculate LOCAL offset relative to the cell's top-left corner local_offset = subcell_pos - cell_topleft # Normalize local offset to 0-1 range for the cell local_norm_pos = local_offset / CELL_SIZE for corner_pos, gradient_vec in corners: # Offset from the corner to the subcell (normalized to 0-1) offset_vec = local_norm_pos - (corner_pos - cell_topleft)/CELL_SIZE dot_values.append(offset_vec.dot(gradient_vec)) # Pass the local normalized position to interpolation noise_value = BilinearInterpolation(dot_values[0], dot_values[1], dot_values[2], dot_values[3], local_norm_pos) # Correct mapping to 0-1 using Perlin's actual range noise_value = (noise_value + 1.4142) / (2 * 1.4142) # Clamp to ensure no values go out of bounds noise_value = max(0.0, min(1.0, noise_value)) cell_noise_values.append(noise_value) noise_values.append(cell_noise_values) return noise_values
Quick Checks to Verify
- Make sure your
grid_mapis correctly storing gradients for every grid corner (including the bottom-right corners of the last cells in your grid— it's easy to miss those!). - Confirm that
CELL_SIZEis a consistent constant across all functions (hardcoding it here for clarity, but using a global constant is better).
After making these changes, you should see smooth, gradual transitions between grid cells instead of random jumps— that's the classic Perlin Noise look!
内容的提问来源于stack exchange,提问作者wert01

