Procedural texture generator (example and wishes)

Hello! This is a feedback to the Computer Graphics student @amyspark. She participates to the Google Summer of Code 2020 on Krita for a procedural generator based on SeExpr. This thread is my answer to her recent call for example in this must-read blog post of her here: https://www.amyspark.me/blog/posts/2020/05/19/what-is-seexpr-about.html

Diclaimer: to get visual examples, I couldn’t produce any procedural layer myself because I couldn’t find any Free Libre and Open Sources software allowing me to produce what I had in mind. So, I had to search online and used for this feedback textures that are not my creations. This example are not for commercial usage, only for information and I hope the authors of the texture used under will allow me this use (if not, contact me and I’ll remove).

Examples

Procedural generator are great for generating electric arc, thunders to overlay over an artwork as a special effect (figure 1.A). With multiple axis of symetry a generator can also do complex mandalas effect; great for any magic ring summoning effect or magic circle (figure 1.B). Energy waves with thin lines would be complex to paint one by one, while a generator can produce them quickly (figure 2.B).

Sometime generator can tiles easily the procedural effects. With the transform tool of Krita that can deform them later; it’s possible to use a thick Voronoï pattern as a lighting effect (figure 2.A), or to use concentric circles bubbles as an interesting floor texture (figure 2.B) or to create more geometric pattern to ease the creation of complex tiles on floors that would take ages to trace manually in perspective and paint (figure 2.C). Of course, all this textures wouldn’t be perfect to use “as it is” and would require to the painter to paint-over them to melt them visually into the painting stroke rythm of the piece. But their quick setup and generation would open creative experimentation and a quick way to prototype an idea before finalising it.

Other ideas: underwater light difraction pattern (figure 3.A), cloudy type of texture for interesting overlays (figure 3.B) or depht of field Bokeh effect auto generated (figure 3.C).

Other ideas: Sci-fi mosaïc pattern (figure 4.A), broken glass shatter pattern or very dry floor (figure 4.B), or more organic webbing (figure 4.C).

Usage

I have to admit that I’m not really personnaly interested by keeping the texture “dynamic”; as in using it as a layer that can rescale and keeps the effect as it is. The procedural effect I want to generate are transitional in my workflow and never an end point. The “texture” generated will be in any case be deformed, erased partially, recolored and painted-over. I’m 100% sure about it for my workflow.

That’s why I wouldn’t mind at all if the dialog would be like a filter and result in filling a layer or a selection with the generated output as baked pixels. I would certainly not use the feature if the whole generation required a special dynamic layer slow to compute at print resolution (around 3000px or 4000px large artworks). But I wouldn’t mind getting a slow progress bar to render and bake to pixel a texture like that after I press “OK” in a dialog.

Speaking of GUI, I wouldn’t use it if it was only code (figure 5.A) It would be a too big investment learning a new syntax and words for only this little “extra texture spice” benefits. Something like a node editor would feel also too overkill to me (figure 5.B) and I think it would be too complex for a feature like that (also, I imagine to maintain). All in all, I really like the GUI on the SeExpr website (figure 5.C): a quick preview, a set of presets (that’s very cool) and a part with widget to adapt parameters: Frequency, Turbulances, etc… I also like the customisable gradient editor to color the effect. I also enjoy to see the code exposed on the bottom but I think a part like that shouldn’t be visible by default; but be hidden as the text tool in Krita does for the SVG markup in another tab.

I think one of the challenge and success of the feature will be to get a solid list of presets in the list, named for practical use in artworks. It could be words like: fire, thunder, electricity sparks, clouds, lava, alien wall, organic nest, rain, snow, grain, crackles, shattering, sci-fi pattern…etc… rather than technical abstract identifier like tiles-voronoi-Freq22, inversed-Perlin27, random-noises, Xgen, EFX, 00121115…etc…. Then users might discover them, customise them (color/zoom/complexity) and insert them in their artworks for creative purpose. That would be a success!

That’s all, I hope my feedback will help @amyspark, you can guess it; I’m really passionate about the topic :slight_smile: That’s because my first comic colored with digital color back in 1998 with Corel PhotoPaint had a procedural generator and I love it.

Thank you for reading.

Other artists and developper: feel free to join this discussion and share your own example and feedback. I posted this on Krita-Artist and not on my blog for this purpose!

12 Likes

Thanks for initiating this thread @Deevad

I fully agree with this. It would be helpful if this is a part of fill layer as an option with title “Dynamic Pattern” or “procedural patterns” . In addition to that this can also be in Filter menu.

1 Like

So, from deevad’s list it seems some kind of implementation of fractal flames would be cool.

I am also looking forward to seeing if we can get some of the non-orthographic-tiling entries from the tesselation wikipedia page generated with seexpr.

1 Like

Thanks for starting this thread @Deevad! I couldn’t get round to doing it because of too much homework here. Also, afternight Eurographics sessions don’t help either :wink:

To summarise: I’ll be really, really grateful if you post references or code here!
During my CG research, I’ve noticed there’s a definite lack of “secret sauce” in the literature; we know the mathematical bits, and we see in the post above the end results from the artists. What we lack, and I’d like to add as examples to my project, is how each example is made.

1 Like

Awesomely made request! I think this would be a great way to expand Krita’s creative capabilities!

Definitely would need some work but I’m looking forward to the progress of this topic!

1 Like

I just have one question, can it be possible to create a fill layer that utilize g’mic-qt via g’mic-qt being used a library? I know that’s a difficult task that would in theory take year or two to program, but I think something like this would fill the needs of the OP in the long run as g’mic-qt already have plenty of useful filters and tools that can be used to generate procedural work.

This thread is regarding @amyspark’s GSOC task to implement Disney/Pixars SeExpr texture generators to Krita. It is not about gmic or implementing Gmic filters and file layers. You can read about the GSOC proposal and posts on the dev’s blog.

1 Like

Hey, I’m not skirting this one :slight_smile:

@Reptorian, yes, it should be possible. This would, incidentally, fix the current unavailability of GMic-Qt on macOS. tl;dr: due to system restrictions on shared memory between apps (Krita and GMic run as separate processes), GMic cannot access opened documents in Krita.

1 Like

Ok, managed to get a really nice one down:

$ratiov = $h/$w;
$uv = [$u, $v*$ratiov, 0];
$size = 15; # 0,30;
$outlines = 10; #1, 20;
$color1 = [0,0,0];
$color2 = [1,1,1];

$point = pvoronoi($size*$uv,4,0,.2);

#circle determining part

$dist = ($uv*$size)-$point;
$dotproduct = dot(dist, dist) * 4;

#filling the circles

$fac = fmod(sin($dotproduct*$outlines) , 1);
$color = ccurve($fac,0,$color1,4,1,$color2,4);

$color

This one is very similar to an old hand-drawn graphical pattern where you place a circle, and then another circle and you then draw circles around those. The problem with getting a computer to do this is that it’s a little hard to explain what exactly what you want, which makes things like having variation between circles while keeping the outline consistent a little hard.

EDIT: I updated it so the circles would be round…

4 Likes

Wow, good one @wolthera :+1: I also really like the setting you expose on the top GUI; “size, repeat, color1 and color 2”. This is exactly the type of preset I would love to get in a list.

