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); }
效果截图
- 带平滑效果:

- 无平滑效果:

问题分析与解决方案
核心计算错误
你当前用分量相乘(QR[0] * QS[0])计算法线,这完全不符合法线的定义。法线是平面两个边向量的叉积,只有叉积结果才是垂直于平面的正确方向向量。无效的平滑逻辑
你写的1/Math.abs(normal[j] - 0.1)既不是法线归一化,也不是邻域平滑,只会导致法线数值异常,加重渲染问题,直接删掉即可。具体修复步骤
- 替换为叉积计算:
手动实现叉积或用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,会导致未定义错误,要统一变量名。 - 可选:顶点法线平滑:
如果要实现平滑光照,需要对共享同一顶点的所有面的法线取平均值,再归一化,而不是用当前的错误逻辑。
- 替换为叉积计算:
测试验证
修正上述问题后,重新运行代码,黑斑问题应该会消失,光照效果恢复正常。
内容的提问来源于stack exchange,提问作者O F A L
相关产品推荐
相关产品推荐

