Files
AnyImage/tools/nodes/groups/image_layer.py
nan c62ec2cd48 Refactor depth surface handling and unify depth sampling rules
- Removed the `always_vertex` parameter from `build_surface_camera` to streamline depth sampling for cutouts and ensure consistent behavior across depth plane and panorama.
- Updated depth sampling logic to ensure that all free boundary points, including the original frame of the depth plane, use neighboring face depth instead of their own texture sampling.
- Adjusted the depth mask deletion process to operate on faces rather than points, eliminating the need for additional cleanup of dangling edges.
- Ensured that the cutout rules are consistently applied across all relevant nodes, removing exceptions and simplifying the logic.
- Updated tests to verify the new behavior, ensuring that cutout points follow the depth of adjacent faces and that the original frame maintains its depth sampling.
- Refactored geometry handling in multiple depth-related nodes to improve clarity and maintainability.
2026-09-16 16:30:56 +08:00

104 lines
5.5 KiB
Python

"""Build O Image Layer."""
import bpy
from anyimage.common.material import IMAGE_MATERIAL_NODE_GROUP_NAME
from anyimage.operators.cutout_tool.shape import NORMAL_REDUCTION_ATTRIBUTE_NAME
from ..common.shader import build_material_nodes, connect_material_outputs
from ..common.normal_map import transform_object_normal_color
def build_image_layer_group():
group_name = IMAGE_MATERIAL_NODE_GROUP_NAME
existing = bpy.data.node_groups.get(group_name)
if existing is not None:
return existing
group = bpy.data.node_groups.new(group_name, 'ShaderNodeTree')
interface = group.interface
input_sockets = [('Color', 'NodeSocketColor'), ('Alpha', 'NodeSocketFloat'), ('Bump Scale', 'NodeSocketFloat')]
input_sockets.insert(2, ('Normal', 'NodeSocketColor'))
input_sockets.insert(3, ('Normal Scale', 'NodeSocketFloat'))
input_sockets.insert(4, ('Object Space', 'NodeSocketBool'))
input_sockets.insert(2, ('Alpha Fix', 'NodeSocketFloat'))
options = interface.new_panel(name='Options', default_closed=True)
option_names = {'Alpha Fix', 'Object Space'}
for name, socket_type in input_sockets:
socket = interface.new_socket(name=name, in_out='INPUT', socket_type=socket_type, parent=options if name in option_names else None)
if name in {'Alpha', 'Alpha Fix'}:
socket.default_value = 1.0 if name == 'Alpha' else 0.0
socket.min_value = 0.0
socket.max_value = 1.0
socket.subtype = 'FACTOR'
elif name == 'Bump Scale':
socket.description = 'Control bump detail derived from image brightness. Negative values reverse the bumps. Zero disables the effect.'
socket.default_value = 0.0
socket.min_value = -10000.0
socket.max_value = 10000.0
elif name == 'Normal':
socket.default_value = (0.5, 0.5, 1.0, 1.0)
elif name == 'Normal Scale':
socket.description = 'Control the strength of surface detail from the normal map.'
socket.default_value = 1.0
socket.min_value = 0.0
socket.max_value = 10.0
elif name == 'Object Space':
socket.description = 'Interpret the normal map in object space when enabled, or tangent space when disabled.'
socket.default_value = False
if name == 'Alpha Fix':
socket.description = 'Reduce semi-transparent fringes around image edges. Higher values remove more faint edge pixels. Zero keeps the original transparency.'
output_sockets = [('Color', 'NodeSocketColor'), ('Roughness', 'NodeSocketFloat'), ('Alpha', 'NodeSocketFloat'), ('Normal', 'NodeSocketVector')]
for name, socket_type in output_sockets:
interface.new_socket(name=name, in_out='OUTPUT', socket_type=socket_type)
nodes, links = group.nodes, group.links
group_input, group_output, roughness_curve, bump = build_material_nodes(group)
normal_attribute = nodes.new('ShaderNodeAttribute')
normal_attribute.attribute_type = 'GEOMETRY'
normal_attribute.attribute_name = NORMAL_REDUCTION_ATTRIBUTE_NAME
normal_weight = nodes.new('ShaderNodeMath')
normal_weight.operation = 'SUBTRACT'
normal_weight.inputs[0].default_value = 1.0
links.new(normal_attribute.outputs['Fac'], normal_weight.inputs[1])
weighted_strength = nodes.new('ShaderNodeMath')
weighted_strength.operation = 'MULTIPLY'
links.new(group_input.outputs['Normal Scale'], weighted_strength.inputs[0])
links.new(normal_weight.outputs[0], weighted_strength.inputs[1])
tangent_normal = nodes.new('ShaderNodeNormalMap')
tangent_normal.space = 'TANGENT'
object_normal = nodes.new('ShaderNodeNormalMap')
object_normal.space = 'OBJECT'
choose_normal = nodes.new('ShaderNodeMix')
choose_normal.data_type = 'VECTOR'
links.new(group_input.outputs['Normal'], tangent_normal.inputs['Color'])
links.new(weighted_strength.outputs[0], tangent_normal.inputs['Strength'])
normal_color, normal_strength = transform_object_normal_color(
group, group_input.outputs['Normal'], weighted_strength.outputs[0],
)
links.new(normal_color, object_normal.inputs['Color'])
links.new(normal_strength, object_normal.inputs['Strength'])
links.new(group_input.outputs['Object Space'], choose_normal.inputs[0])
links.new(tangent_normal.outputs['Normal'], choose_normal.inputs[4])
links.new(object_normal.outputs['Normal'], choose_normal.inputs[5])
links.new(choose_normal.outputs['Result'], bump.inputs['Normal'])
processed_alpha = group_input.outputs['Alpha']
alpha_curve = nodes.new('ShaderNodeFloatCurve')
alpha_curve.mapping.initialize()
alpha_curve.mapping.extend = 'HORIZONTAL'
alpha_curve.mapping.use_clip = True
alpha_points = alpha_curve.mapping.curves[0].points
alpha_points[0].location = (0.0, 0.0)
alpha_points[-1].location = (1.0, 1.0)
alpha_points.new(0.8, 0.0)
alpha_points.new(0.925, 0.925)
for point in alpha_points:
point.handle_type = 'AUTO_CLAMPED'
alpha_curve.mapping.update()
links.new(group_input.outputs['Alpha'], alpha_curve.inputs['Value'])
links.new(group_input.outputs['Alpha Fix'], alpha_curve.inputs['Factor'])
processed_alpha = alpha_curve.outputs['Value']
connect_material_outputs(group, group_input, group_output, roughness_curve, bump, processed_alpha)
group.color_tag = 'SHADER'
group.description = f'Prepare {group_name} material outputs.'
group.use_fake_user = True
for node in nodes:
for socket in node.outputs:
socket.hide = not socket.is_linked
return group