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

离散颜色高度图颜色插值/平滑及法线图生成方案咨询

高度图平滑与法线图生成实现方案

一、高度图平滑方案

普通高斯模糊会均匀加权所有邻域像素,容易抹平原始高度图的区块特征,导致输出地形层级丢失。推荐使用保留边缘的双边滤波插值+坡度关联噪声的方案,既可以生成自然的坡度过渡,也能保留原本的高度区块特征。

实现步骤:

  • 第一步:将输入高度图的像素值转换为归一化高度值,比如8位灰度图可以把0-255的像素值映射到实际地形的[min_h, max_h]高度范围,避免插值过程中出现精度损失。
  • 第二步:用双边滤波做邻域插值,每个像素的权重同时考虑空间距离和高度差:高度差越大的邻域像素权重越低,避免把原本属于不同层级的高度区块强行融合。核心权重公式如下:

    w = exp(-(dx²+dy²)/(2*σ_spatial²)) * exp(-(h_center - h_neighbor)²/(2*σ_height²))
    其中σ_spatial控制平滑的空间范围,数值越大平滑范围越广;σ_height控制高度差过滤阈值,数值越小越能保留不同高度的硬边界,可根据自身高度图的区块落差调整参数。

  • 第三步:插值完成后叠加细节噪声,不要直接加全局随机噪声,仅给坡度区域叠加小振幅的分层噪声,平地区域不加,避免破坏整体地形轮廓,同时模拟自然地表的粗糙感。

示例代码(Python,依赖numpy、Pillow):

import numpy as np
from PIL import Image

def smooth_heightmap(input_path, output_path, sigma_spatial=2, sigma_height=15, noise_strength=3):
    # 读取高度图为灰度数组
    img = Image.open(input_path).convert('L')
    h_arr = np.array(img, dtype=np.float32)
    h, w = h_arr.shape
    smoothed = np.zeros_like(h_arr)
    
    # 3x3邻域双边滤波插值
    for y in range(h):
        for x in range(w):
            total_w = 0
            total_h = 0
            for dy in (-1, 0, 1):
                for dx in (-1, 0, 1):
                    ny, nx = y + dy, x + dx
                    if 0 <= ny < h and 0 <= nx < w:
                        # 计算空间距离权重
                        spatial_w = np.exp(-(dx**2 + dy**2) / (2 * sigma_spatial ** 2))
                        # 计算高度差权重
                        height_w = np.exp(-(h_arr[y, x] - h_arr[ny, nx]) ** 2 / (2 * sigma_height ** 2))
                        w = spatial_w * height_w
                        total_w += w
                        total_h += h_arr[ny, nx] * w
            smoothed[y, x] = total_h / total_w
    
    # 叠加坡度关联的细节噪声
    gy, gx = np.gradient(smoothed)
    slope = np.sqrt(gx**2 + gy**2)
    slope = (slope - slope.min()) / (slope.max() - slope.min() + 1e-6) # 坡度归一化
    noise = np.random.normal(0, noise_strength, size=smoothed.shape) * slope
    final = np.clip(smoothed + noise, 0, 255).astype(np.uint8)
    
    Image.fromarray(final).save(output_path)
    return final

二、平滑高度图生成法线图

法线图存储每个像素点对应地形表面的法向量,将XYZ三个分量从[-1,1]映射到[0,255]的RGB像素值即可生成,实现逻辑非常轻量。

实现步骤:

  • 第一步:计算平滑后高度图的X、Y方向梯度:gx = h[x+1,y] - h[x-1,y]、gy = h[x,y+1] - h[x,y-1]
  • 第二步:构造法向量 normal = (-gx, -gy, 2.0),并将向量归一化为单位长度
  • 第三步:将归一化后的三个分量分别映射到0-255的范围,R通道存X分量、G通道存Y分量、B通道存Z分量,输出为RGB图即可。

示例代码:

def generate_normal_map(smoothed_h_arr, output_path, flip_y=False):
    h, w = smoothed_h_arr.shape
    # 计算x/y方向梯度
    gx, gy = np.gradient(smoothed_h_arr)
    # 适配不同引擎的y轴朝向规则
    gy = -gy if flip_y else gy
    # 构造法向量
    normal = np.stack((-gx, gy, np.ones_like(gx)*2), axis=-1)
    # 归一化到单位向量
    norm = np.linalg.norm(normal, axis=-1, keepdims=True)
    normal = normal / (norm + 1e-6)
    # 分量从[-1,1]映射到[0,255]
    normal = ((normal + 1) * 127.5).astype(np.uint8)
    Image.fromarray(normal).save(output_path)

注意事项:

  • 不同图形引擎的法线Y轴朝向规则不同,OpenGL风格的法线需要翻转Y分量,调用函数时传入flip_y=True即可适配。
  • 如果需要更平滑的法线效果,可以把梯度计算的邻域范围从相邻1像素扩展到3-5像素,减少法线的锯齿感。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.05 14:45:03