Slim is a node-base shader developing environment, dedicated to Renderman. It resembles Hypershade to some extends, but it's integrity with Renderman Shading Language (RSL) empowers artists to create and manipulate custom nodes. It this page I tried to create some image manipulation filters using TCL scripting and RSL.
|
Interface |
||
|
Slim interface consists of three main sections: |
|
|
Writing Custom Nodes |
|
|
slim 1 extensions cutter {
extensions fundza ali {
template color ColorFilter {
previewinfo {
shadingrate 1
objectsize 1
objectshape Plane
frame 1
}
#__custom parameters Begin____________
collection void ColorFilter {
state open
drawmode all
label {Color Filter}
description {Color Filter}
parameter string tex_name {
label "Texture Label"
description "Texture Map"
provider variable
subtype texture
default "/home/aseiff20/mount/stuhome/maya/projects/default/textures/man.tex"
}
parameter float useMap {
label "Use Texture Map"
description "No description."
subtype switch
range {0.0 1.0}
provider variable
default 1
}
parameter color input {
label "Input Color"
description "No description."
default {.9 .5 .1}
detail varying
}
collection void colorManip {
state open
drawmode all
label {Color Manipulation}
description {Color Manipulation}
parameter float hue {
label "Hue Shift"
description "Hue Shift"
subtype slider
range {0 1}
detail varying
default 0
}
parameter float rShift {
label "Red Shift"
description "Red Shift"
subtype slider
range {0 1}
detail varying
default 0
}
parameter float gShift {
label "Green Shift"
description "Green Shift"
subtype slider
range {0 1}
detail varying
default 0
}
parameter float bShift {
label "Blue Shift"
description "Blue Shift"
subtype slider
range {0 1}
detail varying
default 0
}
parameter float sat {
label "Saturation"
description "Saturation"
subtype slider
range {0 1}
detail varying
default 1
}
parameter float rSat {
label "Red Intensity"
description "Red Intensity"
subtype slider
range {0 1}
detail varying
default 1
}
parameter float gSat {
label "Green Intensity"
description "Green Intensity"
subtype slider
range {0 1}
detail varying
default 1
}
parameter float bSat {
label "Bue Intensity"
description "Blue Intensity"
subtype slider
range {0 1}
detail varying
default 1
}
parameter float gray {
label "Grayscale"
description "Grayscale"
subtype switch
range {0.0 1.0}
provider variable
default 0
}
}
collection void pixelation {
state open
drawmode all
label {Pixalation}
description {Pixalation}
parameter float pixelate {
label "Pixelate"
description "Pixelate"
subtype switch
range {0.0 1.0}
provider variable
default 0
}
parameter float rows {
label "Rows"
description "Rows"
subtype slider
range {2 100 1}
detail varying
default 10
}
parameter float cols {
label "Columns"
description "Columns"
subtype slider
range {2 100 1}
detail varying
default 10
}
parameter float jitter {
label "Jitter"
description "Jitter"
subtype slider
range {0 1}
detail varying
default 0.75
}
parameter float seed {
label "Seed"
description "Seed"
subtype slider
range {0 10000 1}
detail varying
default 1234
}
parameter float gapWidth {
label "Cell Border Width"
description "Cell Border Width"
subtype slider
range {0 1}
detail varying
default 0.05
}
parameter color gapColor {
label "Gap Color"
description "Gap Color"
default {0 0 0}
detail varying
}
parameter float gapOpacity {
label "Gap Opacity"
description "Gap Opacity"
subtype slider
range {0 1}
detail varying
default 1
}
}
collection void quantization {
state open
drawmode all
label {Quantization}
description {Quantization}
parameter float quantize {
label "Quantize"
description "Quantize"
subtype switch
range {0.0 1.0}
provider variable
default 0
}
parameter float steps {
label "Quantization Steps"
description "Quantization Steps"
subtype slider
range {2 20 1}
detail varying
default 5
}
}
}
#__custom parameters End______________
# =======================================================================
parameter color result {
access output
display hidden
}
RSLFunction {
void voronoi(
uniform float numS, numT;
float jitter;
float seed;
output float ss, tt;
output float border;
)
{
float SS = ss * numS;
float TT = tt * numT;
float sthiscell = floor(SS)+0.5;
float tthiscell = floor(TT)+0.5;
float f1 = 10000;
float f2 = 10000;
uniform float i, j;
for (i = -1; i <= 1; i += 1)
{
float stestcell = sthiscell + i;
for (j = -1; j <= 1; j += 1)
{
float ttestcell = tthiscell + j;
float spos = stestcell + jitter * (cellnoise(stestcell + seed, ttestcell + seed) - 0.5);
float tpos = ttestcell + jitter * (cellnoise(stestcell + seed + 23, ttestcell + seed - 87) - 0.5);
float soffset = spos - SS;
float toffset = tpos - TT;
float dist = soffset*soffset + toffset*toffset;
if (dist < f1)
{
f2 = f1;
f1 = dist;
ss = spos/numS;
tt = tpos/numT;
}
else if (dist < f2)
{
f2 = dist;
}
} // j loop
} // i loop
border = sqrt(f2) - sqrt(f1);
}
void aliColorFilter (
string tex_name;
float useMap;
color input;
float hue, rShift, gShift, bShift;
float sat, rSat, gSat, bSat;
float gray;
uniform float pixelate;
uniform float rows;
uniform float cols;
uniform float jitter;
uniform float seed;
uniform float gapWidth;
uniform color gapColor;
uniform float gapOpacity;
float quantize;
float steps;
output color result;
)
{
color vorColor;
float r = input[0];
float g = input[1];
float b = input[2];
if (useMap == 1)
{
if (tex_name != "")
{
r = texture(tex_name[0]);
g = texture(tex_name[1]);
b = texture(tex_name[2]);
}
if (pixelate == 1)
{
float border;
color C1, C2;
float ss = s;
float tt = t;
voronoi (rows, cols, jitter, seed, ss, tt, border);
ss = clamp(abs(ss),0,1);
tt = clamp(abs(tt),0,1);
C1 = texture (tex_name, ss, tt);
float gap = smoothstep (gapWidth-0.01, gapWidth, border);
C2 = C1;
C1 = mix(gapColor, C1, gap);
C1 = mix(C2, C1, gapOpacity);
r = comp(C1, 0);
g = comp(C1, 1);
b = comp(C1, 2);
}
}
color CRGB = color(r,g,b);
color CHSV = ctransform("rgb", "hsv", CRGB);
float hh = comp (CHSV, 0);
float ss = comp (CHSV, 1);
float vv = comp (CHSV, 2);
hh = mod (hh + hue, 1);
ss = ss * sat;
CRGB = color(hh, ss, vv);
CRGB = ctransform("hsv", "rgb", CRGB);
if (r == 1 && rSat == 1) {r = CRGB[0];}
else {r = mod(CRGB[0] + rShift, 1) * rSat;}
if (g == 1 && gSat == 1) {g = CRGB[1];}
else {g = mod(CRGB[1] + gShift, 1) * gSat;}
if (b == 1 && bSat == 1) {b = CRGB[2];}
else {b = mod(CRGB[2] + bShift, 1) * bSat;}
float w = (r + g + b) / 3;
float scale = steps - 1;
if (quantize == 1)
{
r = round(scale * r)/(scale);
g = round(scale * g)/(scale);
b = round(scale * b)/(scale);
w = round(scale * w)/(scale);
}
if (gray == 1)
{
result = color(w,w,w);
}
else
{
result = color(r,g,b);
}
}
}
}}}
|
|
In the Color Manipulation section, I embedded Hue Shift and Saturation for the whole colors and also for each color channel separately. The key is reading the color and converting it from RGB into HSV. For Hue shift I used the mod() function to confine the hue value to 0 to 1. After doing the manipulation I converted the colors back to RGB and output the result.
The Pixelation was the most challenging part for me. I wanted to use Voronoi diagram to give more variety to the filter, because in a special case of Voronoi which the feature points are located on a regular grid, the diagram turns into a usual pixelated image. So how does the filter work?
Basically we need a bunch of points scattered on the surface. When shading a surface, each point is shaded completely isolated from other points; therefore we have no access to other shading points. So we need to find a way to assign one single coordinated to a group of points. noise() function could not be very helpful because it varies from point to point. But cellnoise() offers a great solution: it returns one single random number to any number between two integers, i.e. it quantizes the space. Imaging a space diced into 1x1x1 cubes or in our case a 1x1 square. For all of the points inside that region we need one single random point which would be our feature. Then by multiplying the number of cells, we can have more features.
After dicing the space we need to test which of 9 features around each shaded
point is the nearest. We need two nested loops to go through each surrounding
feature and compare the distance to the previous one. For the first feature we
just need to compare it to a very large initial number. In order to get a better
and more precise result we can test the two nearest features and compare their
distances. The output parameters of our Voronoi function are the coordinates of
the nearest feature points and the borders of the cell. Then later on in the body
of our main function we can use the s and t outputs of Voronoi to choose the
colors of our texture for the whole cell. Easy, yea?
The third part of the filter is Color Quantization. An easy approach that I
took to avoid complicated calculations was multiplying the whole space of each
color component to the number of the steps, rounding up the value and shrinking
down the space back into 0 to 1 value.
