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

Three.js中BufferGeometry自定义法线实现平滑着色异常求助

立方球体LOD法线计算问题

我用LOD制作立方球体时,生成法线遇到了问题。将计算好的this.Normals赋值给BufferGeometry,使用MeshPhongMaterial创建网格后,相邻面之间的法线计算不正确,效果表现为相邻面出现明显明暗断层,过渡不自然。

我的法线生成代码如下:

// VectorVertices is an array of Vector3
let VectorNormals = new Array(this.VectorVertices.length);

for (let i = 0; i < VectorNormals.length; i++) {
    VectorNormals[i] = new THREE.Vector3();    
}

for (let i = 0; i < this.Triangles.length; i += 3) {
    let vertexIndexA = this.Triangles[i];
    let vertexIndexB = this.Triangles[i + 1];
    let vertexIndexC = this.Triangles[i + 2];

    let pointA = this.VectorVertices[vertexIndexA];
    let pointB = this.VectorVertices[vertexIndexB];
    let pointC = this.VectorVertices[vertexIndexC];

    pointB.sub(pointA);
    pointC.sub(pointA);
    let vertexNormal = new THREE.Vector3().crossVectors(pointB, pointC).normalize();

    VectorNormals[vertexIndexA].add(vertexNormal);
    VectorNormals[vertexIndexB].add(vertexNormal);
    VectorNormals[vertexIndexC].add(vertexNormal);
}

for (let i = 0; i < VectorNormals.length; i++) {
    VectorNormals[i].normalize();
    this.Normals.push(VectorNormals[i].x, VectorNormals[i].y, VectorNormals[i].z);
}

问题原因

核心问题是直接修改了原始顶点数据:pointB.sub(pointA)和pointC.sub(pointA)会直接改变this.VectorVertices中存储的Vector3对象坐标值。后续处理其他三角形时,用到这些已经被修改的顶点,计算出的面法线必然出错,最终导致顶点法线累加结果异常,出现明暗断层。

修复方案

计算向量时不要修改原始顶点,而是创建顶点的副本进行运算:

// VectorVertices is an array of Vector3
let VectorNormals = new Array(this.VectorVertices.length);

for (let i = 0; i < VectorNormals.length; i++) {
    VectorNormals[i] = new THREE.Vector3();    
}

for (let i = 0; i < this.Triangles.length; i += 3) {
    let vertexIndexA = this.Triangles[i];
    let vertexIndexB = this.Triangles[i + 1];
    let vertexIndexC = this.Triangles[i + 2];

    let pointA = this.VectorVertices[vertexIndexA];
    let pointB = this.VectorVertices[vertexIndexB];
    let pointC = this.VectorVertices[vertexIndexC];

    // 创建顶点副本,避免篡改原始数据
    const vecAB = pointB.clone().sub(pointA);
    const vecAC = pointC.clone().sub(pointA);
    let vertexNormal = new THREE.Vector3().crossVectors(vecAB, vecAC).normalize();

    VectorNormals[vertexIndexA].add(vertexNormal);
    VectorNormals[vertexIndexB].add(vertexNormal);
    VectorNormals[vertexIndexC].add(vertexNormal);
}

for (let i = 0; i < VectorNormals.length; i++) {
    VectorNormals[i].normalize();
    this.Normals.push(VectorNormals[i].x, VectorNormals[i].y, VectorNormals[i].z);
}

说明

通过clone()方法创建顶点副本后,所有向量运算都在副本上进行,原始顶点数据保持完整。每个三角形都能基于正确的原始坐标计算面法线,累加后的顶点法线能让相邻面的明暗过渡自然,解决断层问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.17 14:36:35