scripting freestyle shaders
freestyle has two ways of defining line styles: python scripts and parameter editing mode, with wich you can adjust all sorts of settings yourself. the advantage of scripts is that they can be reused easily freestyle comes with a collection of python files that define line styles, but there’s a good chance the style you want isn’t with them. in this tutorial you’ll learn how to make your own freestyle line style with python.
you don’t need to have any skill in programming, because basically you can copy and paste everything from the line styles that come with freestyle. those files can be found at “path to your blender folder/2.6x/scripts/freestyle/style_modules” (use backslashes on windows).
now let’s open the japanese_bigbrush.py file. it’s advised to use an editor with syntax highlighting (eg . idle,notepad++) or use the blender text editor. also open a second editor window or create a new text block in the blender text editor and save this file in the style_modules folder as your_linestyle_name.py.
In japanese_bigbrush.py you’ll see a big list of imports. Those are the modules freestyle needs. When you make a new line style you’ll have to import those, so copy and paste them into your new file.
from freestyle_init import * from logical_operators import * from PredicatesU1D import * from PredicatesB1D import * from Functions0D import * from shaders import *
The next line uses an operator which selects all of the geometry that is occluded by 0 faces, that means the visible part of the geometry. if you for instance want to select/render all parts that are occluded by one face, you replace the 0 with a 1.
the following line uses another operator. after we have specified which parts of the geometry to render we’ll need to tell freestyle which lines we want to have rendered. in this case the silhouette lines are rendered. NotUP1d() defines for which part freestyle calculates the lines, so use the same number here as the number on the previous line.
some other options to use instead of silhouette are:
pySketchyChainingIterator() ChainPredicateIterator() # look into the other linestyle files for more options
the next two lines define the splitting of the edge. the higher this value the higher the quality and curvature but also render time. sometimes it takes a little experimenting to get this right.
func = pyInverseCurvature2DAngleF0D() Operators.recursiveSplit(func, pyParameterUP0D(0.2,0.8), NotUP1D(pyHigherNumberOfTurnsUP1D(3, 0.5)), 2)
the next line keeps, as documented in the file, only the lines that are longer than 100px. to keep shorter or only longer strokes change this value.
## Keeps only long enough strokes Operators.select(pyHigherLengthUP1D(100))
the next line of code sorts the lines, so the longest lines will be rendered first. most of the time this won’t realy matter, so you could skip this line.
next there is the shaders list. the commands given here corespond with the modifiers you probably know from parameter editing mode. again look in any of the other files for more options for the shaders list.
#a short explanation of what every line does. shaders_list = [ pySamplingShader(10), BezierCurveShader(30), SamplingShader(50), ConstantThicknessShader(10), pyNonLinearVaryingThicknessShader(4,25, 0.6), TextureAssignerShader(6), ConstantColorShader(0.2, 0.2, 0.2,1.0), TipRemoverShader(10) ]
- the pySamplingShader improves the quality of the strokes. the py prefix means this function is defined in python.
- the BezierCurveShader makes the strokes more rounded.
- the SamplingShader resamples the strokes. don’t forget this line, freestyle doesn’t like it when you do.
- the ConstantThicknessShader defines the minimum thickness the line has.
- the pyNonLinearVaryingThicknessShader takes care of the bigbrushy thickness. the values are (start,center,end).
- the TextureAssignerShader gives the stroke some variety in color, is not very recognizable though.
- the ConstantColorShader takes care of the color. the values are (red,green,blue,alpha).
- the TipRemoverShader removes the tips of the lines.
now there remains one thing, the creating of the line style with all options given above. the japanese bigbrush file uses an odd method to do this, because it prevents cluttering and creates the line style on one line. in for example cartoon.py this is done in a more understandable way:
## japanese_bigbrush.py Operators.create(pyDensityUP1D(8,0.4, IntegrationType.MEAN), shaders_list) ## cartoon.py Operators.create(TrueUP1D(), shaders_list)
this final line passes the selected edges and the style of those edges to freestyle.
now you know what everything does you can adjust values yourself. I made a modification to this file because I want the shorter lines to show too, and also made the thickness a little lower. here is my adjusted file:
from freestyle_init import * from logical_operators import * from PredicatesU1D import * from PredicatesB1D import * from Functions0D import * from shaders import * ## edges occluded by one face Operators.select(QuantitativeInvisibilityUP1D(0)) ## select silhouette edges Operators.bidirectionalChain(ChainSilhouetteIterator(),NotUP1D(QuantitativeInvisibilityUP1D(0))) ## Splits strokes at points of highest 2D curavture ## when there are too many abrupt turns in it func = pyInverseCurvature2DAngleF0D() Operators.recursiveSplit(func, pyParameterUP0D(0.2,0.8), NotUP1D(pyHigherNumberOfTurnsUP1D(3, 0.5)), 2) ## keep strokes longer than this value (in pixels) Operators.select(pyHigherLengthUP1D(20)) ## Sorts so as to draw the longest strokes first ## (this will be done using the causal density) ## isn't really necessary Operators.sort(pyLengthBP1D()) # use f to make the line thinner but keep the original ratios # adjust the ConstantThicknessShader or f to make the line thinner/thicker f = 10 shaders_list = [ pySamplingShader(50), BezierCurveShader(100), SamplingShader(110), ConstantThicknessShader(.5), pyNonLinearVaryingThicknessShader(4/f,25/f, 0.6/f), TextureAssignerShader(6), ConstantColorShader(0,0,0,1.0), TipRemoverShader(10) ] ## Use the causal density to avoid cluttering Operators.create(pyDensityUP1D(8,0.4, IntegrationType.MEAN), shaders_list)