Однажды у меня была задача, состоявшая в перезапекании большого количества моделей самих на себя, но в другую UV развёртку.

Однако в процессе Я столкнулся с тем, что в запеченных текстурах получалось большое количество артефактов. Мелких, но неприятных. Замазюкивание косяков вручную подошло бы для 1-2 раз, но в случае постановки этого на конвейер пришлось придумывать что-то ещё.

Артефакты от закрытой при запекании челюсти

Причины

Причиной было то, что при запекании проецирующие «лучи» начинают свой путь на небольшом расстоянии от поверхности. И если между желаемым местом пересечения и реальным местом начала луча что-то есть — увы, на проекцию попадёт именно оно.

Соседние части модели красят друг друга

Решение

Решением стала «разноска» отдельных составляющих (фрагментов) модели на расстояние друг от друга. Это должно обеспечить достаточное расстояние между фрагментами модели для того, что бы лучи запущенные в один фрагмент, не попадали части других фрагментов.

Разноска фрагментов предотвращают покраску соседей

Сделал я это дополнив плагин BakeLab2 дополнительной опцией разноса модели перед запеканием.

Для удобства чтения — вынес этот функционал в отдельный скрипт, который можно запустить напрямую в блендере (проверялось на 4.2 LTS)

Скрипт создаёт копию выделенного ассета, и применяет к нему разбиение на самостоятельные куски. После этого каждый отдельный кусок сдвигается вперёд так, что бы не пересекаться с предыдущими. В конце это всё обратно объединяется в единый, но «разнесённый» меш для удобства запекания.

import bpy

def get_world_scale(o):
    return o.matrix_world.to_scale()

def SelectObject(obj):
    bpy.ops.object.select_all(action = 'DESELECT')
    if obj:
        obj.select_set(True)
    bpy.context.view_layer.objects.active = obj

obj_selected = bpy.context.selected_objects[0] #saving selected object ref
obj_selected_name = obj_selected.name
obj_selected_data_name = obj_selected.data.name

obj_selected.name= obj_selected_name+"." + str(998)
obj_selected.data.name = obj_selected_data_name + "." + str(998)

#duplicating object
bpy.ops.object.duplicate(linked=False)
obj = bpy.context.selected_objects[0]
SelectObject(obj)
bpy.ops.mesh.separate(type='LOOSE')

# shifting parts
shft = 0
shft1 = 0
for nr, obj in enumerate(bpy.context.selected_objects):
    shft = shft + max(obj.dimensions)*(1/get_world_scale(obj)[2])
    if shft1 == 0:
        shft1 = shft
    obj.location = (obj.location[0], obj.location[1], obj.location[2] + shft)
    shft = shft + max(obj.dimensions) * (1 / get_world_scale(obj)[2])
    #clearing custom normals
    bpy.context.view_layer.objects.active = obj
    bpy.ops.mesh.customdata_custom_splitnormals_clear()
# rejoin
bpy.context.view_layer.objects.active = bpy.context.selected_objects[0]
bpy.ops.object.join()

Собственно дальше плагин создаёт копию разнесённого меша и запекает один на другой.

Исходный персонаж
Персонаж, фрагменты которого разнесены

Отредактированный мною плагин ещё с тех пор лежит на Гитхабе. Но Я так и не дошел до того, что бы отредактировать README, дописав туда добавленный мною функционал. Если однажды Я доведу репозиторий до ума — обязательно выложу ссылку.

Заключение

В заключение хочу заметить, что это решение не является абсолютным. Если в меше самопересекается цельная геометрия, то такой подход сам по себе не поможет. В таком случае нужно предаврительно отделить самопересекающиеся части в отдельные фрагменты, уже после чего делать всё то, о чем я писал выше.

Избавляемся от самопересечения при запекании в Blender