Blender Weighted Normal

September 26

效果对比

开启前 / Before apply
开启后 / After apply
开启 Weighted Normal 之前, 渲染结果不平滑。

图片中的收益完全来自于对法线的加权计算, 不增加顶点数和三角形数。如果通过平滑细分来实现相同的效果, 三角形面数可能会增加到原来的 16 倍。


原理

Principle

原理其实十分简单。假设有一个顶点 pp 被4个面共享。由于顶点本身没有法线的概念, 顶点处的法线只能由它所 在面的法线来确定。在计算法线时选择不同的面, 会获得4个不同的法线 n1,n2,n3,n4n_1, n_2, n_3, n_4

如果单独计算每个面的法线, 并直接将计算结果从 vertex shader 送往 fragment shader 插值, 那么四个面的法线将完全不连续, 出现明显的 sharp edge。这种渲染方式叫做 Flat Shading

常用的解决方法是在顶点处储存法线, 再通过 GPU 的插值能力计算平面上各个点处的法线, 从而消除 sharp edge。这种 渲染方式叫做 Smooth Shading

平滑渲染 / Smooth Shading
加权法线 / Weighted Normal

这种方法对面数较高的模型效果很好, 但是对于面数较低, 或者表面形状复杂的模型, 效果就不是很理想了。


源码分析

Blender Weighted Normal Node - Github

Pricinple with interpolated normal

Weighted Normal 就是通过这4个法线的加权平均来保证面转角处的法线过度平滑, 从而在 shading 阶段 获得更加平滑的渲染效果。

Weighted Normal modifier 的核心算法可以分解为以下步骤:

// 1. Initialization prepare_data() initialize_buffers() // 2. Face area calculation for each face of mesh: with different mode, face area: calculate_weight_by_face_area() corner angle: calculate_weight_by_corner_angle() face area and corner angle: calculate_weight_by_face_area_and_corner_angle() sort_face_by_its_weight() // 3. Normal aggregation for each face of sorted faces: for each vertex of face: // 3.1. aggregate normal to the vertex's weighted normal let weight = exponentially_decreasing_weight(vertex.id) vertex.normal += vertex.normal * weight // 4. Normalization normalize_vertex_normals() // 5. Cleanup free_allocated_buffers()

© 2020 - 2025 Ruiyao Luo

25/12/10 11:30

PROD

#68e6fa8