So, these are all variations on a theme. What we’re doing here is just repeating the same function on the base uv coordinate over and over and getting funky marbely patterns through that.

Malachite:

$ratiov = $h/$w;
$uv = [$u, $v*$ratiov, 0];

$uv = $uv+vfbm($uv);
$uv = $uv+vfbm($uv);
$uv = $uv+vfbm($uv);
$uv = $uv+vfbm($uv);

$c = fbm($uv);

$color = ccurve($c,0,[0,0,0],4,0.567308,[0,0.666667,0.498039],4,1,[0.235294,1,0.45098],4);
$color

This one’s kinda bizarely metalic:

$ratiov = $h/$w;
$uv = [$u, $v*$ratiov, 0];

$uv = $uv+vnoise($uv*5);
$uv = $uv+vnoise($uv*5);
$c = noise($uv*5);

$color = ccurve($c,0,[0,0,0],4,1,[1,1,1],4);
$color

One based on turbulence.

$ratiov = $h/$w;
$uv = [$u, $v*$ratiov, 0];

$uv = vturbulence($uv*2);
$c = vturbulence($uv*3);

$color = ccurve($c,0,[0,0,0],4,1,[1,1,1],4);
$color

Don’t remember why I saved this one…

$ratiov = $h/$w;
$uv = [$u, $v*$ratiov, 0];

$uv = vfbm($uv);
$uv = $uv+vfbm($uv);
$c = voronoi($uv, 2);

$color = ccurve($c,0,[0,0,0],4,1,[1,1,1],4);
$color

One with very bright lines:

$ratiov = $h/$w;
$uv = [$u, $v*$ratiov, 0];
$size = 3;

$uv = vturbulence($uv*$size);
$c = voronoi($uv*$size, 2);

$color = ccurve($c,0,[0,0,0],4,1,[1,1,1],4);
$color
7 Likes

Very good presets @wolthera, I tried them all, and the Malachite is really impressive.

I toyed with the two last you posted; (thanks!) I found they were a good base for electric arcs, and it was exactly what I was needing while detailing episode 33 :smiley:

$ratiov = $h/$w;
$uv = [$u, $v*$ratiov, 0];
$size = 1;

$uv = vturbulence($uv*$size);
$c = voronoi($uv*$size*8, 2);

$color = ccurve($c,0.915058,[1,1,1],4,0.409266,[0,0,0],4,0.594595,[0.173112,0.173112,0.173112],4);
$color

Result, rendered at 4K: ( :heart_eyes_cat: oh the details!)

Then overlay on my panel that felt a bit flat. It adds a cool magnetic field (after being tinted in magenta and put in addition blending mode):

6 Likes

Decided to comment this one rather aggressively.

# SeExpr script to draw tiled dots not unlike screentones.
# We'd ideally work this one out further so it'll have the lines be less canvas-
# size based and more 'per so many pixels' or something.

# [$u, $v, 0] is a vector constructed from the proportional horizontal and
# vertical positions ($u and $v) of the output pixel and 0 as the Z-value.
# We multiply the proportional vertical by the ratio we can get from the height
# and width of the image, so that our computed shapes are in a square aspect
# ratio, they'd get streched otherwise.

$ratiov = $h / $w;
$uv = [$u, $v * $ratiov, 0];

# These are all the configurable variables. There's no reason why they're all
# assembled here besides making it easier to read the script. Values inside the
# comments are used by SeExpr widgets to determine the upper and lower limits.

$radius = 1; #0.0, 5.0
$lines = 60; #1, 300
$size = $lines;
$angle = 35; #0, 359
$softness = 0.2; #0.001, 1.0
$color1 = [0,0,0];
$color2 = [1,1,1];


# Tiling part.
# ------------
# we'll construct the position of the current pixel
# in a pattern from the $uv

$tiles = $uv * size;
$repeat = boxstep(1 , fmod( $tiles[1], 1.0) );

# reassemble the tiled vector;

$tiles = [ $tiles[0] + $repeat, $tiles[1], 0];

# Now we rotate the tiled vector, 2d rotation uses the z-axis, that's [0, 0, 1].

$tiles = rotate($tiles, [0, 0, 1], rad($angle));

# Here we remove the offset from the tiled vector.

$tiles -= floor($tiles);

# circle determining part
# -----------------------
# we're computing a 'distance field' here.

$dist = $tiles - [0.5, 0.5, 0];
$dotproduct = dot(dist, dist) * 4;
$leftSide  = (1.0 - (0.5 * $softness)) * $radius;
$rightSize = (1.0 + (0.5 * $softness)) * $radius;
$fac = smoothstep($leftSide, $rightSize, $dotproduct);

# filling the shape
# -----------------
# we only want to use color interpolation if we're sure our distance
# factor is above 0, otherwise it'll need to be color 2.

if ($fac>0) {
  $color = ccurve($fac, 0, $color1, 4, 1, $color2, 4);
} else {
  $color = $color2;
}

# and we return the color

$color
5 Likes

Still having all these super graphical ones in my system… Less comments this time around.

Square dots/Lines

# SeExpr script to draw tiled squares not unlike screentones.
# We'd ideally work this one out further so it'll have the lines be less canvas-
# size based and more 'per so many pixels' or something.

# [$u, $v, 0] is a vector constructed from the proportional horizontal and
# vertical positions ($u and $v) of the output pixel and 0 as the Z-value.
# We multiply the proportional vertical by the ratio we can get from the height
# and width of the image, so that our computed shapes are in a square aspect
# ratio, they'd get streched otherwise.

$ratiov = $h / $w;
$uv = [$u, $v * $ratiov, 0];

# These are all the configurable variables. There's no reason why they're all
# assembled here besides making it easier to read the script. Values inside the
# comments are used by SeExpr widgets to determine the upper and lower limits.

$radius = 0.33; #0.0, 1.0
$lines = 27; #1, 300
$size = $lines;
$angle = 122; #0, 359
$softness = 0.4006; #0.001, 1.0
$color1 = [0,0,0];
$color2 = [1,1,1];


# Tiling part.
# ------------
# we'll construct the position of the current pixel
# in a pattern from the $uv

$tiles = $uv * size;
$repeat = boxstep(1 , fmod( $tiles[1], 1.0) );

# reassemble the tiled vector;

$tiles = [ $tiles[0] + $repeat, $tiles[1], 0];

# Now we rotate the tiled vector, 2d rotation uses the z-axis, that's [0, 0, 1].

$tiles = rotate($tiles, [0, 0, 1], rad($angle));

# Here we remove the offset from the tiled vector.

$tiles -= floor($tiles);

# square determining part
# -----------------------
# we're computing a 'distance field' here.

$fac = remap($tiles[0], .5, 0.5*$radius, 1*$softness, 2);
# comment out the following to make this a lines-script.
$fac *= remap($tiles[1], .5, 0.5*$radius, 1*$softness, 2);


# filling the shape
# -----------------

$color = ccurve($fac, 0, $color2, 4, 1, $color1, 4);

# and we return the color

$color

Truchet

Classic

$ratiov = $h/$w;
$uv = [$u, $v*$ratiov, 0];
$scale2 = 10; #0,50
$color1 = [0,0,0];
$color2 = [1,1,1];

$celval = cellnoise($uv*$scale2);

