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

C++生成类Perlin/Value噪声时插值异常,输出图像底部右侧损坏求助

问题描述

我正尝试生成类似Perlin噪声或Value噪声的效果。
我使用了stb官方仓库提供的stb_image_write库写入图像文件(我先存为HDR格式到本地,再通过GIMP转换为PNG格式便于上传展示)。
本代码需要C++20环境支持,因为我调用了std::lerp()函数。
当前我先生成了一个线性梯度作为测试用例,预期输出也应该是对应的线性梯度。
我知道完整生成目标噪声还有更多步骤,但目前就卡在了插值异常这个问题上。

问题代码

#include <cmath>

template <size_t size>
void noise(float seed[size][size], float output[size][size], size_t octave);

int main()
{
    // 生成测试梯度
    float seedNoise[64][64] = {};
    for ( size_t x = 0; x < 64; x++ )
    {
        for ( size_t y = 0; y < 64; y++ )
        {
            seedNoise[x][y] = ((((float)x) / 64.0f + ((float)y / 64.0f)) / 2.0f);
        }
    }

    float _map[64][64] = { 0 };
    noise<64>(seedNoise, _map, 4);
}

template <size_t size>
void noise(float seed[size][size], float output[size][size], size_t octave)
{
    size_t step = size / octave;
    // size_t step = (size - 1) / octave;

    for ( size_t x = 0; x <= size - step; x += step )
    {
        for ( size_t y = 0; y <= size - step; y += step )
        {
            // 从种子中提取块的四个角点值
            float a = seed[x][y];
            float b = seed[x + (step - 1)][y];
            float c = seed[x][y + (step - 1)];
            float d = seed[x + (step - 1)][y + (step - 1)];

            for ( size_t u = 0; u < step; u++ )
            {
                float uStep = ((float)u) / ((float)step);
                for ( size_t v = 0; v < step; v++ )
                {
                    float vStep = ((float)v) / ((float)step);

                    float x1 = std::lerp(a, b, uStep);  // 顶边插值
                    float x2 = std::lerp(c, d, uStep);  // 底边插值
                    float y1 = std::lerp(a, c, vStep);  // 左边插值
                    float y2 = std::lerp(b, d, vStep);  // 右边插值

                    float x3 = std::lerp(x1, x2, vStep);    // 上下插值结果再插值
                    float y3 = std::lerp(y1, y2, uStep);    // 左右插值结果再插值

                    float odat = (x3 + y3) / 2;     // 两次插值结果取平均

                    output[x + u][y + v] = odat;
                }
            }
        }
    }
}

效果说明

  • 源梯度图:预期输出结果应和该图效果相近
  • 实际输出图:生成图像的右侧和底部区域完全异常
  • 调整后新输出图:修复后的正常输出效果
问题原因及修复方案

1. 插值因子计算错误

当前uStep = u / step、vStep = v / step的计算逻辑,会导致块的最右、最下点插值结果仅能达到角点值的(step-1)/step,和预期的角点值不符,边缘区域就会出现明显断层。可将插值因子修改为uStep = (float)u / (step - 1.f),让u=step-1时插值因子刚好为1,插值结果等于角点值。

2. 双线性插值逻辑错误

双线性插值只需先对水平两条边做x方向插值得到x1、x2,再对x1、x2做y方向插值即可得到正确结果。额外计算垂直两条边的插值再取平均的操作属于冗余计算,会导致最终结果偏离正确值,直接删除这部分逻辑即可。

3. 采样步长逻辑问题

如果octave参数代表采样点的数量(如octave=4对应4*4个采样点),那么步长应该修改为step = (size - 1) / (octave - 1),确保最后一个采样点刚好落在图像的右下角边界,避免边缘区域未正确覆盖。

修复后核心代码片段

template <size_t size>
void noise(float seed[size][size], float output[size][size], size_t octave)
{
    size_t step = (size - 1) / (octave - 1);

    for ( size_t x = 0; x < size; x += step )
    {
        for ( size_t y = 0; y < size; y += step )
        {
            float a = seed[x][y];
            float b = seed[x + step][y];
            float c = seed[x][y + step];
            float d = seed[x + step][y + step];

            for ( size_t u = 0; u < step; u++ )
            {
                float uStep = (float)u / step;
                for ( size_t v = 0; v < step; v++ )
                {
                    float vStep = (float)v / step;
                    float x1 = std::lerp(a, b, uStep);
                    float x2 = std::lerp(c, d, uStep);
                    output[x + u][y + v] = std::lerp(x1, x2, vStep);
                }
            }
        }
    }
}

内容的提问来源于stack exchange,提问作者BrokenCode

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.30 22:27:03