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