#circle determining part

$distance = ($uv*$scale2);

$angle = floor($celval*4)*90;
$distance = rotate($distance, [0,0,1], rad($angle));
$distance -= floor($distance);

$dotproduct = (distance[0]+distance[1]);

$fac = smoothstep(.999, 1.001, $dotproduct);

$color = $color2;
if ($fac>0) {
 $color = ccurve($fac,0,$color1,4,1,$color2,4);
}

$color

Maze

$ratiov = $h/$w;
$uv = [$u, $v*$ratiov, 0];
$scale2 = 10; #0,50
$color1 = [0,0,0];
$color2 = [1,1,1];

$celval = cellnoise($uv*$scale2);

$distance = ($uv*$scale2);

$angle = floor($celval*4)*90;
$distance = rotate($distance, [0,0,1], rad($angle));
$distance -= floor($distance);

$dotproduct = (distance[0]+distance[1]);

$border = 0.01;
$fac = smoothstep(1-$border, 1.0, $dotproduct);
$fac += smoothstep(1+$border, 1.0, $dotproduct);

$color = ccurve($fac,0,$color1,4,1,$color2,4);

$color

Halfround:

$ratiov = $h/$w;
$uv = [$u, $v*$ratiov, 0];
$scale2 = 10; #0,50
$color1 = [0,0,0];
$color2 = [1,1,1];

$celval = cellnoise($uv*$scale2);

$distance = ($uv*$scale2);

$angle = floor($celval*4)*90;
$distance2 = rotate($distance, [0,0,1], rad($angle+180));
$distance = rotate($distance, [0,0,1], rad($angle));
$distance -= floor($distance);
$distance2 -= floor($distance2);

$dotproduct = dot($distance,$distance)*4;
$dotproduct2 = dot($distance2,$distance2)*4;

$border = 0.05;#0, .21
$lowerBorder = 1-$border;
$upperBorder = 1+$border;
$fac = gaussstep($lowerBorder, 1.0, $dotproduct);
$fac += gaussstep($upperBorder, 1.0, $dotproduct);
$fac += gaussstep($lowerBorder, 1.0, $dotproduct2);
$fac += gaussstep($upperBorder, 1.0, $dotproduct2);

$color = ccurve(1-$fac,0.0,$color1,4,1,$color2,4);

$color
4 Likes

Ok, here we go…

I made small scripts that basically just expose the different inbuilt functions and add some basic scale and rotation controls:

Voronoi cells

# Basic usage of the inbuilt seexpr voronoi function.
# This just exposes each of the variables in the voronoi function for editing. Also adds rotation and scaling.

# UV vector manipulation
# ----------------------

# Construction of the UV vector. $u and $v are the normalized x and y coordinates for the image. This gives non-square results by default, so we calculate the ratio and then multiply the relevant dimension with said ratio to get a square result from the algorithms.
# The zCoordinate can be set manually in many of the inbuilt noise functions, as these produce 3d noise, but we have a 2d plane. So allowing people to manipulate the last dimension manually allows them to access other slices of the 3d noise.

$ratio = $h/$w;
$zCoord = 0; #0.0, 1.0
$uv = [$u, $v*$ratio, zCoord];

# We multiply the subdivisions with the uv vector to give a sense of zooming out.

$subdivisions = 10; # 1, 100;
$uv *= $subdivisions;

# We also rotate the uv coordinate around the z-axis, [0,0,1]. 

$rotation = 0; #0, 359;
$uv = rotate($uv, [0,0,1], rad(rotation));


# Voronoi Function
# ----------------

# The type of voronoi algorithm as explain in the manual.
$type = 2; #1, 5

# The jitter, low jitter gives almost grid like appearance, high jitter a
$jitter = 1; #0.0, 1.0

# Some variables for introducing some fractal brownian motion into the voronoi cells.

# The strength of the FBM effect.
$fbmScale = 0;

# Controls the frequencies.
$fbmOctaves = 4; #0, 10

# Spacing between the frequencies - a value of 2 means each octave is twice the previous frequency.
$fbmLacunarity = 2; #0, 10;

# controls how much each frequency is scaled relative to the previous frequency.
$fbmGain = 0.5; #0, 10.0

# Finally we input all the variables into the function to get a factor.
$fac = voronoi($uv, $type, $jitter, $fbmScale, $fbmOctaves, $fbmLacunarity, $fbmGain);


# Color
# -----
# We put the factor into a gradient to get the color.

$color = ccurve($fac,0,[0.141,0.059,0.051],4,0.709957,[1,1,0],4,1,[0.976,0.976,0.976],4,0.212121,[0.333333,0,0],4,0.519481,[1,0.333333,0],4);

# return the color.
$color

Perlin Noise 1983

# Basic usage of the inbuilt seexpr noise function.
# This is based on the original 1983 perlin noise.

# UV vector manipulation
# ----------------------

# Construction of the UV vector. $u and $v are the normalized x and y coordinates for the image. This gives non-square results by default, so we calculate the ratio and then multiply the relevant dimension with said ratio to get a square result from the algorithms.
# The zCoordinate can be set manually in many of the inbuilt noise functions, as these produce 3d noise, but we have a 2d plane. So allowing people to manipulate the last dimension manually allows them to access other slices of the 3d noise.

$ratio = $h/$w;
$zCoord = 0; #0.0, 1.0
$uv = [$u, $v*$ratio, zCoord];

# We multiply the subdivisions with the uv vector to give a sense of zooming out.

$subdivisions = 67; # 1, 100;
$uv *= $subdivisions;

# We also rotate the uv coordinate around the z-axis, [0,0,1]. 

$rotation = 0; #0, 359;
$uv = rotate($uv, [0,0,1], rad(rotation));


# Noise Function
# ----------------


# Finally we input all the UV into the function to get a factor.
$fac = noise($uv);


# Color
# -----
# We put the factor into a gradient to get the color.

$color = ccurve($fac,0,[0.141,0.059,0.051],4,0.709957,[0.576471,0.87451,1],4,1,[0.976,0.976,0.976],4,0.320346,[0.266667,0.117647,0.713725],4);

# return the color.
$color

Cellnoise

# Basic usage of the inbuilt seexpr cellnoise function.
# This is based on the prman cellnoise function

# UV vector manipulation
# ----------------------

# Construction of the UV vector. $u and $v are the normalized x and y coordinates for the image. This gives non-square results by default, so we calculate the ratio and then multiply the relevant dimension with said ratio to get a square result from the algorithms.
# The zCoordinate can be set manually in many of the inbuilt noise functions, as these produce 3d noise, but we have a 2d plane. So allowing people to manipulate the last dimension manually allows them to access other slices of the 3d noise.

$ratio = $h/$w;
$zCoord = 0; #0.0, 1.0
$uv = [$u, $v*$ratio, zCoord];

# We multiply the subdivisions with the uv vector to give a sense of zooming out.

$subdivisions = 67; # 1, 100;
$uv *= $subdivisions;

# We also rotate the uv coordinate around the z-axis, [0,0,1]. 

$rotation = 0; #0, 359;
$uv = rotate($uv, [0,0,1], rad(rotation));


# Cellnoise Function
# ------------------


# Finally we input all the UV into the function to get a factor.
$fac = cellnoise($uv);


