From b17498b9fa6d758ab239f23b163d3c622b597098 Mon Sep 17 00:00:00 2001 From: p-groarke Date: Sat, 10 Sep 2022 12:30:31 -0400 Subject: [PATCH] Organic Noise v2 (#2) * Organic Noise : Fix uv_scale default (is now 1). Add over-sampling blend. * Organic Noise : Add noise type fractal lines. Allow over-sampling different noise type. Over-sampling blends. * Organic Noise : Blend modes, more options, overall betterer. * wip : Colors, new modes, experiments. * Add GrayscaleToColor, extract library functions. * New build system, with include punching. * More colorize options and things. * Add lines and steps. Start presets. * Moar presets! * Add more entry to reduce tiling. More presets. * Organic Noise presets. Move colorize to experiments. --- FeaExperiments/ColorSpaceLAB.osl | 62 +++ FeaExperiments/Colorize.osl | 268 +++++++++ .../Curvalicious.osl | 0 LICENSE | 2 +- OrganicNoise.csv | 28 + OrganicNoise.osl | 517 ++++++++++++------ README.md | 16 +- build.hs | 110 ++++ fea.osl | 291 ++++++++++ zip_fea_osl.bat | 24 - 10 files changed, 1117 insertions(+), 201 deletions(-) create mode 100644 FeaExperiments/ColorSpaceLAB.osl create mode 100644 FeaExperiments/Colorize.osl rename Curvalicious.osl => FeaExperiments/Curvalicious.osl (100%) create mode 100644 OrganicNoise.csv create mode 100644 build.hs create mode 100644 fea.osl delete mode 100644 zip_fea_osl.bat diff --git a/FeaExperiments/ColorSpaceLAB.osl b/FeaExperiments/ColorSpaceLAB.osl new file mode 100644 index 0000000..638c470 --- /dev/null +++ b/FeaExperiments/ColorSpaceLAB.osl @@ -0,0 +1,62 @@ +// ColorSpace shader +// ColorSpace.osl, by Changsoo Eun, adapted by Zap Andersson, Philippe Groarke +// Modified: 2019-11-22 +// Copyright 2019 Autodesk Inc, All rights reserved. This file is licensed under Apache 2.0 license +// https://github.com/ADN-DevTech/3dsMax-OSL-Shaders/blob/master/LICENSE.txt + + + + +shader ColorSpace +[[ + string help = "Convert RGB / HSV / YIQ / XYZ / xyY / Lab", + string label = "Color Space Lab", + string category = "Math Color" +]] +( + string FromSpace = "hsv" + [[ string widget = "mapper", + string options = "rgb:rgb|hsv (hue, saturation, and value):hsv|hsl (hue, saturation, and lightness):hsl" + "|YIQ (for the NTSC television standard):YIQ|XYZ (CIE XYZ coordinates):XYZ|xyY (CIE xyY coordinates):xyY|Lab (CIE LAB coordinates):Lab", + string label = "From space:"]], + string ToSpace = "rgb" + [[ string widget = "mapper", + string options = "rgb:rgb|hsv (hue, saturation, and value):hsv|hsl (hue, saturation, and lightness):hsl" + "|YIQ (for the NTSC television standard):YIQ|XYZ (CIE XYZ coordinates):XYZ|xyY (CIE xyY coordinates):xyY|Lab (CIE LAB coordinates):Lab", + string label = "To space:"]], + + vector In = 0.0, + output color Out = 0.0 +) +{ + if (FromSpace == "Lab" && ToSpace == "Lab") { + Out = In; + return; + } + + if (FromSpace == "Lab") { + // Convert from LAB to XYZ manually, then use transformc. + float x, y, z; + y = In[0] * (1.0 / 116.0) + 16.0 / 116.0; + x = In[1] * (1.0 / 500.0) + y; + z = In[2] * (-1.0 / 200.0) + y; + + x = x > 6.0 / 29.0 ? x * x * x : x * (108.0 / 841.0) - 432.0 / 24389.0; + y = In[0] > 8.0 ? y * y * y : In[0] * (27.0 / 24389.0); + z = z > 6.0 / 29.0 ? z * z * z : z * (108.0 / 841.0) - 432.0 / 24389.0; + + Out = transformc("XYZ", ToSpace, color(x,y,z)); + return; + } + + if (ToSpace == "Lab") { + // Convert into XYZ first, then convert to LAB manually. + color xyz = transformc(FromSpace, "XYZ", In); + Out[0] = xyz[1] * 116.0 - 16.0; + Out[1] = (xyz[0] - xyz[1]) * 500.0; + Out[2] = (xyz[1] - xyz[2]) * 200.0; + return; + } + + Out = transformc(FromSpace , ToSpace, In); +} diff --git a/FeaExperiments/Colorize.osl b/FeaExperiments/Colorize.osl new file mode 100644 index 0000000..1a6be3a --- /dev/null +++ b/FeaExperiments/Colorize.osl @@ -0,0 +1,268 @@ +// Colorize - Various schemes to convert a float grayscale map to color. +// Colorize by Philippe Groarke +// Copyright 2022 Philippe Groarke, All rights reserved. This file is licensed under Apache 2.0 license +// https://github.com/ADN-DevTech/3dsMax-OSL-Shaders/blob/master/LICENSE.txt + +#include "D:\code\3dsmax-plugins\OSL\FeaOSL\fea.osl" + +#define degs30 0.0833333333333333 +#define degs60 0.1666666666666667 + +// Given a hue and how many levels to output, +// returns the closest multiple. +float flattenize(float hue, float start_hue, int levels) { + if (!levels) { + return hue; + } + + float multiple = 1.0 / float(levels); + return ceil(hue / multiple) * multiple; +} + +float flattenize(float hue, int levels) { + return flattenize(hue, 0.0, levels); +} + +// Returns a value between [0,1] the contour strength. +float contourize(float before_flatten, float after_flatten, float width, int soften, float soften_slope, int levels) { + if (!levels) { + return 0.0; + } + + if (after_flatten == 0.0 || after_flatten == 1.0) { + return 0.0; + } + + float multiple = 1.0 / float(levels); + float diff = after_flatten - before_flatten; + + if (diff > width && diff < multiple - width) { + return 0.0; + } + + if (!soften) { + return 1.0; + } + + // flip "inward" side + if (diff >= multiple - width) { + diff = multiple - diff; + } + + float rectify = 1.0 / width; + diff *= rectify; + + float edge0 = width * soften_slope; + float edge1 = 1.0 - width * soften_slope; + return 1.0 - smoothstep(edge0, edge1, diff); + // return 1.0 - fea_interp(soften_slope, diff); +} + +shader Colorize +[[ + string help = + "

Colorize

" + "Various schemes to convert a float grayscale map to color.\n" + "Expects values between [0, 1], clamps input." + , + string label = "Colorize" +]] +( + float In = 0 + [[ + string label = "In", + string help = "Input grayscale map, expects float.", + ]], + + int in_invert = 0 + [[ + string label = "Invert", + string help = "Flips the input values, 1 becomes 0, 0 becomes 1.", + int connectable = 0, + string widget = "checkBox", + ]], + + FEA_SPACER(0), + + int in_color_mode = 0 + [[ + string widget = "mapper", + string label = "Color Mode", + string options = + "Simple Hue:0" + "|Heat Map:1" + "|Bright Burn:2" + "|Complements:3" + "|Analogous:4" + "|Split-Complementary:5" + "|Triad:6" + "|Tetradic:7" + "|Square:8" + , + string help = "The output 'Color' algorithm to use.", + int connectable = 0, + string packName = "Color Mode / Hue", + ]], + + float in_hue = 0 + [[ + string label = "Hue", + string help = "Cycles through available hues.", + // float min = 0, + // float max = 1, + string packName = "Color Mode / Hue", + int widgetWidth = FEA_RPACK_W, + ]], + + int in_flat = 0 + [[ + string label = "Flatten", + string help = "Outputs 'hard' colors, without gradient.", + int connectable = 0, + string widget = "checkBox", + // string packName = "Num Flat Levels / Flatten", + // int widgetWidth = FEA_RPACK_W, + ]], + + int in_num_flat_lvls = 6 + [[ + string label = "Num Flat Levels", + string help = "The number of colors to use in 'flat mode'. More colors == more precision.", + int connectable = 0, + int min = 1, + // string packName = "Num Flat Levels / Flatten", + ]], + + FEA_SPACER(1), + + int in_contours = 0 + [[ + string label = "Contour Lines", + string help = "Draw contours / edges according to number of levels.", + int connectable = 0, + string widget = "checkBox", + string packName = "Contour Lines / Width", + ]], + + float in_contour_width = 1.0 + [[ + string label = "Contour Width", + string help = "The contour width.", + int connectable = 0, + float min = 0.0001, + string packName = "Contour Lines / Width", + int widgetWidth = FEA_RPACK_W, + ]], + + int in_smooth_contours = 0 + [[ + string label = "Soften Contour", + string help = "Soften edges of contour lines.", + int connectable = 0, + string widget = "checkBox", + string packName = "Soften Contours / Slope", + ]], + + float in_smooth_contour_slope = 1.0 + [[ + string label = "Soften Contour Slope", + string help = "The size of the softening gradient. Positive is exponential, negative is logarithmic and '0' is linear interpolation (none).", + int connectable = 0, + // float min = 0.0001, + string packName = "Soften Contours / Slope", + int widgetWidth = FEA_RPACK_W, + ]], + + output color Out = 0 +) +{ + float val = clamp(In, 0.0, 1.0); + val = in_invert ? 1.0 - val : val; + + // float hue = fmod(in_hue, 1.0); + float hue = in_hue; + float hsv_v = 1.0; + float before_flatten = val; + + int flatten_levels = in_num_flat_lvls; + int contour_levels = in_num_flat_lvls; + if (!in_flat) { + flatten_levels = 0; + } + if (!in_contours) { + contour_levels = 0; + } + + val = flattenize(val, flatten_levels); + + hsv_v = 1.0 - contourize(before_flatten, val, in_contour_width * 0.01, in_smooth_contours, in_smooth_contour_slope, contour_levels); + + // float multiple = 1.0 / float(flatten_levels); + // float diff = val - in_val; + + // if (val != 0.0 && val != 1.0 && (diff >= multiple - 0.01 || diff <= 0.01)) { + // if (diff >= multiple - 0.01) { + // diff = multiple - diff; + // } + // hsv_v = smoothstep(0.1, 0.9, diff * 100.0); + // } + + + color out_col; + if (in_color_mode == 0) { + // hsv + // float col = fmod(val + hue, 1); + float col = val + hue; + out_col = color("hsv", col, 1, hsv_v); + } else if (in_color_mode == 1) { + // heat map + float offset = 0.5 + degs60 + hue; + float col = offset + val * 0.5; + out_col = color("hsv", col, 1, hsv_v); + } else if (in_color_mode == 2) { + // bright burn + float g = 0.618033988749895; + float col = val * 0.569 + g + hue; + float s = 1.0 - val; + + s = flattenize(s, flatten_levels); + out_col = color("hsv", col, s, hsv_v); + } else if (in_color_mode == 3) { + // complements + color start_col = color("hsv", hue, 1, 1); + color end_col = color("hsv", 0.5 + hue, 1, 1); + out_col = mix(start_col, end_col, val); + } else if (in_color_mode == 4) { + // analogous + float col = fmod(val * degs60 + hue, 1) ; + out_col = color("hsv", col, 1, 1); + } else if (in_color_mode == 5) { + // split complementary + color c1 = color("hsv", hue, 1, 1); + color c2 = color("hsv", 0.5 + degs30 + hue, 1, 1); + color c3 = color("hsv", 0.5 - degs30 + hue, 1, 1); + out_col = fea_mix(c1, c2, c3, val); + } else if (in_color_mode == 6) { + // triad + color c1 = color("hsv", hue, 1, 1); + color c2 = color("hsv", 0.25 + degs30 + hue, 1, 1); + color c3 = color("hsv", 0.75 - degs30 + hue, 1, 1); + out_col = fea_mix(c1, c3, c2, val); + } else if (in_color_mode == 7) { + // tetradic + color c1 = color("hsv", (1 / 12.0) + hue, 1, 1); + color c2 = color("hsv", 0.5 - degs30 + hue, 1, 1); + color c3 = color("hsv", 0.5 + degs30 + hue, 1, 1); + color c4 = color("hsv", 1.0 - degs30 + hue, 1, 1); + out_col = fea_mix(c1, c4, c2, c3, val); + } else if (in_color_mode == 8) { + // square + color c1 = color("hsv", hue, 1, 1); + color c2 = color("hsv", 0.25 + hue, 1, 1); + color c3 = color("hsv", 0.5 + hue, 1, 1); + color c4 = color("hsv", 0.75 + hue, 1, 1); + out_col = fea_mix(c1, c3, c4, c2, val); + } + + Out = out_col; +} \ No newline at end of file diff --git a/Curvalicious.osl b/FeaExperiments/Curvalicious.osl similarity index 100% rename from Curvalicious.osl rename to FeaExperiments/Curvalicious.osl diff --git a/LICENSE b/LICENSE index 89bdde9..eeb41c4 100644 --- a/LICENSE +++ b/LICENSE @@ -186,7 +186,7 @@ same "printed page" as the copyright notice for easier identification within third-party archives. - Copyright 2021 Autodesk Inc + Copyright 2022 Philippe Groarke Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/OrganicNoise.csv b/OrganicNoise.csv new file mode 100644 index 0000000..26470fc --- /dev/null +++ b/OrganicNoise.csv @@ -0,0 +1,28 @@ +name,UUID,category,cmui_swatch,noise_type,noise_effect,line_mode,line_spacing,camo_mode,camo_angle,bubbles_mode,bubbles_strength,crease_mode,crease_angle,over_sampling,oversampling_strength,oversampling_noise_type,overampling_blend,girth_mode,girth_strength,girth_threshold,girth_threshold_strength,step_mode,step_amount,interpolate,clamp_out,sampling_dist,in_phase,in_perturb_phase +Defaults,OrgN-001,Noise,Swatch-Torus,0,0,0,1.0,0,0.0,0,1.0,0,0.0,0,1.0,-1,13,0,1.0,0,0.5,0,10,0.0,TRUE,1.0,0.0,FALSE +Abstract - Cross Contour,OrgN-021,Noise,Swatch-Torus,0,0,0,1.0,13,0.0,0,1.0,0,0.0,2,50.0,-1,23,0,1.0,0,0.5,5,10,0.0,TRUE,1.0,0.0,FALSE +Abstract - Cross Contour 2,OrgN-022,Noise,Swatch-Torus,0,0,0,1.0,13,0.0,0,1.0,23,0.0,2,50.0,-1,23,0,1.0,0,0.5,10,10,0.0,TRUE,1.0,0.0,FALSE +Abstract - Marbley,OrgN-003,Noise,Swatch-Torus,0,5,5,0.4,0,0.0,0,1.0,0,0.0,0,1.0,-1,13,0,1.0,0,0.5,0,10,0.0,TRUE,5.0,0.0,FALSE +Abstract - Portal To Hell,OrgN-006,Noise,Swatch-Torus,0,0,1,0.2,0,0.0,20,1.0,20,0.0,1,1.0,2,13,1,1.0,2,0.5,0,10,0.0,TRUE,1.0,0.0,FALSE +Abstract - Shooting Stars,OrgN-020,Noise,Swatch-Torus,0,0,0,1.0,0,0.0,13,1.0,0,0.0,2,5.0,-1,16,2,1.0,0,0.5,0,10,10.0,TRUE,0.1,0.0,FALSE +Abstract - Starry Night,OrgN-023,Noise,Swatch-Torus,0,0,0,1.0,23,0.0,28,1.0,0,0.0,1,20.0,-1,22,0,1.0,0,0.5,10,2,0.0,TRUE,9.0,0.0,FALSE +Caustics - Bright,OrgN-015,Noise,Swatch-Torus,0,0,0,1.0,0,0.0,22,1.0,22,0.0,2,1.0,-1,8,2,1.0,0,0.5,0,10,0.0,TRUE,1.0,0.0,FALSE +Caustics - Dark,OrgN-016,Noise,Swatch-Torus,0,0,0,1.0,0,0.0,0,1.0,0,0.0,2,1.0,-1,20,2,1.0,0,0.5,0,10,2.0,TRUE,1.0,0.0,FALSE +Caustics - Fractals,OrgN-017,Noise,Swatch-Torus,2,0,0,1.0,0,0.0,22,1.0,22,0.0,2,0.5,-1,8,2,1.0,0,0.5,0,10,2.0,TRUE,1.0,0.0,FALSE +Caustics - Oily,OrgN-018,Noise,Swatch-Torus,2,0,0,1.0,0,0.0,22,1.0,22,0.0,2,0.5,3,8,2,1.0,0,0.5,0,10,2.0,TRUE,1.0,0.0,FALSE +Caustics - Puddle,OrgN-026,Noise,Swatch-Torus,1,0,9,1.0,0,0.0,9,1.0,0,0.0,1,1.0,2,5,2,1.0,0,0.5,0,10,0.0,TRUE,1.0,0.0,TRUE +Cells,OrgN-009,Noise,Swatch-Torus,0,0,0,1.0,0,0.0,3,1.0,0,0.0,0,1.0,-1,13,0,1.0,0,0.5,0,10,0.0,TRUE,0.1,0.2,FALSE +Cells - Mask,OrgN-010,Noise,Swatch-Torus,2,0,0,1.0,0,0.0,19,1.0,0,0.0,0,1.0,-1,13,0,1.0,0,0.5,0,10,0.0,TRUE,0.1,0.2,FALSE +Fabric - Pinholes,OrgN-011,Noise,Swatch-Torus,1,0,0,1.0,0,0.0,4,0.8,0,0.0,2,1.5,-1,14,0,1.0,0,0.5,0,10,0.0,TRUE,0.1,0.0,FALSE +Fabric - Plastic Bumps,OrgN-008,Noise,Swatch-Torus,2,0,0,1.0,0,0.0,0,1.0,0,0.0,0,1.0,-1,13,2,2.0,0,0.5,0,10,0.0,TRUE,0.1,0.0,FALSE +Fabric - Stretch,OrgN-012,Noise,Swatch-Torus,0,0,0,1.0,0,0.0,4,1.0,0,0.0,2,1.0,-1,14,0,1.0,0,0.5,0,10,0.0,TRUE,1.0,0.0,FALSE +Oil - Mask,OrgN-007,Noise,Swatch-Torus,0,0,0,1.0,0,0.0,0,1.0,0,0.0,1,1.0,3,1,0,1.0,0,0.5,0,10,0.0,TRUE,1.0,0.0,FALSE +Oil - Shiny,OrgN-024,Noise,Swatch-Torus,0,0,0,1.0,0,0.0,0,1.0,0,0.0,1,1.0,3,11,2,0.5,2,0.5,0,10,5.0,TRUE,10.0,0.0,FALSE +Membrane,OrgN-014,Noise,Swatch-Torus,0,0,0,1.0,0,0.0,0,1.0,0,0.0,0,1.0,-1,13,2,2.0,1,0.5,0,10,-5.0,TRUE,1.0,0.0,FALSE +Smoke - Gooey,OrgN-025,Noise,Swatch-Torus,0,0,3,2.75,0,0.0,3,1.0,0,0.0,1,0.9,-1,17,1,1.0,2,0.5,0,10,0.0,TRUE,1.0,0.0,TRUE +Smoke - Puffs,OrgN-027,Noise,Swatch-Torus,0,0,0,1.0,0,0.0,4,1.0,6,0.0,2,1.0,-1,16,1,1.0,2,0.5,0,10,0.0,TRUE,1.0,0.0,TRUE +Smoke - Rings,OrgN-028,Noise,Swatch-Torus,0,6,0,1.0,0,0.0,4,1.0,6,0.0,1,1.0,-1,10,1,1.0,0,0.5,9,50,0.0,TRUE,1.0,0.0,TRUE +Splotches - Bright,OrgN-005,Noise,Swatch-Torus,0,3,15,0.25,0,0.0,0,1.0,0,0.0,1,1.0,-1,21,1,1.0,2,0.5,3,5,-5.0,TRUE,1.0,0.0,FALSE +Splotches - Cartoony,OrgN-002,Noise,Swatch-Torus,0,0,0,1.0,0,0.0,0,1.0,0,0.0,2,1.0,-1,5,0,1.0,0,0.5,4,2,0.0,TRUE,1.0,0.0,FALSE +Splotches - Glassy,OrgN-004,Noise,Swatch-Torus,0,0,0,1.0,0,0.0,0,1.0,0,0.0,2,1.0,-1,5,0,1.0,0,0.5,14,2,0.0,TRUE,1.0,0.0,FALSE +Splotches - Tearing,OrgN-013,Noise,Swatch-Torus,1,5,0,1.0,0,0.0,4,1.0,6,0.0,2,1.5,-1,14,1,1.0,0,0.5,0,10,10.0,TRUE,0.2,0.0,FALSE \ No newline at end of file diff --git a/OrganicNoise.osl b/OrganicNoise.osl index 1250f58..61316e0 100644 --- a/OrganicNoise.osl +++ b/OrganicNoise.osl @@ -1,69 +1,56 @@ // Organic Noise Shader - Caustics, bubbly, and weird noises. // Organic Noise by Philippe Groarke -// Modified: 2022-07-02 -// Copyright 2022 Autodesk Inc, All rights reserved. This file is licensed under Apache 2.0 license +// Copyright 2022 Philippe Groarke, All rights reserved. This file is licensed under Apache 2.0 license // https://github.com/ADN-DevTech/3dsMax-OSL-Shaders/blob/master/LICENSE.txt +#include "D:\code\3dsmax-plugins\OSL\FeaOSL\fea.osl" + #define noise_t int -#define STRENGTH_WIDTH 50 +// Simplex:0 +// Fractal:1 +// Fractal Lines:2 +// Fractal Inverse:3 +#define custom_noise_opts \ + "Simplex:0" \ + "|Fractal Light:1" \ + "|Fractal Dark:2" \ + "|Fractal Lines:3" -#if 0 -// Returns a 2x2 matrix of noise values starting -// at center coords x,y. -matrix noise_2x2(noise_t noise_type, vector coord, float phase, vector xmove, vector ymove) { - matrix ret = 0; +float custom_noise(noise_t noise_type, vector coord, float phase) { + // Minimize calls to noise. + float ret = noise("simplex", coord, phase); - // middlecenter - { - point p = coord; - ret[1][1] = custom_noise(noise_type, p, phase); - } - // middleright - { - point p = coord + xmove; - ret[1][2] = custom_noise(noise_type, p, phase); - } - // bottomcenter - { - point p = coord - ymove; - ret[2][1] = custom_noise(noise_type, p, phase); - } - // bottomright - { - point p = coord + xmove - ymove; - ret[2][2] = custom_noise(noise_type, p, phase); + if (noise_type == 0) { + // simplex + ret = (ret + 1) * 0.5; + } else if (noise_type == 1) { + // fractal light + ret = 1 - fabs(ret); + } else if (noise_type == 2) { + // fractal dark + ret = fabs(ret); + } else if (noise_type == 3) { + // fractal lines + ret = 1 - fabs(ret); + if (ret <= 0.99) { + ret = 0; + } } return ret; } -// Robert's cross convolution. -// Very approximate gradient, but much faster. -// x -// | 1| 0| -// | 0|-1| -// y -// | 0| 1| -// |-1| 0| -vector roberts_gradient(matrix m) { - float x = m[1][1] - m[2][2]; - float y = m[1][2] - m[2][1]; - return vector(x, y, 0); -} -#endif +float fbm(int iterations, vector coord, float phase) { + float ret = 0.0; -float custom_noise(noise_t noise_type, vector coord, float phase) { - // Minimize calls to noise. - float ret = noise("simplex", coord, phase); - if (noise_type == 0) { - // simplex - return (ret + 1) * 0.5; - } - else if (noise_type == 1) { - // fractal - return fabs(ret); + for (int i = 1; i < iterations + 1; ++i) { + float size = i / float(iterations); + ret += noise("usimplex", coord / size, phase + size) * size; } + + ret /= float(iterations); + return ret; } // Returns a 3x3 matrix of noise values around @@ -121,26 +108,6 @@ matrix noise_3x3(noise_t noise_type, vector coord, float phase, vector xmove, ve return ret; } -// Sobel operator convolution. -// Pass in a 3x3 matrix of data to convolve. -// x -// |-1| 0| 1| -// |-2| 0| 2| -// |-1| 0| 1| -// y -// |-1|-2|-1| -// | 0| 0| 0| -// | 1| 2| 1| -vector sobel_gradient(matrix m) { - float x = -m[0][0] + m[0][2] - 2 * m[1][0] + 2 * m[1][2] - - m[2][0] + m[2][2]; - - float y = -m[0][0] - 2 * m[0][1] - m[0][2] - + m[2][0] + 2 * m[2][1] + m[2][2]; - - return vector(x, y, 0); -} - float flatten_top(float mag, float thresh) { return mag < thresh ? thresh : mag; } @@ -150,25 +117,12 @@ float emphasize_top(float mag, float thresh) { return mag > 1 - thresh ? 1 - thresh : mag; } -// Play with k, -// https://www.desmos.com/calculator/og836nvwmx -float interp(float k, float percent) { - float ret = 0.0; - float epsilon = 0.0001; - if (fabs(k) < epsilon) { - // Actual k = 0 == 0 always. Just do linear interp. - ret = percent; - } else { - ret = (exp(k * percent) - 1.0) / (exp(k) - 1.0); - } - return ret; -} shader OrganicNoise [[ string help = "

Organic Noise

" - "Modulates and filters OSL noises to produce organic looking noises. Garden variety of caustic, fleshy and weird noises." + "Modulates and filters OSL noises to produce organic looking noises. Garden variety of caustic, fleshy and abstract noises." , string label = "Organic Noise" ]] @@ -178,40 +132,122 @@ shader OrganicNoise string widget = "mapper", string label = "Noise Type", string options = - "Simplex:0" - "|Fractal:1", - string help = "The source noise type.", + custom_noise_opts + , + string help = "The source noise type. 'Fractal lines' doesn't play well with most options, but is there because why not.", int connectable = 0, ]], - int noise_mode = 0 + int noise_effect = 0 [[ string widget = "mapper", - string label = "Noise Mode", + string label = "Noise Effect", string options = "None:0" - "|Camo:1" - "|Bubbles:2" - "|Camo-Bubbles:3", - string help = "A top level modulation applied to source noise.", + "|Exponentize:1" + "|Logarithmize:2" + "|Sharpen:3" + "|Blur:4" + "|Emboss:5" + "|Outline:6" + , + string help = "Applies an extra effect on your base noise.", int connectable = 0, - string packName = "Noise Mode / Camo Seed", ]], - float camo_rotation = 0 + int line_mode = 0 + [[ + string widget = "mapper", + string label = "Lines", + string options = + fea_blend_opts + , + string help = "Creates lines distorted by noise.", + int connectable = 0, + string packName = "Lines / Spacing", + ]], + + float line_spacing = 1.0 + [[ + string label = "Line Spacing", + string help = "Space between lines.", + float min = 0.00001, + int connectable = 0, + string packName = "Lines / Spacing", + int widgetWidth = FEA_RPACK_W, + ]], + + int camo_mode = 0 + [[ + string widget = "mapper", + string label = "Camo", + string options = + fea_blend_opts + , + string help = "Gives a 'camouflage' look to the noise.", + int connectable = 0, + string packName = "Camo / Angle", + ]], + + float camo_angle = 0 [[ - string units = "degrees", string label = "Camo Seed", string help = "An angle used to tweak 'camo', 0 to 360 degrees.", - float sensitivity = 1, - int digits = 0, float min = 0, - float max = 360, + float max = 1, + int connectable = 0, + string packName = "Camo / Angle", + int widgetWidth = FEA_RPACK_W, + ]], + + int bubbles_mode = 0 + [[ + string widget = "mapper", + string label = "Bubbles", + string options = + fea_blend_opts + , + string help = "Gives a 'bubbly' thicker look to the noise.", + int connectable = 0, + string packName = "Bubbles / Strength", + ]], + + float bubbles_strength = 1.0 + [[ + string label = "Bubbles Strength", + string help = "Affects bubbles strength.", + float min = 0, + float max = 1, int connectable = 0, - string packName = "Noise Mode / Camo Seed", - int widgetWidth = STRENGTH_WIDTH, + string packName = "Bubbles / Strength", + int widgetWidth = FEA_RPACK_W, ]], + int crease_mode = 0 + [[ + string widget = "mapper", + string label = "Crease", + string options = + fea_blend_opts + , + string help = "Filters the noise according to a direction, resulting in dark lines with peak 'creases'.", + int connectable = 0, + string packName = "Crease / Angle", + ]], + + float crease_angle = 0.0 + [[ + string label = "Crease Direction", + string help = "Affects orientation of creases.", + float min = 0, + float max = 1, + int connectable = 0, + string packName = "Crease / Angle", + int widgetWidth = FEA_RPACK_W, + ]], + + FEA_SPACER(1) + int over_sampling = 0 [[ string widget = "mapper", @@ -231,10 +267,37 @@ shader OrganicNoise string help = "Increase or decrease the over-sampling amount.", string packName = "Over-Sampling / Strength", int connectable = 0, - int widgetWidth = STRENGTH_WIDTH, + int widgetWidth = FEA_RPACK_W, ]], - int girth = 0 + int oversampling_noise_type = -1 + [[ + string widget = "mapper", + string label = "Over-Sampling Noise", + string options = + "Same:-1|" + custom_noise_opts + , + string help = "The type of noise to over-sample. By default, same type as 'Noise Type'.", + int connectable = 0, + string packName = "Over-Sampling Noise / Blend", + ]], + + int overampling_blend = 13 + [[ + string widget = "mapper", + string label = "Over-Sampling Blend", + string options = + fea_blend_opts + , + string help = "How to blend the over-sampled noise.", + int connectable = 0, + string packName = "Over-Sampling Noise / Blend", + ]], + + FEA_SPACER(2) + + int girth_mode = 0 [[ string widget = "mapper", string label = "Girth", @@ -242,7 +305,7 @@ shader OrganicNoise "None:0" "|Bubbly:1" "|Caustics:2", - string help = "Bubbly expands the noise, caustics squeezes it.", + string help = "Bubbly expands the noise, caustics squeezes it.\nNote : Caustics don't play well with Bubbles noise mode.", int connectable = 0, string packName = "Girth / Strength", ]], @@ -254,7 +317,7 @@ shader OrganicNoise string packName = "Girth / Strength", float min = 0.001, int connectable = 0, - int widgetWidth = STRENGTH_WIDTH, + int widgetWidth = FEA_RPACK_W, ]], int girth_threshold = 0 @@ -270,163 +333,269 @@ shader OrganicNoise int connectable = 0, ]], - float threshold_strength = 0.5 + float girth_threshold_strength = 0.5 [[ string label = "Girth-Threshold Strength", string help = "The girth threshold limit value.", string packName = "Girth-Threshold / Strength", int connectable = 0, - int widgetWidth = STRENGTH_WIDTH, + int widgetWidth = FEA_RPACK_W, float min = 0, - float max = 0.999, + float max = 0.9, ]], - float sampling_dist = 1 + FEA_SPACER(6) + + int step_mode = 0 [[ - string label = "Sampling Distance", - string help = "The 'search range' of the neighbours. Animate with scene time for interesting results.", + string widget = "mapper", + string label = "Step", + string options = + fea_blend_opts + , + string help = "Creates 'level sets' or topological layers.", + int connectable = 0, + string packName = "Step / Amount", ]], - float phase = 0 + int step_amount = 10 [[ - string label = "Phase", - string help = "The 4th coordinate of the noise. Animate with scene time for interesting results.", + string label = "Step Amount", + string help = "The number of steps to create.", + int min = 2, + int connectable = 0, + string packName = "Step / Amount", + int widgetWidth = FEA_RPACK_W, ]], - float ramp_out = 0 + float interpolate = 0 [[ - string label = "Ramp Output", + string label = "Interpolate Output", string help = "Interpolates final result. Positive is exponential, negative is logarithmic and '0' is linear interpolation (none).", int connectable = 0, - string packName = "Ramp / Clamp Output", + string packName = "Interpolate / Clamp", ]], int clamp_out = 1 [[ string label = "Clamp Output", - string help = "Clamps the output value to [0..1].", + string help = "Clamps the output value between [0..1].", string widget = "checkBox", int connectable = 0, - string packName = "Ramp / Clamp Output", + string packName = "Interpolate / Clamp", + ]], + + FEA_SPACER(3) + + float sampling_dist = 1 + [[ + string label = "Sampling Distance", + string help = "The 'search range' of the neighbours. Low distances give a 'cell' look. Higher sampling distances reduce tiling.", + float min = 0.05, + float max = 100, + int connectable = 0, + ]], + + float in_phase = 0 + [[ + string label = "Phase", + string help = "The 4th coordinate of the noise. You can animate with scene time for movement. " + "For interesting results and 'edge distortion', feed another noise to this parameter.", + string packName = "Phase / Perturb", + ]], + + int in_perturb_phase = 0 + [[ + string label = "Perturb Phase", + string help = "Applies some randomness to the phase itself. Can lead to interesting animations or distortions.", + int connectable = 0, + string widget = "checkBox", + string packName = "Phase / Perturb", ]], - float uv_scale = 5.0 + float uv_scale = 0.25 [[ string label = "UV Scale", string help = "Scale your UVWs up or down. For more betterer transformations, use UVW Transform node.", + float min = 0.00001, int connectable = 0, ]], - point uvw = point(u, v, 0) + point uvw = transform("object", P) [[ string label = "UVW", string help = "Transformed UVW coordinates. Animate 'w' with scene time for interesting results.", ]], - output float Out = 0, - output vector VectorDisplacement = 0 + output float Out = 0 ) { float epsilon = 0.00001; - point orig_coord = uvw * uv_scale; + + // Make default scale a little smaller. + float muv_scale = uv_scale; + if (uvw == transform("object", P)) { + muv_scale = uv_scale * 10.0; + } + + point orig_coord = uvw / muv_scale; point coord = orig_coord; vector grad = 0; + matrix sample_area; float noise_v = 0; - float orig_noise = 0; + float sample_distance = 0.05; + float phase = in_phase; + float entropy = fbm(3, coord, phase); + + + if (in_perturb_phase) { + phase += entropy; + } { // Gets wonky at very low sampling distances. - float sd = fabs(sampling_dist) < 0.05 ? 0.05 : sampling_dist; + sample_distance = sampling_dist; + + // Low sampling distances give a "cell" look, so we can't push it too + // far with the entropy. The higher the sampling dist, the more random. + sample_distance += entropy * sample_distance; + + // Add some perturberance if enabled. + vector xmove; + vector ymove; + if (in_perturb_phase) { + xmove = vector(sample_distance, 0, phase); + ymove = vector(0, sample_distance, phase); + } else { + xmove = vector(sample_distance, 0, 0); + ymove = vector(0, sample_distance, 0); + } // Compute a 3x3 matrix of surrounding samples to the currently // rendered point. - matrix sample_area = noise_3x3(noise_type, coord, phase, vector(sd, 0, 0), vector(0, sd, 0)); + sample_area = noise_3x3(noise_type, coord, phase, xmove, ymove); noise_v = sample_area[1][1]; - orig_noise = noise_v; // Compute gradient of noise. - grad = sobel_gradient(sample_area); + grad = fea_sobel_gradient(sample_area); + } + + if (noise_effect == 1) { + // Exponent + noise_v = fea_interp(10, noise_v); + } else if (noise_effect == 2) { + // Logarithm + noise_v = fea_interp(-10, noise_v); + } else if (noise_effect == 3) { + // sharpen + noise_v = fea_sharpen(sample_area); + } else if (noise_effect == 4) { + // blur + noise_v = fea_blur(sample_area); + } else if (noise_effect == 5) { + // emboss + noise_v = fea_emboss(sample_area); + } else if (noise_effect == 6) { + // outline + noise_v = fea_outline(sample_area); } float mag = length(grad); float dir = atan2(grad[1], grad[0]); - dir = (degrees(dir) + 180) / 360; - dir = fmod(dir + (camo_rotation / 360), 1); - - if (noise_mode == 1) { - noise_v = dir; - } else if (noise_mode == 2) { - noise_v = mag; - } else if (noise_mode == 3) { - noise_v = mag * dir; + + // Lines + if (line_mode != 0) { + vector new_coord = coord + grad; + if (in_perturb_phase) { + new_coord[2] = phase; + } + float lspace = 1.0 / (line_spacing * 0.5); + float val = abs(sin((new_coord[0] + new_coord[1] + new_coord[2]) * lspace)); + noise_v = fea_blend(line_mode, noise_v, val); + } + + // Camo + if (camo_mode != 0) { + float camo_dir = fea_rad_to_per(dir + M_PI) + camo_angle; + camo_dir = fmod(camo_dir, 1); + noise_v = fea_blend(camo_mode, noise_v, camo_dir); + } + + // Bubbles + noise_v = fea_blend(bubbles_mode, noise_v, mag * bubbles_strength); + + // Creases + if (crease_mode != 0) { + float s = fea_per_to_rad(crease_angle); + vector axis = fea_angle_to_vec(s); + float val = abs(dot(normalize(grad), axis)); + noise_v = fea_blend(crease_mode, noise_v, val); } // Over-sampling. if (over_sampling == 1) { + // ring coord += grad * oversampling_strength; } else if (over_sampling == 2) { + // pinch coord += normalize(grad) * oversampling_strength; } if (over_sampling != 0) { - float os_noise = custom_noise(noise_type, coord, phase); - if (noise_mode == 0) { - noise_v = os_noise; - } else { - // Keep noise mode modulation. - noise_v *= os_noise; + int os_noise_type = noise_type; + if (oversampling_noise_type != -1) { + os_noise_type = oversampling_noise_type; } + + float os_noise = custom_noise(os_noise_type, coord, phase); + noise_v = fea_blend(overampling_blend, noise_v, os_noise); } + // Girth // The threshold acts invertly dependending on girth, // flip it so UI makes sense. if (girth_threshold == 1) { // flatten - if (girth == 2) { - mag = flatten_top(mag, threshold_strength); + if (girth_mode == 2) { + mag = flatten_top(mag, girth_threshold_strength); } else { - mag = emphasize_top(mag, threshold_strength); + mag = emphasize_top(mag, girth_threshold_strength); } } else if (girth_threshold == 2) { // emphasize - if (girth == 2) { - mag = emphasize_top(mag, threshold_strength); + if (girth_mode == 2) { + mag = emphasize_top(mag, girth_threshold_strength); } else { - mag = flatten_top(mag, threshold_strength); + mag = flatten_top(mag, girth_threshold_strength); } } - if (girth == 1) { + if (girth_mode == 1) { mag *= girth_strength; noise_v *= mag; - } else if (girth == 2) { + } else if (girth_mode == 2) { // Caustics. - if (noise_mode == 2) { - // Can't use caustics + bubbles mode. - // Do something else interesting. - // noise_v = interp(5 * girth_strength, noise_v); - noise_v /= orig_noise * (1 / girth_strength); - } else { - mag *= 10; - mag *= (1.0 / girth_strength); - noise_v /= mag; - } + mag *= 10; + mag *= (1.0 / girth_strength); + noise_v /= mag; } - if (clamp_out) { - noise_v = clamp(noise_v, 0, 1); + // Step + if (step_mode != 0) { + int inoise = int(noise_v * step_amount); + float val = float(inoise) / float(step_amount); + noise_v = fea_blend(step_mode, noise_v, val); } - if (ramp_out != 0) { - noise_v = interp(ramp_out, noise_v); + // Interpolate + if (interpolate != 0) { + noise_v = fea_interp(interpolate, noise_v); } - Out = noise_v; - - { - vector coord_disp = coord - orig_coord; - // vector disp = vector(-coord_disp[0], -coord_disp[1], Out); - vector disp = vector(coord_disp[0], coord_disp[1], Out); - VectorDisplacement = disp; + // Clamp + if (clamp_out) { + noise_v = clamp(noise_v, 0, 1); } + + Out = noise_v; } \ No newline at end of file diff --git a/README.md b/README.md index bd6d70d..493a407 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,7 @@ Curvalicious is a fast (no raytracing) screen-space curvature shader. It outputs Utility helper that outputs distance to picked node's center. ## Organic Noise -Organic looking noise generator. Caustics, fleshy bubbles and other strange noises. +Organic looking noise generator. Caustics, fleshy bubbles and other abstract noises. Shader Example @@ -30,4 +30,16 @@ SimpleOcean is a Gerstner Wave implementation to simulate ocean vector displacem Shader Example -[More details](doc/simple_ocean.md) \ No newline at end of file +[More details](doc/simple_ocean.md) + + +# Build Instructions + +* [Install haskell](https://www.haskell.org/ghcup/install/). +* [Install 7-zip](https://www.7-zip.org/). +* Run the build script, this will create a build folder and output the finalized shaders inside of it. + +```bash +cd fea_osl +runhaskell build.hs +``` \ No newline at end of file diff --git a/build.hs b/build.hs new file mode 100644 index 0000000..03f7fd7 --- /dev/null +++ b/build.hs @@ -0,0 +1,110 @@ +{-# OPTIONS_GHC -fno-warn-tabs #-} +{-# LANGUAGE ScopedTypeVariables #-} + +import System.IO () +import System.Process ( readProcess, callProcess ) +import System.Directory ( createDirectoryIfMissing, findFileWith, listDirectory, copyFile, removeDirectory, removeDirectoryRecursive, doesDirectoryExist, setCurrentDirectory ) +import Control.Monad ( filterM, liftM2, zipWithM_, when ) +import Control.Exception ( assert ) +import Text.Printf ( printf ) +import Data.Char ( isSpace ) +import Data.List ( isSuffixOf, isPrefixOf, findIndex, isInfixOf, words ) +import System.FilePath (takeFileName) + +{- Globals -} +outDir :: String +outDir = "build" + +tempBuildDir :: String +tempBuildDir = "build/FeaOSL" + +feaLibFilename :: String +feaLibFilename = "fea.osl" + +readmeFilename :: String +readmeFilename = "README.md" + + +{- Pure -} +rstrip :: String -> String +rstrip = reverse . dropWhile isSpace . reverse + +parseIncludePath :: String -> String +parseIncludePath s = do + let wrds = words s + -- assert (length wrds == 2) wrds + tail (init (last wrds)) + +{- Impure -} +getFeaVer :: IO String +getFeaVer = do + v <- readProcess "git" ["describe", "--tags", "--abbrev=0"] [] + let r = rstrip v + return r + +getZipFilename :: IO String +getZipFilename = do + ver <- getFeaVer + let n = "FeaOSL-" ++ ver ++ ".zip" + return n + +replaceInclude :: String -> IO String +replaceInclude contents = do + f_lines <- mapM + (\l -> + if (isInfixOf "#include" l) + then readFile (parseIncludePath l) + else return l + ) + (lines contents) + + return (unlines f_lines) + + +writeToTemp :: FilePath -> String -> IO () +writeToTemp filepath contents = do + let out_filepath = tempBuildDir ++ "/" ++ takeFileName filepath + writeFile out_filepath contents + + +main :: IO () +main = do + out_filename <- getZipFilename + printf "Generating %s\n" out_filename + + -- Cleanup, todo : At end + dirExists <- doesDirectoryExist tempBuildDir + when dirExists (removeDirectoryRecursive tempBuildDir) + createDirectoryIfMissing True tempBuildDir + + files <- listDirectory "." + + -- Copy supporting files (.md, .ui) + let support_files = filter + (\x -> isSuffixOf ".md" x || isSuffixOf ".ui" x || isSuffixOf ".csv" x) + files + + -- let support_files = filter + -- (liftM2 (||) (isSuffixOf ".md") (isSuffixOf ".ui")) + -- files + + mapM_ (\f -> copyFile f (tempBuildDir ++ "/" ++ f)) support_files + + -- Get *.osl files. + let osl_filepaths = filter + (liftM2 (&&) (isSuffixOf ".osl") (/= feaLibFilename)) + files + + -- Replace osl #include statements with target file contents. + osl_file_contents <- mapM readFile osl_filepaths + new_file_contents <- mapM replaceInclude osl_file_contents + zipWithM_ writeToTemp osl_filepaths new_file_contents + + -- Now, zip everything in the temp directory. + let out_filepath = "FeaOSL/" ++ out_filename + -- callProcess "7z" ["a", "-aoa", "-tzip", out_filepath, + -- "./" ++ tempBuildDir ++ "/*"] + setCurrentDirectory "./build" + callProcess "7z" ["a", "-aoa", "-tzip", out_filepath, + "FeaOSL/*"] + diff --git a/fea.osl b/fea.osl new file mode 100644 index 0000000..90a8860 --- /dev/null +++ b/fea.osl @@ -0,0 +1,291 @@ +// fea osl libraries by Philippe Groarke +// Copyright 2022 Philippe Groarke, All rights reserved. This file is licensed under Apache 2.0 license +// https://github.com/ADN-DevTech/3dsMax-OSL-Shaders/blob/master/LICENSE.txt + + +#define FEA_RPACK_W 50 +#define FEA_SPACER(n) int __spacer##n = 0 [[string widget = "checkBox", int connectable = 0, int widgetWidth = 1]], + +/** + * Conversions + */ + +// Converts a precentage to radian. +// Expects percent 0.0 to 1.0. +float fea_per_to_rad(float percent) { + return percent * M_2PI; +} + +// Converts a radian to percentage. +// Returns percent 0.0 to 1.0. +float fea_rad_to_per(float rad) { + return rad * 0.15915494309189498; +} + +// Given a radian angle, returns a 2d vector pointing +// in that direction. +vector fea_angle_to_vec(float rad) { + return vector(cos(rad), sin(rad), 0); +} + + +/** + * Convolutions + */ + +// Robert's cross convolution. +// Very approximate gradient, but much faster. +// Causes uv translation :( +// x +// | 1| 0| +// | 0|-1| +// y +// | 0| 1| +// |-1| 0| +vector roberts_gradient(matrix m) { + float x = m[1][1] - m[2][2]; + float y = m[1][2] - m[2][1]; + return vector(x, y, 0); +} + +// Sobel operator convolution. +// Pass in a 3x3 matrix of data to convolve. +// x +// |-1| 0| 1| +// |-2| 0| 2| +// |-1| 0| 1| +// y +// |-1|-2|-1| +// | 0| 0| 0| +// | 1| 2| 1| +vector fea_sobel_gradient(matrix m) { + float x = -m[0][0] + m[0][2] - 2 * m[1][0] + 2 * m[1][2] + - m[2][0] + m[2][2]; + + float y = -m[0][0] - 2 * m[0][1] - m[0][2] + + m[2][0] + 2 * m[2][1] + m[2][2]; + + return vector(x, y, 0); +} + +// Sharpen convolution. +// Pass in a 3x3 matrix of data to convolve. +float fea_sharpen(matrix m) { + return -m[0][1] - m[1][0] + 5 * m[1][1] - m[1][2] - m[2][1]; +} + +// Blur convolution. +// Pass in a 3x3 matrix of data to convolve. +float fea_blur(matrix m) { + return 0.0625 * (m[0][0] + m[0][2] + m[2][0] + m[2][2]) + + 0.125 * (m[0][1] + m[1][0] + m[1][2] + m[2][1]) + + 0.25 * m[1][1]; +} + +// Emboss convolution. +// Pass in a 3x3 matrix of data to convolve. +float fea_emboss(matrix m) { + return -2 * m[0][0] - m[0][1] - m[1][0] + m[1][1] + m[1][2] + m[2][1] + 2 * m[2][2]; +} + +// Outline convolution. +// Pass in a 3x3 matrix of data to convolve. +float fea_outline(matrix m) { + return -m[0][0] - m[0][1] - m[0][2] - m[1][0] + 8 * m[1][1] - m[1][2] - m[2][0] - m[2][1] - m[2][2]; +} + + +/** + * Interpolation + */ + +// Given a value val, returns the closest multiple. +float fea_closest_multiple(float val, float multiple) { + if (multiple == 0.0) { + return 0.0; + } + return floor(val / multiple) * multiple; +} + +// Given the 3 colors and a precentage, +// mix between them (1/3 per color). +color fea_mix(color c1, color c2, color c3, float p) { + float p1 = clamp(p * 2 - 1, 0, 1); + color temp = mix(c2, c3, p1); + + float p2 = clamp(p * 2, 0, 1); + return mix(c1, temp, p2); +} + +// Given the 4 colors and a precentage, +// mix between them (1/4 per color). +color fea_mix(color c1, color c2, color c3, color c4, float p) { + float p1 = clamp(p * 3 - 2, 0, 1); + color temp1 = mix(c3, c4, p1); + + float p2 = clamp(p * 3 - 1, 0, 1); + color temp2 = mix(c2, temp1, p2); + + float p3 = clamp(p * 3, 0, 1); + return mix(c1, temp2, p3); +} + +// Play with k, +// https://www.desmos.com/calculator/og836nvwmx +// k > 0, exponential +// k == 0, linear +// k < 0, logarithmic +float fea_interp(float k, float percent) { + float ret = 0.0; + float epsilon = 0.0001; + if (fabs(k) < epsilon) { + // Actual k = 0 == 0 always. Just do linear interp. + ret = percent; + } else { + ret = (exp(k * percent) - 1.0) / (exp(k) - 1.0); + } + return ret; +} + + +// Blend 2 floats using typical color blend modes. +// 'bot' is base layer, 'top' is top layer. +#define fea_blend_opts \ + "None:0" \ + "|Replace:1" \ + "|Darken:3" \ + "|Multiply:4" \ + "|Color Burn:5" \ + "|Linear Burn:6" \ + /*"|Darker Color:7"*/ \ + "|Lighten:8" \ + "|Screen:9" \ + "|Color Dodge:10" \ + "|Linear Dodge (Add):11" \ + /*"|Lighter Color:12"*/ \ + "|Overlay:13" \ + "|Soft Light:14" \ + "|Hard Light:15" \ + "|Vivid Light:16" \ + "|Linear Light:17" \ + "|Pin Light:18" \ + "|Hard Mix:19" \ + "|Difference:20" \ + "|Exclusion:21" \ + "|Substract:22" \ + "|Divide:23" \ + /*"|Hue:24"*/ \ + /*"|Saturation:25"*/ \ + /*"|Color:26"*/ \ + /*"|Luminosity:27"*/ \ + "|Average:28" + +float fea_blend(int blend_type, float bot, float top) { + // http://www.simplefilter.de/en/basics/mixmods.html + + float ret = bot; + if (blend_type == 0) { + // none + // ret = bot; + } else if (blend_type == 1) { + // replace + ret = top; + } else if (blend_type == 3) { + // darken + ret = min(bot, top); + } else if (blend_type == 4) { + // multiply + ret = bot * top; + } else if (blend_type == 5) { + // color burn + ret = 1 - (1 - bot) / top; + } else if (blend_type == 6) { + // linear burn + ret = bot + top - 1; + } else if (blend_type == 7) { + // todo : darker color + } else if (blend_type == 8) { + // lighten + ret = max(bot, top); + } else if (blend_type == 9) { + // screen + ret = 1 - (1 - bot) * (1 - top); + } else if (blend_type == 10) { + // color dodge + ret = bot / (1 - top); + } else if (blend_type == 11) { + // linear dodge (add) + ret = bot + top; + } else if (blend_type == 12) { + // todo : lighter color + } else if (blend_type == 13) { + // overlay + if (bot <= 0.5) { + ret = 2 * bot * top; + } else { + ret = 1 - 2 * (1 - bot) * (1 - top); + } + } else if (blend_type == 14) { + // soft light + ret = (1 - 2 * top) * pow(bot, 2) + 2 * bot * top; + } else if (blend_type == 15) { + // hard light + if (top <= 0.5) { + ret = 2 * top * bot; + } else { + ret = 1 - 2 * (1 - top) * (1 - bot); + } + } else if (blend_type == 16) { + // vivid light + if (top <= 0.5) { + ret = 1 - (1 - bot) / (2 * top); + } else { + ret = bot / (2 * (1 - top)); + } + } else if (blend_type == 17) { + // linear light + ret = bot + 2 * top - 1; + } else if (blend_type == 18) { + // pin light + if (bot < 2 * top - 1) { + ret = 2 * top - 1; + } else if (bot > 2 * top) { + ret = 2 * top; + } else { + ret = bot; + } + } else if (blend_type == 19) { + // hard mix + if (top < 1 - bot) { + ret = 0; + } else { + ret = 1; + } + } else if (blend_type == 20) { + // difference + ret = fabs(top - bot); + } else if (blend_type == 21) { + // exclusion + ret = top + bot - 2 * top * bot; + } else if (blend_type == 22) { + // substract + ret = bot - top; + } else if (blend_type == 23) { + // divide + float t = top == 0.0 ? 0.0000001 : top; + ret = bot / t; + } else if (blend_type == 28) { + // average + ret = (bot + top) * 0.5; + } + + return ret; +} + +vector fea_blend(int blend_type, vector bot, vector top) { + return vector( + fea_blend(blend_type, bot[0], top[0]), + fea_blend(blend_type, bot[1], top[1]), + fea_blend(blend_type, bot[2], top[2]) + ); +} diff --git a/zip_fea_osl.bat b/zip_fea_osl.bat deleted file mode 100644 index 6bf9fac..0000000 --- a/zip_fea_osl.bat +++ /dev/null @@ -1,24 +0,0 @@ -@echo off -setlocal - -git describe --tags --abbrev=0 > ver.txt -set /p version=