在Blender中一般会使用实体化修改器来进行描边。
但是,如果模型法线在转角处没有平滑,就会导致描边出现断裂的问题。
使用焊接修改器虽然可以解决描边断裂问题,但是会破坏物体法线,使得原本就难看的阴影雪上加霜。

使用Python脚本创建平滑法线自定义属性,并在几何节点中引用。通过几何节点创建几何实例副本单独负责描边,不会影响原有网格法线。

import bpy
import bmesh
from mathutils import Vector

def store_average_normals_using_vertex_position(obj):
    if obj.type != 'MESH':
        print(f"Object {obj.name} is not a mesh. Skipping.")
        return

    # Switch to object mode if needed
    if bpy.context.mode != 'OBJECT':
        bpy.ops.object.mode_set(mode='OBJECT')

    # Create BMesh from object data
    mesh = obj.data
    bm = bmesh.new()
    bm.from_mesh(mesh)

    # Ensure normals are updated
    bm.normal_update()

    # Prepare a custom vertex attribute layer for average normals
    avg_normal_layer = bm.verts.layers.float_vector.new("average_normal")

    # Create a dictionary to store average normals by vertex position
    average_normal_hash = {}

    # Iterate over all vertices to calculate average normals based on position
    for vert in bm.verts:
        position = tuple(vert.co)
        if position not in average_normal_hash:
            average_normal_hash[position] = vert.normal.copy()
        else:
            average_normal_hash[position] += vert.normal

    # Normalize the accumulated normals
    for position in average_normal_hash:
        average_normal_hash[position].normalize()

    # Assign the averaged normals back to vertices
    for vert in bm.verts:
        position = tuple(vert.co)
        vert[avg_normal_layer] = average_normal_hash[position]

    # Write the modified BMesh back to the mesh
    bm.to_mesh(mesh)
    bm.free()

# Apply the function to the selected object
obj = bpy.context.object
if obj:
    store_average_normals_using_vertex_position(obj)
    print(f"Average normals stored in custom data layer for {obj.name}.")
else:
    print("No object selected.")

背面法描边是让顶点沿着法线移动一段距离,去做一个类似于鸡蛋壳的包裹来实现描边的。但是就拿一个正方体举例,一个角上其实有三个顶点。这三个顶点在不做处理的情况下,共享位置,但法线来自于各自属于的面。所以在扩张的时候就会沿着不同方向,导致三个点分离,从而产生描边断裂。
上面的代码的逻辑就是,将所有共享坐标的顶点的法线全部平均化再指定回去,这样就可以保证位置相同的顶点不会在向外扩展之后分离。