# Color
# -----
# We put the factor into a gradient to get the color.

$color = ccurve($fac,0,[0.141,0.059,0.051],4,0.709957,[0.996078,1,0.572549],4,1,[0.976,0.976,0.976],4,0.320346,[0.713725,0.427451,0.647059],4);

# return the color.
$color

Fractal Brownian Motion

# Basic usage of the inbuilt seexpr FBM function.
# This just exposes each of the variables in the FBM function for editing. Also adds rotation and scaling.

# UV vector manipulation
# ----------------------

# Construction of the UV vector. $u and $v are the normalized x and y coordinates for the image. This gives non-square results by default, so we calculate the ratio and then multiply the relevant dimension with said ratio to get a square result from the algorithms.
# The zCoordinate can be set manually in many of the inbuilt noise functions, as these produce 3d noise, but we have a 2d plane. So allowing people to manipulate the last dimension manually allows them to access other slices of the 3d noise.

$ratio = $h/$w;
$zCoord = 0; #0.0, 1.0
$uv = [$u, $v*$ratio, zCoord];

# We multiply the subdivisions with the uv vector to give a sense of zooming out.

$subdivisions = 23; # 1, 100;
$uv *= $subdivisions;

# We also rotate the uv coordinate around the z-axis, [0,0,1]. 

$rotation = 0; #0, 359;
$uv = rotate($uv, [0,0,1], rad(rotation));


# FBM Function
# ----------------

# Controls the frequencies.
$fbmOctaves = 2; #0, 10

# Spacing between the frequencies - a value of 2 means each octave is twice the previous frequency.
$fbmLacunarity = 1; #0, 10;

# controls how much each frequency is scaled relative to the previous frequency.
$fbmGain = 0.5; #0, 10.0

# Finally we input all the variables into the function to get a factor.
$fac = fbm($uv, $fbmOctaves, $fbmLacunarity, $fbmGain);


# Color
# -----
# We put the factor into a gradient to get the color.

$color = ccurve($fac,0,[0.141,0.059,0.051],4,0.709957,[0.556863,1,0.913725],4,1,[0.976,0.976,0.976],4,0.212121,[0.105882,0.164706,0.266667],4);

# return the color.
$color

Turbulence

# Basic usage of the inbuilt seexpr turbulence function.
# This just exposes each of the variables in the turbulence function for editing. Also adds rotation and scaling.

# UV vector manipulation
# ----------------------

# Construction of the UV vector. $u and $v are the normalized x and y coordinates for the image. This gives non-square results by default, so we calculate the ratio and then multiply the relevant dimension with said ratio to get a square result from the algorithms.
# The zCoordinate can be set manually in many of the inbuilt noise functions, as these produce 3d noise, but we have a 2d plane. So allowing people to manipulate the last dimension manually allows them to access other slices of the 3d noise.

$ratio = $h/$w;
$zCoord = 0; #0.0, 1.0
$uv = [$u, $v*$ratio, zCoord];

# We multiply the subdivisions with the uv vector to give a sense of zooming out.

$subdivisions = 23; # 1, 100;
$uv *= $subdivisions;

# We also rotate the uv coordinate around the z-axis, [0,0,1]. 

$rotation = 0; #0, 359;
$uv = rotate($uv, [0,0,1], rad(rotation));


# Tubulence Function
# ------------------

# Controls the frequencies.
$fbmOctaves = 2; #0, 10

# Spacing between the frequencies - a value of 2 means each octave is twice the previous frequency.
$fbmLacunarity = 1; #0, 10;

# controls how much each frequency is scaled relative to the previous frequency.
$fbmGain = 0.5; #0, 10.0

# Finally we input all the variables into the function to get a factor.
$fac = turbulence($uv, $fbmOctaves, $fbmLacunarity, $fbmGain);


# Color
# -----
# We put the factor into a gradient to get the color.

$color = ccurve($fac,0,[0.141,0.059,0.051],4,0.709957,[0.980392,0.662745,1],4,1,[0.976,0.976,0.976],4,0.212121,[0.105882,0.164706,0.266667],4);

# return the color.
$color

And the same song, but now it’s the colornoise functions.

Color Voronoi

# Basic usage of the inbuilt seexpr color voronoi function.
# This just exposes each of the variables in the voronoi function for editing. Also adds rotation and scaling.

# UV vector manipulation
# ----------------------

# Construction of the UV vector. $u and $v are the normalized x and y coordinates for the image. This gives non-square results by default, so we calculate the ratio and then multiply the relevant dimension with said ratio to get a square result from the algorithms.
# The zCoordinate can be set manually in many of the inbuilt noise functions, as these produce 3d noise, but we have a 2d plane. So allowing people to manipulate the last dimension manually allows them to access other slices of the 3d noise.

$ratio = $h/$w;
$zCoord = 0; #0.0, 1.0
$uv = [$u, $v*$ratio, zCoord];

# We multiply the subdivisions with the uv vector to give a sense of zooming out.

$subdivisions = 10; # 1, 100;
$uv *= $subdivisions;

# We also rotate the uv coordinate around the z-axis, [0,0,1]. 

$rotation = 0; #0, 359;
$uv = rotate($uv, [0,0,1], rad(rotation));


# Voronoi Function
# ----------------

# The type of voronoi algorithm as explain in the manual.
$type = 1; #1, 5

# The jitter, low jitter gives almost grid like appearance, high jitter a
$jitter = 1; #0.0, 1.0

# Some variables for introducing some fractal brownian motion into the voronoi cells.

# The strength of the FBM effect.
$fbmScale = 0.21533;

# Controls the frequencies.
$fbmOctaves = 4; #0, 10

# Spacing between the frequencies - a value of 2 means each octave is twice the previous frequency.
$fbmLacunarity = 2; #0, 10;

# controls how much each frequency is scaled relative to the previous frequency.
$fbmGain = 0.5; #0, 10.0

# Color
# -----
# We put all the variables into the color voronoi function to get the color.

$color = cvoronoi($uv, $type, $jitter, $fbmScale, $fbmOctaves, $fbmLacunarity, $fbmGain);

# return the color.
$color

Color Perlin Noise 1983

# Basic usage of the inbuilt seexpr color noise function.
# This is based on the original 1983 perlin noise.

# UV vector manipulation
# ----------------------

# Construction of the UV vector. $u and $v are the normalized x and y coordinates for the image. This gives non-square results by default, so we calculate the ratio and then multiply the relevant dimension with said ratio to get a square result from the algorithms.
# The zCoordinate can be set manually in many of the inbuilt noise functions, as these produce 3d noise, but we have a 2d plane. So allowing people to manipulate the last dimension manually allows them to access other slices of the 3d noise.

$ratio = $h/$w;
$zCoord = 0; #0.0, 1.0
$uv = [$u, $v*$ratio, zCoord];

# We multiply the subdivisions with the uv vector to give a sense of zooming out.

$subdivisions = 67; # 1, 100;
$uv *= $subdivisions;

# We also rotate the uv coordinate around the z-axis, [0,0,1]. 

$rotation = 0; #0, 359;
$uv = rotate($uv, [0,0,1], rad(rotation));



# Function
# --------
# Finally we input the UV into the color function to get a color.

$color = cnoise($uv);

# return the color.
$color

