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

WebGL光照法线计算异常:线性黑斑问题求助

WebGL光照计算法线出现线性黑斑问题求助

在WebGL光照计算法线时,出现了线性分布的黑斑。尝试给每个法线添加0.1做平滑处理,效果不理想;用gl-matrix库计算法线,结果更差,求解决方法。

法线计算函数代码

function initVertexNormals(verts, indices, rowSize, num_dims) {
    //
    const normals = new Array(vertexNormals.length).fill(0);

    // Iterate through the indices to calculate normals

    //for loop size explained:
    //vertexNormals.length/2: we calculate normal for each square not triangle
    // - 3 * rowSize: we can't calculate normals for last row as we need points in next row to make calculations,
    //                so the 2nd last iteration takes care of last row normals.
    for (let i= 0; i < vertexNormals.length/2 - 3 * rowSize; i+=3) {
        const b = [vertices[i], vertices[i + 1], vertices[i + 2]];
        const s = [vertices[(i + rowSize * num_dims)], vertices[(i + rowSize * num_dims) + 1], vertices[(i + rowSize * num_dims) + 2]];

        let r = new Array(3).fill(0);
        if (i % 2 === 0) {
            r = [vertices[(i + 1 * num_dims)], vertices[(i + 1 * num_dims) + 1], vertices[(i + 1 * num_dims) + 2]];
        } else {
            r = [vertices[(i + (rowSize - 1) * num_dims)], vertices[(i + (rowSize - 1) * num_dims) + 1], vertices[(i + (rowSize - 1) * num_dims) + 2]];
        }

        const QR = [r[0] - b[0], r[1] - b[1], r[2] - b[2]];
        const QS = [s[0] - b[0], s[1] - b[1], s[2] - b[2]];

        let normal = new Array(3).fill(0);
        if (i % 2 === 0) {
            normal = [(QR[0] * QS[0]) / 2, (QR[1] * QS[1]) / 2, (QR[2] * QS[2]) / 2];

            for (let j= 0; j<num_dims; j++){
                normal[j] = 1/Math.abs(normal[j] - 0.1);
            }

            normals[i] = normal[0];
            normals[i + 1] = normal[1];
            normals[i + 2] = normal[2];

            normals[(i + 1 * num_dims)] = normal[0];
            normals[(i + 1 * num_dims) + 1] = normal[1];
            normals[(i + 1 * num_dims) + 2] = normal[2];

            normals[(i + rowSize * num_dims)] = normal[0];
            normals[(i + rowSize * num_dims) + 1] = normal[1];
            normals[(i + rowSize * num_dims) + 2] = normal[2];
        } else {
            normal = [QR[0] * QS[0], QR[1] * QS[1], QR[2] * QS[2]];

            for (let j= 0; j<num_dims; j++){
                normal[j] = 1/Math.abs(normal[j] - 0.1);
            }

            normals[i] = normal[0];
            normals[i + 1] = normal[1];
            normals[i + 2] = normal[2];

            normals[(i + (rowSize - 1) * num_dims)] = normal[0];
            normals[(i + (rowSize - 1) * num_dims) + 1] = normal[1];
            normals[(i + (rowSize - 1) * num_dims) + 2] = normal[2];

            normals[(i + rowSize * num_dims)] = normal[0];
            normals[(i + rowSize * num_dims) + 1] = normal[1];
            normals[(i + rowSize * num_dims) + 2] = normal[2];
        }
    }
    return normals;
}

平滑处理代码片段

for (let j= 0; j<num_dims; j++){
    normal[j] = 1/Math.abs(normal[j] - 0.1);
}

效果截图

  • 带平滑效果:带平滑效果的渲染结果
  • 无平滑效果:无平滑效果的渲染结果

问题分析与解决方案

  1. 核心计算错误
    你当前用分量相乘(QR[0] * QS[0])计算法线,这完全不符合法线的定义。法线是平面两个边向量的叉积,只有叉积结果才是垂直于平面的正确方向向量。

  2. 无效的平滑逻辑
    你写的1/Math.abs(normal[j] - 0.1)既不是法线归一化,也不是邻域平滑,只会导致法线数值异常,加重渲染问题,直接删掉即可。

  3. 具体修复步骤

    • 替换为叉积计算:
      手动实现叉积或用gl-matrix的vec3.cross,示例代码:
      // 计算QR和QS的叉积,得到法线向量
      const normal = [
          QR[1] * QS[2] - QR[2] * QS[1],
          QR[2] * QS[0] - QR[0] * QS[2],
          QR[0] * QS[1] - QR[1] * QS[0]
      ];
      
    • 归一化法线:
      叉积后的向量需要归一化(转为单位向量),否则光照计算会出现明暗不均:
      const length = Math.sqrt(normal[0]**2 + normal[1]**2 + normal[2]**2);
      if (length > 0) {
          normal[0] /= length;
          normal[1] /= length;
          normal[2] /= length;
      }
      
    • 修正变量不一致:
      函数参数是verts,但代码里用的是vertices,会导致未定义错误,要统一变量名。
    • 可选:顶点法线平滑:
      如果要实现平滑光照,需要对共享同一顶点的所有面的法线取平均值,再归一化,而不是用当前的错误逻辑。
  4. 测试验证
    修正上述问题后,重新运行代码,黑斑问题应该会消失,光照效果恢复正常。


内容的提问来源于stack exchange,提问作者O F A L

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.10 12:18:13