法线图,法线贴图学习笔记

发布于:2025-03-20 ⋅ 阅读:(98) ⋅ 点赞:(0)

目录

法线图相关知识

法线计算方式

示例 1:从三角面计算法线

示例 2:从深度图计算法线图


法线图相关知识

https://zhuanlan.zhihu.com/p/465801563

法线图(Normal Map)用于表示3D模型的表面方向信息,通常用于法线贴图(normal mapping)技术,以增强模型的细节而无需增加额外的几何面数。法线图的计算通常依赖于网格模型的顶点法线或从深度信息计算法线。

法线计算方式

  1. 从三角面计算法线(基于几何)

    • 计算三角形面的法线
    • 使用顶点法线(通过邻接面加权平均)
  2. 从深度图计算法线(屏幕空间法线)

    • 通过相邻像素的深度差计算梯度,再归一化得到法线

示例 1:从三角面计算法线

下面是一个 Python 代码示例,使用 numpy 计算三角面的法线:

import numpy as np

def compute_face_normal(v0, v1, v2):
    """
    计算三角形面的法线
    :param v0, v1, v2: 三角形的三个顶点(numpy 数组)
    :return: 归一化的法线向量
    """
    edge1 = v1 - v0
    edge2 = v2 - v0
    normal = np.cross(edge1, edge2)  # 叉积计算法线
    normal = normal / np.linalg.norm(normal)  # 归一化
    return normal

# 示例三角形顶点
v0 = np.array([0.0, 0.0, 0.0])
v1 = np.array([1.0, 0.0, 0.0])
v2 = np.array([0.0, 1.0, 0.0])

normal = compute_face_normal(v0, v1, v2)
print("Face Normal:", normal)

输出

Face Normal: [ 0. 0. 1.]

这表示该三角形的法线方向是 (0, 0, 1),即沿 Z 轴正方向。


示例 2:从深度图计算法线图

如果有一张深度图 depth_map,可以计算每个像素的梯度并归一化,得到法线图:

import cv2
import numpy as np

def compute_normal_map(depth_map):
    """
    从深度图计算法线图
    :param depth_map: 2D numpy 数组,表示深度值
    :return: 法线图 (H, W, 3),范围 [0, 1]
    """
    h, w = depth_map.shape
    normal_map = np.zeros((h, w, 3), dtype=np.float32)

    # 计算 X 和 Y 方向梯度
    grad_x = cv2.Sobel(depth_map, cv2.CV_32F, 1, 0, ksize=5)
    grad_y = cv2.Sobel(depth_map, cv2.CV_32F, 0, 1, ksize=5)

    # 计算法线
    normal_map[..., 0] = -grad_x  # X 分量
    normal_map[..., 1] = -grad_y  # Y 分量
    normal_map[..., 2] = 1.0  # Z 分量 (假设视线方向)

    # 归一化
    norm = np.linalg.norm(normal_map, axis=2, keepdims=True)
    normal_map = normal_map / (norm + 1e-8)

    # 转换到 [0, 255] 以便可视化
    normal_map_vis = ((normal_map + 1) / 2.0 * 255).astype(np.uint8)
    
    return normal_map_vis

# 生成一个简单的深度图
depth_map = np.random.rand(100, 100).astype(np.float32)  # 随机深度数据
normal_map = compute_normal_map(depth_map)

# 显示法线图
cv2.imshow("Normal Map", normal_map)
cv2.waitKey(0)
cv2.destroyAllWindows()

解释

  • cv2.Sobel() 计算 X 和 Y 方向的梯度。
  • 负号是因为图像坐标系与法线方向相反。
  • 假设 Z 分量是 1.0,然后进行归一化。
  • 最后转换到 [0, 255] 方便可视化。

 


网站公告

今日签到

点亮在社区的每一天
去签到