Color Cellnoise

# Basic usage of the inbuilt seexpr color cellnoise function.
# This is based on the prman cellnoise function

# UV vector manipulation
# ----------------------

# Construction of the UV vector. $u and $v are the normalized x and y coordinates for the image. This gives non-square results by default, so we calculate the ratio and then multiply the relevant dimension with said ratio to get a square result from the algorithms.
# The zCoordinate can be set manually in many of the inbuilt noise functions, as these produce 3d noise, but we have a 2d plane. So allowing people to manipulate the last dimension manually allows them to access other slices of the 3d noise.

$ratio = $h/$w;
$zCoord = 0; #0.0, 1.0
$uv = [$u, $v*$ratio, zCoord];

# We multiply the subdivisions with the uv vector to give a sense of zooming out.

$subdivisions = 67; # 1, 100;
$uv *= $subdivisions;

# We also rotate the uv coordinate around the z-axis, [0,0,1]. 

$rotation = 0; #0, 359;
$uv = rotate($uv, [0,0,1], rad(rotation));


# Cellnoise Function
# ------------------

# We input all the uv into the function to get a color.

$color = ccellnoise($uv);

# return the color.
$color

Color Fractal Brownian Motion

# Basic usage of the inbuilt seexpr color FBM function.
# This just exposes each of the variables in the FBM function for editing. Also adds rotation and scaling.

# UV vector manipulation
# ----------------------

# Construction of the UV vector. $u and $v are the normalized x and y coordinates for the image. This gives non-square results by default, so we calculate the ratio and then multiply the relevant dimension with said ratio to get a square result from the algorithms.
# The zCoordinate can be set manually in many of the inbuilt noise functions, as these produce 3d noise, but we have a 2d plane. So allowing people to manipulate the last dimension manually allows them to access other slices of the 3d noise.

$ratio = $h/$w;
$zCoord = 0; #0.0, 1.0
$uv = [$u, $v*$ratio, zCoord];

# We multiply the subdivisions with the uv vector to give a sense of zooming out.

$subdivisions = 23; # 1, 100;
$uv *= $subdivisions;

# We also rotate the uv coordinate around the z-axis, [0,0,1]. 

$rotation = 0; #0, 359;
$uv = rotate($uv, [0,0,1], rad(rotation));


# FBM Function
# ----------------

# Controls the frequencies.
$fbmOctaves = 2; #0, 10

# Spacing between the frequencies - a value of 2 means each octave is twice the previous frequency.
$fbmLacunarity = 1; #0, 10;

# controls how much each frequency is scaled relative to the previous frequency.
$fbmGain = 0.5; #0, 10.0


# Color
# -----
# Finally we input all the variables into the function to get a color.

$color = cfbm($uv, $fbmOctaves, $fbmLacunarity, $fbmGain);

# return the color.
$color

Color Turbulence

# Basic usage of the inbuilt seexpr color turbulence function.
# This just exposes each of the variables in the turbulence function for editing. Also adds rotation and scaling.

# UV vector manipulation
# ----------------------

# Construction of the UV vector. $u and $v are the normalized x and y coordinates for the image. This gives non-square results by default, so we calculate the ratio and then multiply the relevant dimension with said ratio to get a square result from the algorithms.
# The zCoordinate can be set manually in many of the inbuilt noise functions, as these produce 3d noise, but we have a 2d plane. So allowing people to manipulate the last dimension manually allows them to access other slices of the 3d noise.

$ratio = $h/$w;
$zCoord = 0; #0.0, 1.0
$uv = [$u, $v*$ratio, zCoord];

# We multiply the subdivisions with the uv vector to give a sense of zooming out.

$subdivisions = 23; # 1, 100;
$uv *= $subdivisions;

# We also rotate the uv coordinate around the z-axis, [0,0,1]. 

$rotation = 0; #0, 359;
$uv = rotate($uv, [0,0,1], rad(rotation));


# Tubulence Function
# ------------------

# Controls the frequencies.
$fbmOctaves = 2; #0, 10

# Spacing between the frequencies - a value of 2 means each octave is twice the previous frequency.
$fbmLacunarity = 1; #0, 10;

# controls how much each frequency is scaled relative to the previous frequency.
$fbmGain = 0.5; #0, 10.0


# Color
# -----
# We put the factor into the color turbulence to get the color.

$color = cturbulence($uv, $fbmOctaves, $fbmLacunarity, $fbmGain);

# return the color.
$color
5 Likes

Some gradients this time, and some stuff I had lying around that I hadn’t published yet.

The gradients in particular were something I really wanted to get done, as the ‘dirty screentone’ gradient is in effect a combination of several scripts, which in turn shows how you can combine and layer scripts for cool effects, and I really wanted to get that across.

Linear Gradient

# Basic angled linear gradient.
# -----------------------------
# By itself not a terribly interesting generator, but a useful building block in more complex scripts.

# UV vector manipulation
# ----------------------

# Construction of the UV vector. $u and $v are the normalized x and y coordinates for the image. This gives non-square results by default, so we calculate the ratio and then multiply the relevant dimension with said ratio to get a square result from the algorithms.

$ratio = $h / $w;
$uv = [$u, $v * $ratio, 0];

# Linear Gradient
# ---------------
# We're going to draw a linear angled gradient. Let's get the angle.

$rotation = 45; #0, 359

# The gradient is going to need a central point through which it's direction is crossing.

$centerX = 0.5;
$centerY = 0.5;

$center = [$centerX, $centerY*$ratio, 0];

# Subtract center from uv to get coordinate relative to center.
$uv -= $center;

# Use trig to get the value of this relative point on the gradient.

$fac = cos(rad($rotation))* $uv[0];
$fac += sin(rad($rotation))* $uv[1];

# Normalize the value into 0-1 space so we use the whole gradient.

$fac = clamp(0.5 - $fac, 0, 1);

# Color
# -----
# We put the factor into a gradient to get the color.

$color = ccurve($fac,0,[0.141,0.059,0.051],4,0.709957,[0.576471,0.87451,1],4,1,[0.976,0.976,0.976],4,0.320346,[0.196078,0.262745,0.4],4);

# return the color.
$color

Dirty Screentone

Here’s a combo between that gradient and the screentone dots and cell noise to create a unique dirty effect.

    # Angled, 'dirty' screentone.
# ---------------------------
# This combines a linear gradient, a cellnoise function, and the tiled dots scripts. We use both the cellnoise function and the gradient to additionally manipulate the radius of the dots. Combining different parts of scripts together is where little shader languages like SeExpr really shine.


# UV vector manipulation
# ----------------------

# Construction of the UV vector. $u and $v are the normalized x and y coordinates for the image. This gives non-square results by default, so we calculate the ratio and then multiply the relevant dimension with said ratio to get a square result from the algorithms.

$ratio = $h/$w;
$uv = [$u, $v*$ratio, 0];

# Everything for the dots.

# These are all the configurable variables. There's no reason why they're all
# assembled here besides making it easier to read the script. Values inside the
# comments are used by SeExpr widgets to determine the upper and lower limits.

$radius = 2; #0.0, 5.0
$lines = 60; #1, 300
$size = $lines;
$angle = 35; #0, 359
$softness = 0.2; #0.001, 1.0
$color1 = [0,0,0];
$color2 = [1,1,1];


