- Introduced a new design for selection rasterization that decouples the selection geometry from the image content, ensuring that the SelectionMask is independent of the source image's alpha and color. - Updated the `rasterize_selection_path` function to only accept selection paths and image dimensions, removing parameters related to source alpha and alpha thresholds. - Implemented a new approach for handling selection paths using non-zero winding fill, ensuring overlapping areas are preserved. - Enhanced performance by utilizing a scanline algorithm for rasterization, significantly reducing processing time for high-resolution images. - Updated internal documentation and added comprehensive tests to validate the new behavior and performance improvements. - Ensured backward compatibility by maintaining existing interfaces where necessary while introducing breaking changes in the selection processing logic.
196 lines
8.2 KiB
Python
196 lines
8.2 KiB
Python
"""Build the image material Shader node groups."""
|
|
|
|
import bpy
|
|
|
|
from anyimage.common.material import (
|
|
DEPTH_MATERIAL_NODE_GROUP_NAME,
|
|
IMAGE_MATERIAL_NODE_GROUP_NAME,
|
|
)
|
|
|
|
|
|
BUMP_BASE_DISTANCE = 0.001
|
|
MATERIAL_DISPLACEMENT_ATTRIBUTE = "o_material_displacement"
|
|
BACKGROUND_DEPTH_ATTRIBUTE = "o_background_depth"
|
|
DEPTH_SCALE_ATTRIBUTE = "o_depth_scale"
|
|
|
|
|
|
def build_material_layer_group(depth_plane=False):
|
|
group_name = (
|
|
DEPTH_MATERIAL_NODE_GROUP_NAME
|
|
if depth_plane
|
|
else 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"),
|
|
]
|
|
normal_group = not depth_plane
|
|
if normal_group:
|
|
input_sockets.insert(2, ("Normal", "NodeSocketColor"))
|
|
input_sockets.insert(3, ("Normal Scale", "NodeSocketFloat"))
|
|
input_sockets.insert(4, ("Object Space", "NodeSocketBool"))
|
|
if depth_plane:
|
|
input_sockets.insert(2, ("Depth", "NodeSocketFloat"))
|
|
for name, socket_type in input_sockets:
|
|
socket = interface.new_socket(
|
|
name=name,
|
|
in_out="INPUT",
|
|
socket_type=socket_type,
|
|
)
|
|
if name == "Alpha":
|
|
socket.default_value = 1.0
|
|
socket.min_value = 0.0
|
|
socket.max_value = 1.0
|
|
socket.subtype = "FACTOR"
|
|
elif name == "Bump Scale":
|
|
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.default_value = 1.0
|
|
socket.min_value = 0.0
|
|
socket.max_value = 10.0
|
|
elif name == "Object Space":
|
|
socket.default_value = False
|
|
output_sockets = [
|
|
("Color", "NodeSocketColor"),
|
|
("Roughness", "NodeSocketFloat"),
|
|
("Alpha", "NodeSocketFloat"),
|
|
("Normal", "NodeSocketVector"),
|
|
]
|
|
if depth_plane:
|
|
output_sockets.append(("Displacement", "NodeSocketVector"))
|
|
for name, socket_type in output_sockets:
|
|
interface.new_socket(
|
|
name=name,
|
|
in_out="OUTPUT",
|
|
socket_type=socket_type,
|
|
)
|
|
|
|
nodes = group.nodes
|
|
links = group.links
|
|
group_input = nodes.new("NodeGroupInput")
|
|
group_input.location = (-500.0, 100.0)
|
|
group_output = nodes.new("NodeGroupOutput")
|
|
group_output.location = (300.0, 100.0)
|
|
roughness_curve = nodes.new("ShaderNodeFloatCurve")
|
|
roughness_curve.mapping.initialize()
|
|
curve_points = roughness_curve.mapping.curves[0].points
|
|
curve_points[0].location = (0.0, 0.5)
|
|
curve_points[-1].location = (1.0, 0.1)
|
|
roughness_curve.mapping.update()
|
|
roughness_curve.location = (-250.0, 350.0)
|
|
bump = nodes.new("ShaderNodeBump")
|
|
bump.location = (100.0, 0.0)
|
|
if normal_group:
|
|
tangent_normal = nodes.new("ShaderNodeNormalMap")
|
|
tangent_normal.space = "TANGENT"
|
|
tangent_normal.location = (-500.0, -100.0)
|
|
object_normal = nodes.new("ShaderNodeNormalMap")
|
|
object_normal.space = "OBJECT"
|
|
object_normal.location = (-500.0, -350.0)
|
|
choose_normal = nodes.new("ShaderNodeMix")
|
|
choose_normal.data_type = "VECTOR"
|
|
choose_normal.location = (-150.0, -150.0)
|
|
for normal_map in (tangent_normal, object_normal):
|
|
links.new(group_input.outputs["Normal"], normal_map.inputs["Color"])
|
|
links.new(
|
|
group_input.outputs["Normal Scale"],
|
|
normal_map.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"])
|
|
displacement = None
|
|
|
|
processed_alpha = group_input.outputs["Alpha"]
|
|
bump_depth = group_input.outputs["Color"]
|
|
if depth_plane:
|
|
displacement = nodes.new("ShaderNodeDisplacement")
|
|
displacement.inputs["Midlevel"].default_value = 0.0
|
|
displacement.inputs["Scale"].default_value = 1.0
|
|
group_input.location = (-1200.0, 100.0)
|
|
group_output.location = (1050.0, 100.0)
|
|
roughness_curve.location = (-300.0, 450.0)
|
|
bump.location = (750.0, 50.0)
|
|
displacement.location = (750.0, -300.0)
|
|
background_attribute = nodes.new("ShaderNodeAttribute")
|
|
background_attribute.attribute_type = "GEOMETRY"
|
|
background_attribute.attribute_name = BACKGROUND_DEPTH_ATTRIBUTE
|
|
background_attribute.location = (-950.0, -500.0)
|
|
material_attribute = nodes.new("ShaderNodeAttribute")
|
|
material_attribute.attribute_type = "GEOMETRY"
|
|
material_attribute.attribute_name = MATERIAL_DISPLACEMENT_ATTRIBUTE
|
|
material_attribute.location = (-50.0, -650.0)
|
|
strength_attribute = nodes.new("ShaderNodeAttribute")
|
|
strength_attribute.attribute_type = "GEOMETRY"
|
|
strength_attribute.attribute_name = DEPTH_SCALE_ATTRIBUTE
|
|
strength_attribute.location = (200.0, -650.0)
|
|
foreground_mask = nodes.new("ShaderNodeMath")
|
|
foreground_mask.operation = "GREATER_THAN"
|
|
foreground_mask.inputs[1].default_value = 0.5
|
|
foreground_mask.location = (-950.0, -250.0)
|
|
clamp_foreground = nodes.new("ShaderNodeMath")
|
|
clamp_foreground.operation = "MAXIMUM"
|
|
clamp_foreground.location = (-650.0, -400.0)
|
|
masked_depth = nodes.new("ShaderNodeMix")
|
|
masked_depth.data_type = "FLOAT"
|
|
masked_depth.location = (-350.0, -350.0)
|
|
enabled_depth = nodes.new("ShaderNodeMath")
|
|
enabled_depth.operation = "MULTIPLY"
|
|
enabled_depth.location = (250.0, -350.0)
|
|
scaled_depth = nodes.new("ShaderNodeMath")
|
|
scaled_depth.operation = "MULTIPLY"
|
|
scaled_depth.location = (450.0, -350.0)
|
|
links.new(group_input.outputs["Alpha"], foreground_mask.inputs[0])
|
|
links.new(group_input.outputs["Depth"], clamp_foreground.inputs[0])
|
|
links.new(background_attribute.outputs["Fac"], clamp_foreground.inputs[1])
|
|
links.new(foreground_mask.outputs[0], masked_depth.inputs[0])
|
|
links.new(background_attribute.outputs["Fac"], masked_depth.inputs[2])
|
|
links.new(clamp_foreground.outputs[0], masked_depth.inputs[3])
|
|
links.new(masked_depth.outputs["Result"], enabled_depth.inputs[0])
|
|
links.new(material_attribute.outputs["Fac"], enabled_depth.inputs[1])
|
|
links.new(enabled_depth.outputs[0], scaled_depth.inputs[0])
|
|
links.new(strength_attribute.outputs["Fac"], scaled_depth.inputs[1])
|
|
processed_alpha = foreground_mask.outputs[0]
|
|
processed_depth = scaled_depth.outputs[0]
|
|
|
|
links.new(group_input.outputs["Color"], group_output.inputs["Color"])
|
|
links.new(group_input.outputs["Color"], roughness_curve.inputs["Value"])
|
|
links.new(
|
|
roughness_curve.outputs["Value"],
|
|
group_output.inputs["Roughness"],
|
|
)
|
|
links.new(processed_alpha, group_output.inputs["Alpha"])
|
|
links.new(bump_depth, bump.inputs["Height"])
|
|
bump_distance = nodes.new("ShaderNodeMath")
|
|
bump_distance.operation = "MULTIPLY"
|
|
bump_distance.inputs[1].default_value = BUMP_BASE_DISTANCE
|
|
bump_distance.location = (500.0, 100.0) if depth_plane else (-250.0, 150.0)
|
|
links.new(group_input.outputs["Bump Scale"], bump_distance.inputs[0])
|
|
links.new(bump_distance.outputs[0], bump.inputs["Distance"])
|
|
links.new(bump.outputs["Normal"], group_output.inputs["Normal"])
|
|
if displacement is not None:
|
|
links.new(processed_depth, displacement.inputs["Height"])
|
|
links.new(
|
|
displacement.outputs["Displacement"],
|
|
group_output.inputs["Displacement"],
|
|
)
|
|
group.color_tag = "SHADER"
|
|
group.description = (
|
|
"Prepare image depth material outputs."
|
|
if depth_plane
|
|
else f"Prepare {group_name} material outputs."
|
|
)
|
|
group.use_fake_user = True
|
|
return group
|