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

Three.js中如何为被遮挡球面网格面设置清晰着色并存储颜色数组

解决方案:清晰着色遮挡球面网格面并存储颜色数组

一、解决颜色渐变问题,实现清晰面着色

颜色渐变的核心原因是顶点颜色插值或未启用平面着色,要让每个网格面呈现纯色,需从几何体配置和材质设置两方面入手:

1. 配置几何体与材质

使用FaceColors(针对传统Geometry)或统一顶点颜色(针对BufferGeometry),同时启用flatShading禁用平滑插值:

传统Geometry实现

// 创建球面几何体,设置足够分段确保面独立
const sphereGeo = new THREE.SphereGeometry(5, 32, 32);
sphereGeo.computeFaceNormals();

// 初始化所有面为默认白色
sphereGeo.faces.forEach(face => {
  face.color.set(0xffffff);
});

// 创建材质:启用面颜色、平面着色
const sphereMat = new THREE.MeshLambertMaterial({
  vertexColors: THREE.FaceColors,
  flatShading: true,
  side: THREE.DoubleSide // 可选:若需显示球面内侧
});

const sphereMesh = new THREE.Mesh(sphereGeo, sphereMat);
scene.add(sphereMesh);

BufferGeometry实现(性能更优)

const sphereGeo = new THREE.SphereBufferGeometry(5, 32, 32);
const colorArray = new Float32Array(sphereGeo.attributes.position.count * 3);

// 初始化所有顶点为白色(每个面的三个顶点颜色一致,保证面纯色)
for (let i = 0; i < colorArray.length; i += 3) {
  colorArray[i] = 1;
  colorArray[i + 1] = 1;
  colorArray[i + 2] = 1;
}

sphereGeo.setAttribute('color', new THREE.BufferAttribute(colorArray, 3));

const sphereMat = new THREE.MeshLambertMaterial({
  vertexColors: true,
  flatShading: true
});

const sphereMesh = new THREE.Mesh(sphereGeo, sphereMat);
scene.add(sphereMesh);

2. 精准检测遮挡面

使用Raycaster检测时,基于面中心而非顶点发射射线,避免因部分顶点可见导致的颜色混合:

const raycaster = new THREE.Raycaster();
const origin = new THREE.Vector3(0, 0, 0); // 原点位置
const obstacleMesh = yourObstacleMesh; // 你的障碍物网格

// --- 针对传统Geometry ---
sphereGeo.faces.forEach((face, index) => {
  // 计算面中心坐标
  const v1 = sphereGeo.vertices[face.a];
  const v2 = sphereGeo.vertices[face.b];
  const v3 = sphereGeo.vertices[face.c];
  const faceCenter = new THREE.Vector3()
    .addVectors(v1, v2)
    .add(v3)
    .divideScalar(3);

  // 设置射线:从原点指向面中心,排除球面自身
  raycaster.set(origin, faceCenter.clone().sub(origin).normalize());
  const intersects = raycaster.intersectObject(obstacleMesh, false);

  // 判断是否被遮挡:交点到原点的距离小于面中心到原点的距离
  if (intersects.length > 0) {
    const intersectDist = intersects[0].distance;
    const faceCenterDist = faceCenter.distanceTo(origin);
    if (intersectDist < faceCenterDist) {
      face.color.set(0xff0000); // 标记遮挡面为红色
    }
  }
});
sphereGeo.colorsNeedUpdate = true; // 更新颜色

// --- 针对BufferGeometry ---
const index = sphereGeo.index;
const positions = sphereGeo.attributes.position.array;
const colors = sphereGeo.attributes.color.array;

for (let i = 0; i < index.count; i += 3) {
  const a = index.getX(i);
  const b = index.getX(i + 1);
  const c = index.getX(i + 2);

  // 计算面中心
  const v1 = new THREE.Vector3(positions[a*3], positions[a*3+1], positions[a*3+2]);
  const v2 = new THREE.Vector3(positions[b*3], positions[b*3+1], positions[b*3+2]);
  const v3 = new THREE.Vector3(positions[c*3], positions[c*3+1], positions[c*3+2]);
  const faceCenter = new THREE.Vector3()
    .addVectors(v1, v2)
    .add(v3)
    .divideScalar(3);

  // 射线检测逻辑同上
  raycaster.set(origin, faceCenter.clone().sub(origin).normalize());
  const intersects = raycaster.intersectObject(obstacleMesh, false);

  if (intersects.length > 0) {
    const intersectDist = intersects[0].distance;
    const faceCenterDist = faceCenter.distanceTo(origin);
    if (intersectDist < faceCenterDist) {
      // 将当前面的三个顶点设为红色
      colors[a*3] = 1; colors[a*3+1] = 0; colors[a*3+2] = 0;
      colors[b*3] = 1; colors[b*3+1] = 0; colors[b*3+2] = 0;
      colors[c*3] = 1; colors[c*3+1] = 0; colors[c*3+2] = 0;
    }
  }
}
sphereGeo.attributes.color.needsUpdate = true;

二、存储与加载颜色数组

将每个面的颜色数据存储为数组,后续可直接复用,无需重新计算遮挡:

1. 存储颜色数组

// --- 针对传统Geometry ---
const savedFaceColors = sphereGeo.faces.map(face => face.color.getHex());
// 存储到localStorage或导出为JSON文件
localStorage.setItem('sphereFaceColors', JSON.stringify(savedFaceColors));

// --- 针对BufferGeometry ---
const savedColorArray = Array.from(sphereGeo.attributes.color.array);
localStorage.setItem('sphereColorArray', JSON.stringify(savedColorArray));

2. 加载颜色数组

// --- 针对传统Geometry ---
const loadedColors = JSON.parse(localStorage.getItem('sphereFaceColors'));
if (loadedColors) {
  sphereGeo.faces.forEach((face, index) => {
    face.color.set(loadedColors[index]);
  });
  sphereGeo.colorsNeedUpdate = true;
}

// --- 针对BufferGeometry ---
const loadedColorArray = JSON.parse(localStorage.getItem('sphereColorArray'));
if (loadedColorArray) {
  sphereGeo.attributes.color.array.set(loadedColorArray);
  sphereGeo.attributes.color.needsUpdate = true;
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.29 06:37:37