# Linear Gradient
# ---------------

# We're going to draw a linear angled gradient.

$gradientRotation = 45; #0, 359

# The gradient is going to need a central point through which it's direction is crossing.

$centerX = 0.5;
$centerY = 0.5;

$center = [$centerX, $centerY*$ratio, 0];

$gradientUV = $uv - $center;

$gradientFac = cos(rad($gradientRotation))*$gradientUV[0];
$gradientFac += sin(rad($gradientRotation))*$gradientUV[1];

$gradientFac = clamp(0.5-$gradientFac, 0, 1);

# Applying the gradient to the radius...
$gradientFactor = 1;
$radius *= compress($gradientFac, 1-$gradientFactor, 1+$gradientFactor);

# Tiling part.
# ------------
# we'll construct the position of the current pixel
# in a pattern from the $uv

$tiles = $uv * size;
$repeat = boxstep(1 , fmod( $tiles[1], 1.0) );

# reassemble the tiled vector;

$tiles = [ $tiles[0] + $repeat, $tiles[1], 0];

# Now we rotate the tiled vector, 2d rotation uses the z-axis, that's [0, 0, 1].

$tiles = rotate($tiles, [0, 0, 1], rad($angle));

# 'Dirty' factor
# --------------
# If we input a multiplied and rotated uv into the cellnoise
# function, we can use that to make the radius, and thus the
# circles a little 'dirty'.
$dirtyFactor = 0.3;
$radius *= compress(cellnoise($tiles), 1-$dirtyFactor, 1+$dirtyFactor);

# Here we remove the offset from the tiled vector.
$tiles -= floor($tiles);

# circle determining part
# -----------------------
# we're computing a 'distance field' here.

$dist = $tiles - [0.5, 0.5, 0];
$dotproduct = dot(dist, dist) * 4;
$leftSide  = (1.0 - (0.5 * $softness)) * $radius;
$rightSize = (1.0 + (0.5 * $softness)) * $radius;
$fac = smoothstep($leftSide, $rightSize, $dotproduct);

# filling the shape
# -----------------
# we only want to use color interpolation if we're sure our distance
# factor is above 0, otherwise it'll need to be color 2.

if ($fac>0) {
$color = ccurve($fac, 0, $color1, 4, 1, $color2, 4);
} else {
$color = $color2;
}

# return the color.
$color

Conical Gradient

Not too happy with this one, as I can’t seem to figure out how to make the center possible to modify thanks to that hack…

# Conical Gradient.
# -----------------
# Creates a conical gradient by using the angle of the current pixel to the center.

# Construction of the UV vector. $u and $v are the normalized x and y coordinates for the image. This gives non-square results by default, so we calculate the ratio and then multiply the relevant dimension with said ratio to get non-stretched results from the algorithms.
$ratio = $h/$w;
$uv = [$u, $v*$ratio, 0];

# Center of the image.
$center = [.5, .5*$ratio, 0];

# Figure out how many repeats the gradient should have.
$repeats = 12;#0, 24;
$slice = 360/$repeats;

# Find the angle between the current pixel and the image center.
$relPixel = $uv-$center;
$curAngle = angle($center, $relPixel);

# convoluted thing to flip the angle right.
if (((1-$u)+$v)>1) {
$curAngle = (2*PI)-$curAngle;
}

# figure out in which section of the slice the pixel is.
$fac = fmod(deg($curAngle), $slice)/$slice;

# Colors
# ------

$color = ccurve($fac,0,[1,0,0],4,1,[1,0,0],4,0.5,[0,1,1],4,0.66,[0,0,1],4,0.33,[0,1,0],4,0.16,[1,1,0],4,0.83,[1,0,1],4);

# and we return the color

$color

Weird rectangle pattern

# This generates a pattern of concentric rectangles where the center is the same for each.

$outlines = 14; #1, 20;
$color1 = [0,0,0];
$color2 = [1,1,1];

# UV vector manipulation
# ----------------------

# Construction of the UV vector. $u and $v are the normalized x and y coordinates for the image. This gives non-square results by default, so we calculate the ratio and then multiply the relevant dimension with said ratio to get a square result from the algorithms.
# The zCoordinate can be set manually in many of the inbuilt noise functions, as these produce 3d noise, but we have a 2d plane. So allowing people to manipulate the last dimension manually allows them to access other slices of the 3d noise.

$ratio = $h/$w;
$zCoord = 0; #0.0, 1.0
$uv = [$u, $v*$ratio, zCoord];

# We multiply the subdivisions with the uv vector to give a sense of zooming out.

$subdivisions = 12; # 1, 100;
$uv *= $subdivisions;

# We also rotate the uv coordinate around the z-axis, [0,0,1]. 

$rotation = 0; #0, 359;
$uv = rotate($uv, [0,0,1], rad(rotation));

$point = pvoronoi($uv, 4, 0);

#square determining part

$distance = ($uv)-$point;

$fac = remap($distance[0], 0, .0, 2, 0);
$fac *= remap($distance[1], 0, .0, 2, 0);
$fac *=5;

#filling the squares

$fac = fmod(sin((1-$fac)*$outlines) , 1);
$color = ccurve($fac,0,$color1,4,1,$color2,4);

$color

Circles with voronoi

This one is slightly different from the other circle pattern.

# This generates a pattern of concentric circles where the center is the same for each.

$outlines = 8; #1, 20;
$color1 = [0,0,0];
$color2 = [1,1,1];

# UV vector manipulation
# ----------------------

# Construction of the UV vector. $u and $v are the normalized x and y coordinates for the image. This gives non-square results by default, so we calculate the ratio and then multiply the relevant dimension with said ratio to get a square result from the algorithms.
# The zCoordinate can be set manually in many of the inbuilt noise functions, as these produce 3d noise, but we have a 2d plane. So allowing people to manipulate the last dimension manually allows them to access other slices of the 3d noise.

$ratio = $h/$w;
$zCoord = 0; #0.0, 1.0
$uv = [$u, $v*$ratio, zCoord];

# We multiply the subdivisions with the uv vector to give a sense of zooming out.

$subdivisions = 10; # 1, 100;
$uv *= $subdivisions;

# We also rotate the uv coordinate around the z-axis, [0,0,1]. 

$rotation = 0; #0, 359;
$uv = rotate($uv, [0,0,1], rad(rotation));

# We get the center point of the voronoi cells, to use as a circle center.

$point = pvoronoi($uv, 4, 0);

#circle determining part

$distance = ($uv)-$point;

$fac = remap($distance[0], 0, .0, 1.1, 1);
$fac *= remap($distance[1], 0, .0, 1.1, 1);
$fac *=5;

#filling the circles

$fac = fmod(sin((1-$fac)*$outlines) , 1.)*($fac*.5);
$color = ccurve($fac,0,$color1,4,1,$color2,4);

$color

Random rotated hatches.

# tiled hatches.
# --------------
# Uses cellnoise to randomly rotate some straight lines 90°.

$ratiov = $h/$w;
$uv = [$u, $v*$ratiov, 0];
$scale2 = 10; #0,50
$color1 = [0,0,0];
$color2 = [1,1,1];

#use cellnoise to get a random value.

$celval = cellnoise($uv*$scale2);

