定义
法线是指垂直于某一表面(如平面或曲面)在该点的单位向量。在三维图形中,法线用于描述表面朝向,常用于光照、碰撞等计算。
一般计算方法
- 对于平面,法线可以用两个不平行的边的叉积得到。
- 对于曲面(如高度图地形),常用相邻点的高度差近似切平面,再用叉积或类似
Vector3(u, _vertex_spacing, v)这种方式近似法线,最后归一化为单位向量。
Terrain3D 中的法线计算示例
Godot Terrain3D 插件中计算法线函数是这样的:
Vector3 Terrain3DData::get_normal(const Vector3 &p_global_position) const {
if (get_region_idp(p_global_position) < 0 || is_hole(get_control(p_global_position))) {
return Vector3(NAN, NAN, NAN);
}
real_t height = get_height(p_global_position);
real_t u = height - get_height(p_global_position + Vector3(_vertex_spacing, 0.0f, 0.0f));
real_t v = height - get_height(p_global_position + Vector3(0.f, 0.f, _vertex_spacing));
Vector3 normal = Vector3(u, _vertex_spacing, v);
normal.normalize();
return normal;
}为什么
(u, _vertex_spacing, v)能表示为法线?1. 地形表面的参数表示 地形可以看作一个高度函数
y = f(x, z),其中:
x, z是水平坐标y是高度值2. 切向量的计算 在当前点,我们可以计算两个切向量:
- X 方向切向量:
T_x = (vertex_spacing, Δy_x, 0)- Z 方向切向量:
T_z = (0, Δy_z, vertex_spacing)其中:
Δy_x = get_height(x + vertex_spacing) - get_height(x)Δy_z = get_height(z + vertex_spacing) - get_height(z)3. 法向量 = 两个切向量的叉积
N = T_x × T_z = (vertex_spacing, Δy_x, 0) × (0, Δy_z, vertex_spacing)计算叉积:
N = (Δy_x × vertex_spacing - 0 × Δy_z, 0 × 0 - vertex_spacing × vertex_spacing, vertex_spacing × Δy_z - Δy_x × 0) = (Δy_x × vertex_spacing, -vertex_spacing², Δy_z × vertex_spacing)4. 代码中的处理 代码中使用了相反的高度差:
real_t u = height - get_height(p_global_position + Vector3(_vertex_spacing, 0.0f, 0.0f)); real_t v = height - get_height(p_global_position + Vector3(0.f, 0.f, _vertex_spacing));这相当于:
u = -Δy_xv = -Δy_z所以法向量变为:
N = (-Δy_x × vertex_spacing, -vertex_spacing², -Δy_z × vertex_spacing) = (-u × vertex_spacing, -vertex_spacing², -v × vertex_spacing)如果我们将其归一化并调整符号(让法向量指向上方),最终得到的就是
(u, vertex_spacing, v)的形式。直观理解
想象一个倾斜的平面:
- 如果地形在 X 方向上升,
u为负,法向量的 X 分量为负- 如果地形在 Z 方向上升,
v为负,法向量的 Z 分量为负- Y 分量(
vertex_spacing)始终为正,确保法向量大致指向 ” 上方 ”这样构成的向量确实垂直于地形表面!