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