# Draw the lines
#---------------

$distance = ($uv*$scale2)-[.5,.5,0];

# angle the lines using the cellnoise.

$angle = floor($celval*4)*90;
$distance = rotate($distance, [0,0,1], rad($angle));

$fac = fmod(sin($distance*(50-$scale2)), 1);

# Color
#------
$color = ccurve($fac,0,$color1,4,1,$color2,4);

$color

EDIT: fixed the dirty screentone as the cellnoise was using the wrong input…

4 Likes

Two more cellnoise adjusted textures and a moiré pattern:

Dirty Squares/Lines

# SeExpr script to draw tiled squares not unlike screentones, incoporates a radial graidient and uses cellnoise to introduce some dirtyness.

# [$u, $v, 0] is a vector constructed from the proportional horizontal and
# vertical positions ($u and $v) of the output pixel and 0 as the Z-value.
# We multiply the proportional vertical by the ratio we can get from the height
# and width of the image, so that our computed shapes are in a square aspect
# ratio, they'd get streched otherwise.

$ratiov = $h / $w;
$uv = [$u, $v * $ratiov, 0];

# These are all the configurable variables. There's no reason why they're all
# assembled here besides making it easier to read the script. Values inside the
# comments are used by SeExpr widgets to determine the upper and lower limits.

$radius = 0.5; #0.0, 1.0
$lines = 74; #1, 300
$size = $lines;
$angle = 217; #0, 359
$softness = 0.3; #0.001, 1.0
$color1 = [0,0,0];
$color2 = [1,1,1];

# Radial Gradient
#-----------------

$centerX = 0.5;
$centerY = 0.5;
$gradientUV = $uv-[$centerX, $centerY*$ratiov, 0];
$gradientFac = dot(gradientUV, gradientUV);
$gradientFactor = 1;
$radius *= compress($gradientFac, 1-$gradientFactor, 1+$gradientFactor);


# Tiling part.
# ------------
# we'll construct the position of the current pixel
# in a pattern from the $uv

$tiles = $uv * size;
$repeat = boxstep(1 , fmod( $tiles[1], 1.0) );

# reassemble the tiled vector;

$tiles = [ $tiles[0] + $repeat, $tiles[1], 0];

# Now we rotate the tiled vector, 2d rotation uses the z-axis, that's [0, 0, 1].

$tiles = rotate($tiles, [0, 0, 1], rad($angle));

# Here we remove the offset from the tiled vector.

$tileUV = $tiles;
$tiles -= floor($tiles);

# 'Dirty' factor
# --------------
# If we input a multiplied and rotated uv into the cellnoise
# function, we can use that to make the radius, and thus the
# circles a little 'dirty'.
$dirtyFactor = 1;
$radius *= compress(cellnoise([$tileUV[0], 0, 0]), 1-$dirtyFactor, 1+$dirtyFactor);


# square determining part
# -----------------------
# we're computing a 'distance field' here.

$fac = remap($tiles[0], 0.5, 0.5*$radius, 1*$softness, 2);
$fac *= remap($tiles[1], .5, 0.5*$radius, 1*$softness, 2);


# filling the shape
# -----------------

$color = ccurve($fac, 0, $color2, 4, 1, $color1, 4);

# and we return the color

$color

Radial Lines

# Radial Lines
# Using a variety of techniques, this combines a concentric gradient with a radial gradient and some cell noise to create radial lines.

$ratio = $h/$w;
$uv = [$u, $v*$ratio, 0];
$center = [.5, .5*$ratio, 0];
$subdivs = 120;#0, 360;
$slice = 360/$subdivs;
$color1 = [0,0,0];
$color2 = [1,1,1];

# Radial Gradient
#-----------------

$gradientUV = $uv-$center;
$gradientFac = dot(gradientUV, gradientUV);
$gradientFac = curve($gradientFac,0,0,4,1,1,1);
$gradientFactor = 1;

$radius = compress($gradientFac, 1-$gradientFactor, 1+$gradientFactor);

# Find the angle between the current pixel and the image center.
$curAngle = angle($center, $uv-$center);

# convoluted thing to flip the angle right.
if (((1-$u)+$v)>1) {
$curAngle = (2*PI)-$curAngle;
}

$fac = fmod(deg($curAngle), $slice)/$slice;

$fac = invert(abs(0.5-($fac))*2);

# Dirty factor using cellnoise.
$dirtyFactor = 0.5;
$dirtyAdjust = 3;#0, 10;
$random = cellnoise([deg($curAngle)/slice, $dirtyAdjust, 0]);
$random = compress($random, 1-$dirtyFactor, 1+$dirtyFactor);


$fac = fac*random*radius;
$fac = gaussstep(.9, 1.1, $fac);


# filling the shape
# -----------------

$color = ccurve($fac,0,$color2,2, 1,$color1,2);
if (fac <= 0) { color = color1;}

# and we return the color

$color

Moiré Pattern

# SeExpr script to create a moire pattern of two grids of dots.
# Moiré patterns are often brought up as mistakes, but can be invoked deliberately for cool effects.

# [$u, $v, 0] is a vector constructed from the proportional horizontal and
# vertical positions ($u and $v) of the output pixel and 0 as the Z-value.
# We multiply the proportional vertical by the ratio we can get from the height
# and width of the image, so that our computed shapes are in a square aspect
# ratio, they'd get streched otherwise.

$ratiov = $h / $w;
$uv = [$u, $v * $ratiov, 0];

# These are all the configurable variables. There's no reason why they're all
# assembled here besides making it easier to read the script. Values inside the
# comments are used by SeExpr widgets to determine the upper and lower limits.

$radius = 0.3; #0.0, 5.0
$lines = 60; #1, 300
$size = $lines;
$angle = 30; #0, 359
$moireOffset = 30; #0, 90
$softness = 0.2; #0.001, 1.0
$color1 = [0,0,0];
$color2 = [1,1,1];


# Tiling part.
# ------------
# we'll construct the position of the current pixel
# in a pattern from the $uv

$tiles = ($uv-[0.5, 0.5*$ratiov, 0]) * size;
$repeat = boxstep(1 , fmod( $tiles[1], 1.0) );

# reassemble the tiled vector;

$tiles = [ $tiles[0] + $repeat, $tiles[1], 0];

# Now we rotate the tiled vector, 2d rotation uses the z-axis, that's [0, 0, 1].

$tiles2= rotate($tiles, [0, 0, 1], rad($angle+$moireOffset));
$tiles = rotate($tiles, [0, 0, 1], rad($angle));

# Here we remove the offset from the tiled vector.

$tiles -= floor($tiles);
$tiles2 -= floor($tiles2);

# circle determining part
# -----------------------
# we're computing a 'distance field' here.

$dist = $tiles - [0.5, 0.5, 0];
$dotproduct = dot(dist, dist) * 4;
$leftSide  = (1.0 - (0.5 * $softness)) * $radius;
$rightSize = (1.0 + (0.5 * $softness)) * $radius;
$fac = smoothstep($leftSide, $rightSize, $dotproduct);

$dist = $tiles2 - [0.5, 0.5, 0];
$dotproduct = dot(dist, dist) * 4;
$fac2 = smoothstep($leftSide, $rightSize, $dotproduct);

