postterize, gitch, etc
This commit is contained in:
+292
-60
@@ -44,6 +44,7 @@ class FlexImagePosterize(FlexImageBase):
|
||||
"dither_strength": ("FLOAT", {"default": 0.3, "min": 0.0, "max": 1.0, "step": 0.01}),
|
||||
"channel_separation": ("FLOAT", {"default": 0.2, "min": 0.0, "max": 1.0, "step": 0.01}),
|
||||
"gamma": ("FLOAT", {"default": 1.2, "min": 0.1, "max": 2.2, "step": 0.1}),
|
||||
"dither_method": (["ordered", "floyd", "none"], {"default": "ordered"}),
|
||||
})
|
||||
return base_inputs
|
||||
|
||||
@@ -52,41 +53,59 @@ class FlexImagePosterize(FlexImageBase):
|
||||
return ["max_levels", "dither_strength", "channel_separation", "gamma", "None"]
|
||||
|
||||
def apply_effect_internal(self, image: np.ndarray, max_levels: int, dither_strength: float,
|
||||
channel_separation: float, gamma: float, **kwargs) -> np.ndarray:
|
||||
channel_separation: float, gamma: float, dither_method: str, **kwargs) -> np.ndarray:
|
||||
# Apply gamma correction
|
||||
image_gamma = np.power(image, gamma)
|
||||
|
||||
# Convert image to uint8
|
||||
image_uint8 = (image_gamma * 255).astype(np.uint8)
|
||||
# Initialize result array
|
||||
result = np.zeros_like(image_gamma)
|
||||
|
||||
posterized = np.zeros_like(image_uint8)
|
||||
# Create ordered dither pattern if needed
|
||||
if dither_method == "ordered":
|
||||
bayer_pattern = np.array([[0, 8, 2, 10],
|
||||
[12, 4, 14, 6],
|
||||
[3, 11, 1, 9],
|
||||
[15, 7, 13, 5]], dtype=np.float32) / 16.0
|
||||
h, w = image.shape[:2]
|
||||
# Tile the pattern to match image size
|
||||
pattern_h = (h + 3) // 4
|
||||
pattern_w = (w + 3) // 4
|
||||
dither_pattern = np.tile(bayer_pattern, (pattern_h, pattern_w))[:h, :w]
|
||||
dither_pattern = dither_pattern[..., np.newaxis] * dither_strength
|
||||
|
||||
for c in range(3): # RGB channels
|
||||
# Calculate levels for each channel
|
||||
# Calculate levels for each channel with separation
|
||||
channel_levels = int(np.clip(2 + (max_levels - 2) * (1 + channel_separation * (c - 1)), 2, max_levels))
|
||||
|
||||
# Posterize
|
||||
div = 256 // channel_levels
|
||||
posterized[:,:,c] = (image_uint8[:,:,c] // div) * div
|
||||
# Get current channel
|
||||
channel = image_gamma[..., c]
|
||||
|
||||
if dither_method == "ordered":
|
||||
# Add ordered dither pattern
|
||||
channel = np.clip(channel + dither_pattern[..., 0] - 0.5 * dither_strength, 0, 1)
|
||||
|
||||
# Quantize
|
||||
scale = (channel_levels - 1)
|
||||
quantized = np.round(channel * scale) / scale
|
||||
|
||||
if dither_method == "floyd":
|
||||
# Efficient Floyd-Steinberg dithering using convolution
|
||||
error = (channel - quantized) * dither_strength
|
||||
|
||||
# Prepare error diffusion kernel
|
||||
kernel = np.array([[0, 0, 0],
|
||||
[0, 0, 7/16],
|
||||
[3/16, 5/16, 1/16]])
|
||||
|
||||
# Apply error diffusion
|
||||
from scipy.signal import convolve2d
|
||||
error_diffused = convolve2d(error, kernel, mode='same')
|
||||
quantized = np.clip(quantized + error_diffused, 0, 1)
|
||||
|
||||
result[..., c] = quantized
|
||||
|
||||
if dither_strength > 0:
|
||||
# Apply Floyd-Steinberg dithering with adjustable strength
|
||||
error = (image_uint8.astype(np.float32) - posterized) * dither_strength
|
||||
h, w = image.shape[:2]
|
||||
for c in range(3): # RGB channels
|
||||
for i in range(h - 1):
|
||||
for j in range(w - 1):
|
||||
old_pixel = posterized[i, j, c]
|
||||
new_pixel = np.clip(old_pixel + error[i, j, c], 0, 255)
|
||||
quant_error = old_pixel - new_pixel
|
||||
posterized[i, j, c] = new_pixel
|
||||
error[i, j+1, c] += quant_error * 7 / 16
|
||||
error[i+1, j-1, c] += quant_error * 3 / 16
|
||||
error[i+1, j, c] += quant_error * 5 / 16
|
||||
error[i+1, j+1, c] += quant_error * 1 / 16
|
||||
|
||||
# Convert back to float32 and apply gamma correction
|
||||
return np.power(posterized.astype(np.float32) / 255.0, 1/gamma)
|
||||
# Apply inverse gamma correction
|
||||
return np.power(result, 1/gamma)
|
||||
|
||||
@apply_tooltips
|
||||
class FlexImageKaleidoscope(FlexImageBase):
|
||||
@@ -197,35 +216,151 @@ class FlexImageGlitch(FlexImageBase):
|
||||
def INPUT_TYPES(cls):
|
||||
base_inputs = super().INPUT_TYPES()
|
||||
base_inputs["required"].update({
|
||||
"shift_amount": ("FLOAT", {"default": 0.1, "min": 0.0, "max": 1.0, "step": 0.01}),
|
||||
"scan_lines": ("INT", {"default": 10, "min": 0, "max": 100, "step": 1}),
|
||||
"color_shift": ("FLOAT", {"default": 0.1, "min": 0.0, "max": 1.0, "step": 0.01}),
|
||||
"glitch_type": (["digital", "analog", "compression", "wave", "corrupt"], {"default": "digital"}),
|
||||
"intensity": ("FLOAT", {"default": 0.5, "min": 0.0, "max": 1.0, "step": 0.01}),
|
||||
"block_size": ("INT", {"default": 32, "min": 8, "max": 128, "step": 8}),
|
||||
"wave_amplitude": ("FLOAT", {"default": 0.1, "min": 0.0, "max": 1.0, "step": 0.01}),
|
||||
"wave_frequency": ("FLOAT", {"default": 5.0, "min": 0.1, "max": 20.0, "step": 0.1}),
|
||||
"corruption_amount": ("FLOAT", {"default": 0.1, "min": 0.0, "max": 1.0, "step": 0.01}),
|
||||
"time_seed": ("INT", {"default": 0, "min": 0, "max": 10000, "step": 1}),
|
||||
})
|
||||
return base_inputs
|
||||
|
||||
@classmethod
|
||||
def get_modifiable_params(cls):
|
||||
return ["shift_amount", "scan_lines", "color_shift", "None"]
|
||||
return ["intensity", "block_size", "wave_amplitude", "wave_frequency", "corruption_amount", "time_seed", "None"]
|
||||
|
||||
def apply_effect_internal(self, image: np.ndarray, shift_amount: float, scan_lines: int, color_shift: float, **kwargs) -> np.ndarray:
|
||||
def apply_effect_internal(self, image: np.ndarray, glitch_type: str, intensity: float,
|
||||
block_size: int, wave_amplitude: float, wave_frequency: float,
|
||||
corruption_amount: float, time_seed: int, **kwargs) -> np.ndarray:
|
||||
h, w = image.shape[:2]
|
||||
|
||||
# Apply horizontal shift
|
||||
shift = int(w * shift_amount)
|
||||
result = np.roll(image, shift, axis=1)
|
||||
|
||||
# Add scan lines
|
||||
if scan_lines > 0:
|
||||
scan_line_mask = np.zeros((h, w))
|
||||
scan_line_mask[::scan_lines] = 1
|
||||
result = result * (1 - scan_line_mask)[:,:,np.newaxis] + scan_line_mask[:,:,np.newaxis]
|
||||
|
||||
# Apply color channel shift
|
||||
if color_shift > 0:
|
||||
color_shift_amount = int(w * color_shift)
|
||||
result[:,:,0] = np.roll(result[:,:,0], color_shift_amount, axis=1)
|
||||
result[:,:,2] = np.roll(result[:,:,2], -color_shift_amount, axis=1)
|
||||
|
||||
|
||||
# Add smart padding - using reflection for most natural look
|
||||
pad_size = max(int(min(h, w) * 0.1), 32) # At least 32 pixels or 10% of size
|
||||
padded = cv2.copyMakeBorder(image, pad_size, pad_size, pad_size, pad_size,
|
||||
cv2.BORDER_REFLECT_101)
|
||||
|
||||
ph, pw = padded.shape[:2]
|
||||
result = padded.copy()
|
||||
|
||||
# Set random seed for reproducibility
|
||||
np.random.seed(time_seed)
|
||||
|
||||
# Apply effects as before, but now working with padded image
|
||||
if glitch_type == "digital":
|
||||
for c in range(3):
|
||||
shift_x = int(pw * intensity * np.random.uniform(-0.1, 0.1))
|
||||
shift_y = int(ph * intensity * np.random.uniform(-0.1, 0.1))
|
||||
result[..., c] = np.roll(np.roll(padded[..., c], shift_x, axis=1), shift_y, axis=0)
|
||||
|
||||
num_blocks = int(intensity * 10)
|
||||
for _ in range(num_blocks):
|
||||
x = np.random.randint(0, pw - block_size)
|
||||
y = np.random.randint(0, ph - block_size)
|
||||
block = result[y:y+block_size, x:x+block_size].copy()
|
||||
|
||||
shift_x = int(block_size * np.random.uniform(-1, 1))
|
||||
shift_y = int(block_size * np.random.uniform(-1, 1))
|
||||
|
||||
new_x = np.clip(x + shift_x, 0, pw - block_size)
|
||||
new_y = np.clip(y + shift_y, 0, ph - block_size)
|
||||
|
||||
result[new_y:new_y+block_size, new_x:new_x+block_size] = block
|
||||
|
||||
elif glitch_type == "compression":
|
||||
# Simulate JPEG compression artifacts
|
||||
num_blocks_y = ph // block_size
|
||||
num_blocks_x = pw // block_size
|
||||
|
||||
for by in range(num_blocks_y):
|
||||
for bx in range(num_blocks_x):
|
||||
y1, y2 = by * block_size, (by + 1) * block_size
|
||||
x1, x2 = bx * block_size, (bx + 1) * block_size
|
||||
|
||||
# Random block corruption
|
||||
if np.random.random() < corruption_amount:
|
||||
# Quantization effect
|
||||
block = result[y1:y2, x1:x2]
|
||||
# Simulate DCT quantization
|
||||
quant_level = int(8 * intensity)
|
||||
block = (block * quant_level).astype(int) / quant_level
|
||||
# Add blocking artifacts
|
||||
block += np.random.uniform(-0.1, 0.1, block.shape) * intensity
|
||||
result[y1:y2, x1:x2] = block
|
||||
|
||||
elif glitch_type == "wave":
|
||||
y_coords, x_coords = np.mgrid[0:ph, 0:pw]
|
||||
|
||||
for i in range(3):
|
||||
freq = wave_frequency * (i + 1)
|
||||
amp = wave_amplitude / (i + 1)
|
||||
|
||||
x_offset = amp * pw * np.sin(2 * np.pi * y_coords / ph * freq + time_seed * 0.1)
|
||||
y_offset = amp * ph * np.cos(2 * np.pi * x_coords / pw * freq + time_seed * 0.1)
|
||||
|
||||
x_map = (x_coords + x_offset * intensity).astype(np.float32)
|
||||
y_map = (y_coords + y_offset * intensity).astype(np.float32)
|
||||
|
||||
x_map = np.clip(x_map, 0, pw-1)
|
||||
y_map = np.clip(y_map, 0, ph-1)
|
||||
|
||||
result = cv2.remap(result, x_map, y_map, cv2.INTER_LINEAR)
|
||||
|
||||
elif glitch_type == "corrupt":
|
||||
# Data corruption simulation
|
||||
for _ in range(int(corruption_amount * 20)):
|
||||
# Random line corruption
|
||||
if np.random.random() < 0.5:
|
||||
y = np.random.randint(0, ph)
|
||||
length = int(pw * np.random.uniform(0.1, 0.5))
|
||||
start = np.random.randint(0, pw - length)
|
||||
|
||||
# Corrupt line with various effects
|
||||
corrupt_type = np.random.choice(['repeat', 'shift', 'noise'])
|
||||
if corrupt_type == 'repeat':
|
||||
result[y, start:start+length] = result[y, start]
|
||||
elif corrupt_type == 'shift':
|
||||
shift = np.random.randint(-50, 50)
|
||||
result[y, start:start+length] = np.roll(result[y, start:start+length], shift, axis=0)
|
||||
else: # noise
|
||||
result[y, start:start+length] = np.random.random((length, 3))
|
||||
|
||||
# Block corruption
|
||||
else:
|
||||
y = np.random.randint(0, ph - block_size)
|
||||
x = np.random.randint(0, pw - block_size)
|
||||
|
||||
# Different corruption patterns
|
||||
pattern = np.random.choice(['solid', 'noise', 'repeat'])
|
||||
if pattern == 'solid':
|
||||
result[y:y+block_size, x:x+block_size] = np.random.random(3)
|
||||
elif pattern == 'noise':
|
||||
result[y:y+block_size, x:x+block_size] = np.random.random((block_size, block_size, 3))
|
||||
else: # repeat
|
||||
result[y:y+block_size, x:x+block_size] = result[y, x]
|
||||
|
||||
elif glitch_type == "analog":
|
||||
# Simulate analog TV distortion
|
||||
# Add scan lines
|
||||
scan_lines = np.ones((ph, pw, 3))
|
||||
scan_lines[::2] *= 0.8
|
||||
result *= scan_lines
|
||||
|
||||
# Add noise
|
||||
noise = np.random.normal(0, 0.1 * intensity, (ph, pw, 3))
|
||||
result += noise
|
||||
|
||||
# Add vertical hold distortion
|
||||
hold_offset = int(ph * 0.1 * intensity * np.sin(time_seed * 0.1))
|
||||
result = np.roll(result, hold_offset, axis=0)
|
||||
|
||||
# Add ghosting
|
||||
ghost = np.roll(result, int(pw * 0.05 * intensity), axis=1) * 0.3
|
||||
result = result * 0.7 + ghost
|
||||
|
||||
# Extract the center portion (removing padding)
|
||||
result = result[pad_size:pad_size+h, pad_size:pad_size+w]
|
||||
|
||||
return np.clip(result, 0, 1)
|
||||
|
||||
@apply_tooltips
|
||||
@@ -301,26 +436,123 @@ class FlexImageBloom(FlexImageBase):
|
||||
"threshold": ("FLOAT", {"default": 0.7, "min": 0.0, "max": 1.0, "step": 0.01}),
|
||||
"blur_amount": ("FLOAT", {"default": 10.0, "min": 0.0, "max": 50.0, "step": 0.1}),
|
||||
"intensity": ("FLOAT", {"default": 0.5, "min": 0.0, "max": 1.0, "step": 0.01}),
|
||||
"num_passes": ("INT", {"default": 4, "min": 1, "max": 8, "step": 1}),
|
||||
"color_bleeding": ("FLOAT", {"default": 0.2, "min": 0.0, "max": 1.0, "step": 0.01}),
|
||||
"falloff": ("FLOAT", {"default": 1.2, "min": 0.1, "max": 3.0, "step": 0.1}),
|
||||
"surface_scatter": ("FLOAT", {"default": 0.5, "min": 0.0, "max": 1.0, "step": 0.01}),
|
||||
"normal_influence": ("FLOAT", {"default": 0.7, "min": 0.0, "max": 1.0, "step": 0.01}),
|
||||
})
|
||||
base_inputs["optional"].update({
|
||||
"opt_normal_map": ("IMAGE",),
|
||||
})
|
||||
return base_inputs
|
||||
|
||||
@classmethod
|
||||
def get_modifiable_params(cls):
|
||||
return ["threshold", "blur_amount", "intensity", "None"]
|
||||
return ["threshold", "blur_amount", "intensity", "num_passes", "color_bleeding", "falloff", "surface_scatter", "normal_influence", "None"]
|
||||
|
||||
def apply_effect_internal(self, image: np.ndarray, threshold: float, blur_amount: float,
|
||||
intensity: float, **kwargs) -> np.ndarray:
|
||||
# Extract bright areas
|
||||
bright_areas = np.maximum(image - threshold, 0) / (1 - threshold)
|
||||
|
||||
# Apply gaussian blur
|
||||
blurred = gaussian_filter(bright_areas, sigma=blur_amount)
|
||||
|
||||
# Combine with original image
|
||||
result = image + blurred * intensity
|
||||
|
||||
intensity: float, num_passes: int, color_bleeding: float,
|
||||
falloff: float, surface_scatter: float, normal_influence: float,
|
||||
opt_normal_map: np.ndarray = None, **kwargs) -> np.ndarray:
|
||||
# Convert to float32 for better precision
|
||||
image = image.astype(np.float32)
|
||||
|
||||
# Extract bright areas with smooth threshold
|
||||
brightness = np.max(image, axis=2)
|
||||
bright_mask = np.clip((brightness - threshold) / (1 - threshold), 0, 1)
|
||||
bright_mask = np.power(bright_mask, falloff)
|
||||
|
||||
# Initialize accumulator for bloom effect
|
||||
bloom_accumulator = np.zeros_like(image)
|
||||
|
||||
# Process normal map if provided
|
||||
if opt_normal_map is not None:
|
||||
# Extract the normal map for the current frame and ensure it's numpy
|
||||
if torch.is_tensor(opt_normal_map):
|
||||
opt_normal_map = opt_normal_map.cpu().numpy()
|
||||
|
||||
if len(opt_normal_map.shape) > 3: # If batched
|
||||
frame_index = kwargs.get('frame_index', 0)
|
||||
opt_normal_map = opt_normal_map[frame_index]
|
||||
|
||||
# Resize normal map to match image dimensions if needed
|
||||
h, w = image.shape[:2]
|
||||
if opt_normal_map.shape[:2] != (h, w):
|
||||
opt_normal_map = cv2.resize(opt_normal_map, (w, h), interpolation=cv2.INTER_LINEAR)
|
||||
|
||||
# Convert normal map from [0,1] to [-1,1] range
|
||||
normals = opt_normal_map * 2.0 - 1.0
|
||||
|
||||
# Calculate surface alignment factors using numpy operations
|
||||
view_vector = np.array([0, 0, 1])
|
||||
surface_alignment = np.zeros((h, w), dtype=np.float32)
|
||||
for i in range(3): # Manual dot product
|
||||
surface_alignment += normals[..., i] * view_vector[i]
|
||||
surface_alignment = (surface_alignment + 1) * 0.5 # Normalize to [0,1]
|
||||
|
||||
# Create directional kernels based on surface normals
|
||||
y_grad, x_grad = np.gradient(surface_alignment)
|
||||
angles = np.arctan2(y_grad, x_grad)
|
||||
strengths = np.sqrt(x_grad**2 + y_grad**2)
|
||||
|
||||
# Multi-pass gaussian blur at different scales
|
||||
for i in range(num_passes):
|
||||
# Calculate sigma for this pass (increasing with each pass)
|
||||
current_sigma = blur_amount * (1 + i)
|
||||
|
||||
# Extract color information with color bleeding control
|
||||
color_contribution = image * (1 - color_bleeding) + np.mean(image, axis=2, keepdims=True) * color_bleeding
|
||||
|
||||
# Apply bright mask with pass-specific weighting
|
||||
pass_contribution = color_contribution * bright_mask[..., np.newaxis]
|
||||
|
||||
if opt_normal_map is not None:
|
||||
# Apply anisotropic gaussian blur based on surface normals
|
||||
for c in range(3):
|
||||
channel = pass_contribution[..., c]
|
||||
|
||||
# Calculate directional sigmas based on surface normals
|
||||
sigma_x = current_sigma * (1 + strengths * surface_scatter * np.cos(angles))
|
||||
sigma_y = current_sigma * (1 + strengths * surface_scatter * np.sin(angles))
|
||||
|
||||
# Blend between directional and uniform blur based on normal influence
|
||||
uniform_blur = gaussian_filter(channel, sigma=current_sigma)
|
||||
directional_blur = np.zeros_like(channel)
|
||||
|
||||
# Apply directional blur
|
||||
for angle in np.linspace(0, np.pi, 4): # Sample 4 directions
|
||||
rot_sigma_x = sigma_x * np.cos(angle) - sigma_y * np.sin(angle)
|
||||
rot_sigma_y = sigma_x * np.sin(angle) + sigma_y * np.cos(angle)
|
||||
# Take mean of sigma values for each direction
|
||||
sigma_y_mean = np.mean(np.abs(rot_sigma_y))
|
||||
sigma_x_mean = np.mean(np.abs(rot_sigma_x))
|
||||
directional_blur += gaussian_filter(channel, sigma=[sigma_y_mean, sigma_x_mean])
|
||||
|
||||
directional_blur /= 4 # Average the directional samples
|
||||
|
||||
# Blend between uniform and directional blur
|
||||
blurred_channel = uniform_blur * (1 - normal_influence) + directional_blur * normal_influence
|
||||
pass_contribution[..., c] = blurred_channel
|
||||
|
||||
# Modulate blur by surface alignment
|
||||
pass_contribution *= (1 - surface_alignment[..., np.newaxis] * surface_scatter)
|
||||
else:
|
||||
# Regular gaussian blur if no normal map
|
||||
pass_contribution = gaussian_filter(pass_contribution, sigma=[current_sigma, current_sigma, 0])
|
||||
|
||||
# Weight contribution based on pass number (later passes contribute less)
|
||||
pass_weight = 1.0 / (2 ** i)
|
||||
bloom_accumulator += pass_contribution * pass_weight
|
||||
|
||||
# Normalize accumulator
|
||||
bloom_accumulator /= np.sum([1.0 / (2 ** i) for i in range(num_passes)])
|
||||
|
||||
# Combine with original image using intensity
|
||||
result = image + bloom_accumulator * intensity
|
||||
|
||||
return np.clip(result, 0, 1)
|
||||
|
||||
|
||||
@apply_tooltips
|
||||
class FlexImageTiltShift(FlexImageBase):
|
||||
@classmethod
|
||||
|
||||
@@ -28,10 +28,14 @@ Each node type has different parameters that can be modulated:
|
||||
|
||||
# FlexImagePosterize tooltips (inherits from: FlexImageBase)
|
||||
TooltipManager.register_tooltips("FlexImagePosterize", {
|
||||
"max_levels": "Maximum number of color levels per channel (2 to 256)",
|
||||
"dither_strength": "Intensity of dithering effect (0.0 to 1.0)",
|
||||
"channel_separation": "Degree of separation between color channels (0.0 to 1.0)",
|
||||
"gamma": "Gamma correction applied before posterization (0.1 to 2.2)",
|
||||
"max_levels": "Maximum number of color levels per channel. Higher values preserve more color detail (2 to 256)",
|
||||
"dither_strength": "Intensity of dithering effect. Higher values reduce color banding (0.0 to 1.0)",
|
||||
"channel_separation": "Degree of separation between color channels. Creates color shift effects (0.0 to 1.0)",
|
||||
"gamma": "Gamma correction applied before posterization. Affects brightness distribution (0.1 to 2.2)",
|
||||
"dither_method": """Method used for dithering:
|
||||
- ordered: Fast Bayer matrix dithering, good for retro effects
|
||||
- floyd: Floyd-Steinberg dithering, better for natural gradients
|
||||
- none: No dithering, creates sharp color transitions""",
|
||||
"feature_param": """Choose which parameter to modulate:
|
||||
|
||||
- max_levels: Dynamically adjust color quantization
|
||||
@@ -74,31 +78,43 @@ Each node type has different parameters that can be modulated:
|
||||
|
||||
# FlexImageGlitch tooltips (inherits from: FlexImageBase)
|
||||
TooltipManager.register_tooltips("FlexImageGlitch", {
|
||||
"shift_amount": "Magnitude of horizontal shift (0.0 to 1.0)",
|
||||
"scan_lines": "Number of scan lines to add (0 to 100)",
|
||||
"color_shift": "Amount of color channel separation (0.0 to 1.0)",
|
||||
"glitch_type": """Type of glitch effect to apply:
|
||||
- digital: RGB channel shifts and block displacement
|
||||
- analog: Classic TV distortion with scan lines and ghosting
|
||||
- compression: JPEG-like block artifacts
|
||||
- wave: Smooth wave distortions
|
||||
- corrupt: Random data corruption effects""",
|
||||
"intensity": "Overall strength of the glitch effect (0.0 to 1.0)",
|
||||
"block_size": "Size of blocks for digital glitches and compression artifacts (8 to 128)",
|
||||
"wave_amplitude": "Height of wave distortions (0.0 to 1.0)",
|
||||
"wave_frequency": "Frequency of wave patterns (0.1 to 20.0)",
|
||||
"corruption_amount": "Probability and intensity of corruption artifacts (0.0 to 1.0)",
|
||||
"time_seed": "Seed for random glitch generation. Same seed produces same pattern (0 to 10000)",
|
||||
"feature_param": """Choose which parameter to modulate:
|
||||
|
||||
- shift_amount: Dynamically adjust glitch displacement
|
||||
- scan_lines: Dynamically adjust line density
|
||||
- color_shift: Dynamically adjust color separation
|
||||
- intensity: Dynamically adjust effect strength
|
||||
- block_size: Dynamically adjust artifact size
|
||||
- wave_amplitude: Dynamically adjust wave height
|
||||
- wave_frequency: Dynamically adjust wave frequency
|
||||
- corruption_amount: Dynamically adjust corruption intensity
|
||||
- time_seed: Dynamically change random patterns
|
||||
- None: No parameter modulation"""
|
||||
}, inherits_from='FlexImageBase')
|
||||
|
||||
# FlexImageChromaticAberration tooltips (inherits from: FlexImageBase)
|
||||
TooltipManager.register_tooltips("FlexImageChromaticAberration", {
|
||||
"shift_amount": "Magnitude of color channel shift (0.0 to 0.1)",
|
||||
"angle": "Angle of the shift effect (0.0 to 360.0)",
|
||||
"shift_amount": "Amount of RGB channel separation. Higher values create more color fringing (0.0 to 0.5)",
|
||||
"angle": "Direction of the chromatic aberration effect in degrees. 0° is horizontal, 90° is vertical (0.0 to 720.0)",
|
||||
"feature_param": """Choose which parameter to modulate:
|
||||
|
||||
- shift_amount: Dynamically adjust color separation amount
|
||||
- shift_amount: Dynamically adjust color separation
|
||||
- angle: Dynamically adjust separation direction
|
||||
- None: No parameter modulation"""
|
||||
}, inherits_from='FlexImageBase')
|
||||
|
||||
# FlexImagePixelate tooltips (inherits from: FlexImageBase)
|
||||
TooltipManager.register_tooltips("FlexImagePixelate", {
|
||||
"pixel_size": "Size of each pixelated block (1 to 100)",
|
||||
"pixel_size": "Size of each pixelated block. Larger values create more pronounced pixelation (1 to 100)",
|
||||
"feature_param": """Choose which parameter to modulate:
|
||||
|
||||
- pixel_size: Dynamically adjust pixelation size
|
||||
@@ -107,25 +123,36 @@ Each node type has different parameters that can be modulated:
|
||||
|
||||
# FlexImageBloom tooltips (inherits from: FlexImageBase)
|
||||
TooltipManager.register_tooltips("FlexImageBloom", {
|
||||
"threshold": "Brightness threshold for the bloom effect (0.0 to 1.0)",
|
||||
"blur_amount": "Amount of blur applied to the bloom (0.0 to 50.0)",
|
||||
"intensity": "Strength of the bloom effect (0.0 to 1.0)",
|
||||
"threshold": "Brightness threshold for bloom effect. Only pixels brighter than this value will glow (0.0 to 1.0)",
|
||||
"blur_amount": "Base radius of the bloom effect. Higher values create softer, more diffused glow (0.0 to 50.0)",
|
||||
"intensity": "Overall strength of the bloom effect. Controls the brightness of the glow (0.0 to 1.0)",
|
||||
"num_passes": "Number of bloom passes. More passes create layered, atmospheric glow effects (1 to 8)",
|
||||
"color_bleeding": "Amount of color spreading between bright areas. Higher values create more color mixing (0.0 to 1.0)",
|
||||
"falloff": "How quickly the bloom effect diminishes with distance. Higher values create tighter, more focused glow (0.1 to 3.0)",
|
||||
"surface_scatter": "How much the bloom follows surface geometry when using normal map. Affects glow distribution (0.0 to 1.0)",
|
||||
"normal_influence": "Strength of normal map's influence on bloom direction. Controls surface-aware lighting (0.0 to 1.0)",
|
||||
"opt_normal_map": "Optional normal map for surface-aware bloom. Enables realistic light scattering based on surface geometry",
|
||||
"feature_param": """Choose which parameter to modulate:
|
||||
|
||||
- threshold: Dynamically adjust glow threshold
|
||||
- blur_amount: Dynamically adjust glow spread
|
||||
- intensity: Dynamically adjust glow strength
|
||||
- threshold: Dynamically adjust bloom threshold
|
||||
- blur_amount: Dynamically adjust bloom radius
|
||||
- intensity: Dynamically adjust bloom strength
|
||||
- num_passes: Dynamically adjust bloom quality
|
||||
- color_bleeding: Dynamically adjust color spread
|
||||
- falloff: Dynamically adjust bloom falloff
|
||||
- surface_scatter: Dynamically adjust surface influence
|
||||
- normal_influence: Dynamically adjust normal map impact
|
||||
- None: No parameter modulation"""
|
||||
}, inherits_from='FlexImageBase')
|
||||
|
||||
# FlexImageTiltShift tooltips (inherits from: FlexImageBase)
|
||||
TooltipManager.register_tooltips("FlexImageTiltShift", {
|
||||
"blur_amount": "Strength of the blur effect (0.0 to 50.0)",
|
||||
"focus_position_x": "X-coordinate of the focus center (0.0 to 1.0)",
|
||||
"focus_position_y": "Y-coordinate of the focus center (0.0 to 1.0)",
|
||||
"focus_width": "Width of the focus area (0.0 to 1.0)",
|
||||
"focus_height": "Height of the focus area (0.0 to 1.0)",
|
||||
"focus_shape": "Shape of the focus area ('rectangle' or 'ellipse')",
|
||||
"blur_amount": "Amount of blur applied to out-of-focus areas (0.0 to 50.0)",
|
||||
"focus_position_x": "Horizontal position of focus center. 0 = left, 1 = right (0.0 to 1.0)",
|
||||
"focus_position_y": "Vertical position of focus center. 0 = top, 1 = bottom (0.0 to 1.0)",
|
||||
"focus_width": "Width of the focus area relative to image width (0.0 to 1.0)",
|
||||
"focus_height": "Height of the focus area relative to image height (0.0 to 1.0)",
|
||||
"focus_shape": "Shape of the focus area:\n- rectangle: Sharp-edged focus region\n- ellipse: Smooth oval focus region",
|
||||
"feature_param": """Choose which parameter to modulate:
|
||||
|
||||
- blur_amount: Dynamically adjust blur intensity
|
||||
|
||||
Reference in New Issue
Block a user