WebGL中mat3.normalFromMat4的使用方法及原理技术咨询
嘿,这个问题我当初刚碰WebGL光照的时候也卡了好久!让我一步步给你掰明白normalMatrix到底是咋回事,还有mat3.normalFromMat4该咋用~
为啥法线不能直接用模型视图矩阵?
首先得搞清楚:顶点是位置向量,用模型视图矩阵变换没问题,但法线是方向向量——它的核心作用是保持和表面切线垂直。如果直接用模型视图矩阵(尤其是当模型有非均匀缩放的时候),法线会被“拉歪”,不再垂直于表面,光照计算直接就崩了。这时候就必须用专门的normalMatrix来变换法线。
「取前3x3+逆转置」的原理
你看的《计算机图形学导论》里的步骤,其实就是normalMatrix的计算逻辑,拆解一下:
- 砍掉第四行第四列:模型视图矩阵的第四部分是用来处理平移的,但法线是方向向量,平移不影响方向,所以直接提取前3×3的矩阵就够了。
- 求逆矩阵:如果模型被缩放了,逆矩阵能把缩放的影响“反向修正”,保证法线的方向不会被拉伸得离谱。
- 转置矩阵:这是向量变换的数学要求——要保证法线和变换后的表面切线依然垂直,就必须用逆矩阵的转置来变换法线。如果你的模型只有旋转和平移(没有非均匀缩放),逆矩阵等于转置,这时候逆转置后的矩阵和原矩阵前3×3一样,可以偷懒直接用,但有缩放的话必须老老实实算。
mat3.normalFromMat4的实际用法 这个函数就是帮你把上面的步骤自动化了!你不用手动写复杂的矩阵逆和转置运算,只需要传入你的模型视图矩阵,它会自动输出正确的normalMatrix。
举个原生WebGL的完整例子:
顶点着色器部分
precision mediump float; uniform mat4 modelViewMatrix; uniform mat4 projectionMatrix; uniform mat3 normalMatrix; attribute vec3 position; attribute vec3 normal; varying vec3 vNormal; // 传递给片元着色器的变换后法线 void main() { gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0); // 变换后记得归一化,避免缩放导致法线长度变化 vNormal = normalize(normalMatrix * normal); }
JavaScript端代码(用gl-matrix库)
// 假设你已经构建好了模型视图矩阵(比如用mat4组合平移/旋转/缩放) const modelViewMatrix = mat4.create(); mat4.lookAt(modelViewMatrix, [0, 2, 5], [0, 0, 0], [0, 1, 0]); // 示例:相机看向原点 // 计算normalMatrix const normalMatrix = mat3.create(); mat3.normalFromMat4(normalMatrix, modelViewMatrix); // 将normalMatrix传给着色器uniform const normalMatrixLoc = gl.getUniformLocation(program, 'normalMatrix'); gl.uniformMatrix3fv(normalMatrixLoc, false, normalMatrix);
额外提一句Three.js画质的小建议
你说Three.js纹理映射画质下降,大概率是纹理过滤的默认设置导致的。原生WebGL里你可以手动控制纹理过滤参数,比如加载纹理时设置:
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_LINEAR); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
这样能获得更清晰的纹理效果,避免Three.js默认的低精度过滤带来的模糊。
内容的提问来源于stack exchange,提问作者alan.raceQs
相关产品推荐
相关产品推荐

