You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Perlin Noise算法网格单元间无法平滑过渡的问题求助

Troubleshooting Your Perlin Noise Smoothness Issue

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_map is 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_SIZE is 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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.28 06:39:37