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

基于11个3D采样点的Three.js网格面颜色插值方法问询

3D网格面数值插值方案与实现

问题背景

我有一个3D网格,以及11个带预定义数值的静态采样点,坐标与数值如下:

x: 0.3 y: 0.4, z: 0.5, value: 100,
x: 0.6 y: 0.9, z: 0.66, value: 20,
x: 0.4 y: 0.22, z: 0.35, value: 50,
x: 0.11 y: 0.34, z: 0.65, value: 0,
x: 0.35 y: 0.65, z: 0.5, value: 0,
x: 0.22 y: 0.67, z: 0.5, value: 0,
x: 0.43 y: 0.22, z: 0.5, value: 0,
x: 0.67 y: 0.31, z: 0.5, value: 16,
x: 0.84 y: 0.34, z: 0.5, value: 13,
x: 0.22 y: 0.43, z: 0.5, value: 0,
x: 0.13 y: 0.24, z: 0.5, value: 11,

需要根据这些采样点的距离为每个网格面赋予对应数值(例如某面坐标 x: 0.34, y: 0.43, z: 0.18,求其对应value),最终实现网格面着色。希望得到前端JavaScript/Three.js的解决方案,或后端Python的处理库,之前找到的方法仅支持2D或两点插值,不知如何适配多点数组。

推荐插值方案

最适合这个场景的是反距离加权插值(IDW, Inverse Distance Weighting),它支持任意维度的多点插值,核心逻辑是:目标点的数值由周围采样点的数值加权平均得到,权重与采样点到目标点的距离成反比,距离越近的点权重越高。

核心逻辑

目标点P的数值计算方式:
$$
V(P) = \frac{\sum_{i=1}^{n} \frac{V_i}{d_ik}}{\sum_{i=1}{n} \frac{1}{d_i^k}}
$$
其中:

  • $V_i$:第i个采样点的数值
  • $d_i$:第i个采样点到目标点P的欧氏距离
  • $k$:权重指数(通常取2,可根据需求调整,值越大,近点的权重占比越高)

如果某个采样点与目标点距离为0,直接取该点的数值即可。


前端JavaScript/Three.js实现

可以直接编写IDW插值函数,结合Three.js获取网格面的中心坐标(通常用面的三个顶点坐标平均值作为代表点),计算后赋值给面的颜色属性。

示例代码

// 采样点数据整理为数组
const samplePoints = [
  { x: 0.3, y: 0.4, z: 0.5, value: 100 },
  { x: 0.6, y: 0.9, z: 0.66, value: 20 },
  { x: 0.4, y: 0.22, z: 0.35, value: 50 },
  { x: 0.11, y: 0.34, z: 0.65, value: 0 },
  { x: 0.35, y: 0.65, z: 0.5, value: 0 },
  { x: 0.22, y: 0.67, z: 0.5, value: 0 },
  { x: 0.43, y: 0.22, z: 0.5, value: 0 },
  { x: 0.67, y: 0.31, z: 0.5, value: 16 },
  { x: 0.84, y: 0.34, z: 0.5, value: 13 },
  { x: 0.22, y: 0.43, z: 0.5, value: 0 },
  { x: 0.13, y: 0.24, z: 0.5, value: 11 },
];

// 计算3D欧氏距离
function calculateDistance(p1, p2) {
  const dx = p1.x - p2.x;
  const dy = p1.y - p2.y;
  const dz = p1.z - p2.z;
  return Math.sqrt(dx*dx + dy*dy + dz*dz);
}

// IDW插值函数
function idwInterpolation(targetPoint, samples, k = 2) {
  let numerator = 0;
  let denominator = 0;

  for (const sample of samples) {
    const d = calculateDistance(targetPoint, sample);
    if (d === 0) return sample.value; // 重合点直接返回数值

    const weight = 1 / Math.pow(d, k);
    numerator += sample.value * weight;
    denominator += weight;
  }

  return denominator === 0 ? 0 : numerator / denominator;
}

// 在Three.js中处理网格面
function colorMeshFaces(mesh) {
  const geometry = mesh.geometry;
  geometry.computeFaceNormals();
  const colors = [];

  // 遍历每个面,计算中心坐标并插值
  for (let i = 0; i < geometry.faces.length; i++) {
    const face = geometry.faces[i];
    // 获取三个顶点坐标
    const v1 = geometry.vertices[face.a];
    const v2 = geometry.vertices[face.b];
    const v3 = geometry.vertices[face.c];
    // 计算面中心坐标
    const center = {
      x: (v1.x + v2.x + v3.x) / 3,
      y: (v1.y + v2.y + v3.y) / 3,
      z: (v1.z + v2.z + v3.z) / 3,
    };
    // 插值得到数值
    const value = idwInterpolation(center, samplePoints);
    // 将数值映射为颜色(示例:0-100对应黑到红)
    const normalized = value / 100;
    colors.push(new THREE.Color(normalized, 0, 0));
  }

  geometry.colors = colors;
  mesh.geometry = geometry;
  mesh.material.vertexColors = THREE.FaceColors;
}

后端Python实现

可以手动实现IDW,也可以使用scipy库的插值模块,若需要更专业的空间插值,还可以用pykrige库实现克里金插值,但IDW足够满足基础需求。

手动实现IDW示例

import numpy as np

# 采样点数据
sample_points = np.array([
    [0.3, 0.4, 0.5, 100],
    [0.6, 0.9, 0.66, 20],
    [0.4, 0.22, 0.35, 50],
    [0.11, 0.34, 0.65, 0],
    [0.35, 0.65, 0.5, 0],
    [0.22, 0.67, 0.5, 0],
    [0.43, 0.22, 0.5, 0],
    [0.67, 0.31, 0.5, 16],
    [0.84, 0.34, 0.5, 13],
    [0.22, 0.43, 0.5, 0],
    [0.13, 0.24, 0.5, 11],
])

def idw_interpolation(target_point, samples, k=2):
    # 提取采样点坐标和数值
    coords = samples[:, :3]
    values = samples[:, 3]
    # 计算距离
    distances = np.linalg.norm(coords - target_point, axis=1)
    # 处理重合点
    zero_dist_mask = distances == 0
    if np.any(zero_dist_mask):
        return values[zero_dist_mask][0]
    # 计算权重和插值结果
    weights = 1 / (distances ** k)
    numerator = np.sum(values * weights)
    denominator = np.sum(weights)
    return numerator / denominator if denominator != 0 else 0

# 测试目标点
target = np.array([0.34, 0.43, 0.18])
result = idw_interpolation(target, sample_points)
print(f"目标点数值: {result:.2f}")

使用Scipy的径向基函数插值(可选)

如果需要更平滑的插值效果,可以用Scipy的Rbf函数:

from scipy.interpolate import Rbf

x = sample_points[:, 0]
y = sample_points[:, 1]
z = sample_points[:, 2]
v = sample_points[:, 3]

# 创建径向基函数插值器
rbf = Rbf(x, y, z, v, function='multiquadric')
# 计算目标点数值
result = rbf(0.34, 0.43, 0.18)
print(f"目标点数值: {result:.2f}")

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.13 10:01:15