# filling the shape
# -----------------
# we only want to use color interpolation if we're sure our distance
# factor is above 0, otherwise it'll need to be color 2.

if ($fac>0) {
$color = ccurve($fac, 0, $color1, 4, 1, $color2, 4);
} else {
$color = $color2;
}

if ($fac2>0) {
$colorb = ccurve($fac2, 0, $color1, 4, 1, $color2, 4);
} else {
$colorb = $color2;
}

color = min (color, colorb);

# and we return the color

$color

Of the examples deevad selected, one of them is a fractal flame, which cannot be done via seexpr, but maybe we could integrate flame3 somehow?

Put tiled into perspective is… within reach, but… making it’s UX make sense is it’s own mountain :slight_smile:

I also tried to search for this weird circular dot pattern I saw. Seems that this actually require poisson distribution, or as a hack, we reuse the penrose tiles, so I added some options to generate dots in the multigrid…

I think I’ve hit most of what I wanted to do…

5 Likes

Uploaded all of Wolthera and David’s examples here!

https://dump.amyspark.me/Krita_Artists’_SeExpr_examples.bundle

1 Like

These are some great examples. Thanks @wolthera for doing all this work coming up with these.

Spiral - logarithmic

Exposes a bunch of parameters and different rendering styles to choose from.

$moveHorizontal = 0; # -1.0, 1.0
$moveVertical = 0; # -1.0, 1.0
$zoom = 1; # 1.0, 10.0
$arms = 1; # 1,32
$mirror = 1; # 0,1
# default growth value 0.61803 (=1/φ =1-φ) is conjugate of golden ratio φ=1.61803...
$growth = 0.61803; # 0.0, 3.0
$drawStyle = 2; # 1, 3
$useGradient = 1; # 0, 1
$color = 0*1; # calculation to hide it from UI

# UV centering, offset, scaling, aspect correction
$ratiov = $h/$w;
$u = ($u-.5 - $moveHorizontal)/$zoom;
$v = ($v-.5 - $moveVertical)*$ratiov/$zoom;

$pxSize = 1/$w; # relative pixel size

$dist = hypot($u,$v); # distance from UV center
$ang = atan2($u,$v); # angle towards UV center
if ($mirror) {$ang = -atan2($u,$v);} # choice to flip growth direction

$fBase = $arms*(log($dist)/$growth + $ang); # function for 1 spiral period
$fOverlay = log(1+$arms*$pxSize/($dist*$zoom*log(1+$growth))); # used for correcting sharp edges

# allow switching between different styles
if ($drawStyle == 1) {
  $color = -sin($fBase)*0.5+0.5;
}
if ($drawStyle == 2) {
  $f1 = $fBase/(2*PI) % 1;
  # remove the edge aliasing caused by the modulo
  $f2 = $f1 - 0.1*$fOverlay;
  $color = $f1 - smoothstep($f1,$f2,1);
}
if ($drawStyle == 3) {
  $f1 = abs(sin($fBase));
  # keep constant line thickness with anti-aliasing
  $f2 = $f1 - 0.5*$fOverlay;
  $color = smoothstep($f2,$f1,0);
}

$color = max($color,0.0); # clamp to positive values

if ($useGradient) {
  $gradient = ccurve($color,0,[0,0,0.25],4,1,[1,1,1],4,0.5,[1,0.666667,0],4,0.25,[0.35,0,0.25],4);
  $color = $gradient;
}

$color

Spiral - simple

$moveHorizontal = 0; # -1.0, 1.0
$moveVertical = 0; # -1.0, 1.0
$zoom = 1; # 1.0, 10.0
$arms = 1; # 1,32
$mirror = 0; # 0,1
$drawStyle = 2; # 1, 3
$color = 0*1; # calculation to hide it from UI
$color1 =[0,0,0];
$color2 = [1,1,1];

# UV centering, offset, scaling, aspect correction
$ratiov = $h/$w;
$u = ($u-.5 - $moveHorizontal)/$zoom;
$v = ($v-.5 - $moveVertical)*$ratiov/$zoom;

$dist = hypot($u,$v); # distance from UV center
$ang = atan2($u,$v); # angle towards UV center

if ($mirror) {$ang = -atan2($u,$v);} # choice to flip growth direction

$color = sin($arms*$ang+$dist*100); # spiral sine function

# automatically converts to RGB vec3
if ($drawStyle == 1) {$color = $color1 + max($color,0)*$color2;}
# if drawStyle isn't 1, use ccurve to remap the sine function
$border=0.1/$zoom;
if ($drawStyle == 2) {$color = ccurve($color,-$border,$color1,4,$border,$color2,4);}
if ($drawStyle == 3) {$color = ccurve($color,-$border,$color1,4,0,$color2,4,$border,$color1,4);}

$color

Jewelry

Messing around with combining some periodic functions and a gradient map, also exposes a bunch of parameters to play with.
If anyone happens to know a nice and simple enough method to anti-alias or bandlimit those kind of functions in SeExpr, let me know.

$moveHorizontal = 0; # -1.0, 1.0
$moveVertical = 0.2; # -1.0, 1.0
$zoom = 3; # 1.0, 8.0
$mix = -0.8; # -1.0, 2.0
$mixOverlay = -0.2; # -2.0, 1.0
$f1Symmetry = 1.2; # 0.0, 2.0
$f2Frequency = 0.4; # 0.0, 2.0
$f2Crease = 0.5; # 0.0, 2.0
$f2Beam = 1.25; # 0.0, 2.0
$f2Mirror = 0; # 0, 1
$falloff = 1.25; # 0.0, 3.0

# UV centering, offset, scaling, aspect correction
$ratiov = $h/$w;
$u = ($u-.5 - $moveHorizontal)/$zoom;
$v = ($v-.5 - $moveVertical)*$ratiov/$zoom;

$pxSize = 1/$w; # relative pixel size

$ang = atan2($u,$v)/PI; # angle towards UV center between -1 to 1
$dist = hypot($u,$v)*100; # distance from UV center

$f1 = ($ang+$dist-1.5) % abs($ang) +$v+1/$dist;
if ($f1Symmetry) {
  $angMirror = atan2($u,-$v)/PI;
  $f1 += $f1Symmetry*(($angMirror+$dist-1.5) % abs($angMirror) -$v+1/$dist);
}

if ($f2Mirror) {
  $ang = atan2($u,-$v)/PI;
}
$f2 = 1/sin($f2Frequency*$dist*PI^($f2Crease*abs($ang)+1));
if ($f2Beam) {
  $f2 += $f2Beam*abs(1/($ang*3));
}

$color = mix($f1,$f2,$mix) + abs(($f1+$f2)/2)^$mixOverlay;

$dist ^= $falloff;

$gradient = ccurve(1/$dist * $color,0,[0.121569,0.027451,0.0431373],4,0.55,[0.764706,0.521569,0.239216],4,0.9,[1,0.909804,0.2],4,0.15,[0.894118,1,0.905882],4,0.35,[1,0.968627,0.490196],4,0.06,[0,0.0705882,0.32549],4,0.98,[1,1,0.796078],4,0.65,[0.454902,0.345098,0.270588],4,1,[1,1,0.498039],4);

$color = $gradient;
$color

Edit: Slight update to the spiral - simple one.

6 Likes