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
相关产品推荐
相关产品推荐

