Blender 2.8 Grease Pencil Scripting and Generative Art | by 5agado | Towards Data Science

What: learning the basics of scripting for Blender Grease-Pencil tool, with focus on generative art as a concrete playground. Less talking, more code (commented) and many examples.
Why: mostly because we can. Also because Blender is a very rich ecosystem, and Grease-Pencil in version 2.8 is a powerful and versatile tool. Generative art is a captivating way to showcase the tool potential: if you love Python and don’t feel like learning Processing, or are still unsure about venturing with p5.js or Three.js, here you will find the perfect playground.
Who: Python ≥ 3.6 and Blender 2.8. I took inspiration from other resources on generative art, in particular the book Generative Art by Matt Pearson and the Kadenze online course Generative Art and Computational Creativity.
Where/When: right here, right now. Can also find the complete code here (and Blender utils here).
In Blender, you can work with code from the Python Console Editor (be sure to make good use of the Autocomplete function)
Or you can more easily run .py scripts from the text editor. Always toggle the system console so you can check the Python output. Useful shortcuts are alt+P for running the script and alt+R for reloading the file content. Unless you are just playing around or experimenting, I suggest to code in a dedicated IDE and get back to Blender just to verify results.
That’s all needed to get started. So here the code for getting our Grease-Pencil (GP) instance ready.
The hierarchy works like this: each GP object can have multiple layers, which is an abstraction to organize your work. In a layer you can then create strokes, which are a series of points associated with a material. Different strokes in the same layer can have different materials. Strokes live in frames, which is the temporal dimension of a GP object. We’ll see more about frames later in the animation section, with static content we just need a single target frame where to draw our strokes.
gp_layer = init_grease_pencil()gp_frame = gp_layer.frames.new(0)
With this single reusable frame we can start drawing.
A GP stroke is just a collection of points with properties. For example here the method to draw a line.
For a given GP frame, create a new stroke, define the length (number of points) and set each point 3D coordinate. You can then have your stroke on screen (in 3D space and editable, thanks to the display_mode flag) and at frame 0 via
gp_layer = init_grease_pencil()gp_frame = gp_layer.frames.new(0)draw_line(gp_frame, (0, 0, 0), (1, 1, 0))
We can draw a square by reusing the previous draw_line method or we can more nicely compose our square from scratch by computing coordinates and adding more points. Similar, moving to 3D shapes, to build a cube.
The following code is instead to draw a circle. Notice the draw_cyclic flag to close the loop (connect first with last stroke point).
A GP is now a first-class citizen, and as such can rely on any higher level utility/method of a common Blender object. For strokes is not the same, but that gives us the chance to learn/understand a bit more about basic mechanisms of Computer Graphics and, consequentially, Mathematics. We can for example rotate our circle (which is just a bunch of points) using a rotation matrix.
With these two methods one can obtain a sphere via
def draw_sphere(gp_frame, radius: int, circles: int):    angle = math.pi / circles    for i in range(circles):        circle = draw_circle(gp_frame, (0, 0, 0), radius, 32)        rotate_stroke(circle, angle*i, 'x')
From these basic components and steps, you can assemble a practically infinite variety of results. Here follow some basic examples I obtained using this code with some mathematical models to generate the data itself.
Materials are a way to explore new dimensions in an attempt to escape the dullness of a monochromatic reality.
Each stroke can be associated with a specific material by setting its material_index attribute to the corresponding material index. Stroke thickness can instead be set at creation time via the line_width attribute.
gp_stroke = gp_frame.strokes.new()gp_stroke.display_mode = '3DSPACE'  # allows for editing# The new lines needed to customize stroke thickness and materialgp_stroke.line_width = 100gp_stroke.material_index = 1
Notice that some properties are at layer level, while others are at material level. You can then manually tweak materials and have real-time feedback from the UI, but for the thickness you can play around just at layer level, unless you start using Effect or Modifiers.
Materials can also be created programmatically, but that’s content for a dedicated post. For now enjoy some of the results I got fiddling around with basic materials options.
Frames are your best friends for animation. They are the medium along which you narrate your story; canvases across the timeline that you can animate in Blender. From a code point of view is just about creating new frames and accessing them to manage strokes.
In this script, we set our target number of frames and relative distance, update scene variables accordingly and then run through the frames, creating one at each of our target destinations in the timeline. The gp_layer.frames.new method requires as argument the index of the new frame on the timeline, it will throw an error if this already exists. You can also access frames via gp_layer.frames[index], but notice that the index in the frames list does not necessarily match the timeline frame number (as frames can be spaced differently). For that you can check instead the frame_number attribute of a frame object.
In the previous example we used gp_layer.frames.new , which returns exactly a new blank frame, with no memory of the past. This can be used if you are going to draw something entirely new. If you are instead interested in reusing what drawn in a previous frame, you should use gp_layer.frames.copy(gp_frame), where gp_frame is an actual frame instance. This will copy the content of the given frame to the next available timeline slot.
The following animation uses exactly this method, and draws just a new segment each frame, copying all that has been already drawn before, plus a “cube-expand effect” between each segment drawing.
This is really all there is to know to script basic animation. Here some more examples I created based on what covered in this post.
As a bonus, here I hooked-up a Sketch-RNN model and related Quick, Draw! Dataset, for a nice grid of drawing agents.
In this entry we covered the basics of Grease-Pencil scripting, with focus on generative art for examples and directions.
Regarding Grease-Pencil there is not much to say, if not to get creative and come up with your own useful/creative use-case, leveraging of course Blender great potential. Aim at hybrids, for example mixing 3D and 2D, or “artistic data visualization”.
I wanted instead to finish with suggestions around Generative Art, starting with some fundamental recipes and effective creative routines:
explore and combine multiple mathematical functions and models
take inspiration from other domains (e.g. biology, architecture, psychology, physics)
decompose reality and others’ works and try to reproduce
design and experiment with composable hierarchies of reusable atomic methods and objects
reinterpret: visualize properties, objects and actions in alternative ways
“Don’t do what a camera can do, do what it can’t do”
Obligatory starting topics to explore (some highly correlated):
Grammars, L-System and Fractals
Cellular Automata, Flocks and Ants
Software/Digital Agents and Emergent Behavior
Artificial Life
Procedural Content Generation
Chaos Theory
Beyond the 3D dimension
Finally, I highly suggest diving into machine learning and related generative models. The results of these techniques speak for themselves.
“I hear and I forget, I see and I remember, I do and I understand” — Confucius
Can find more on my Instagram (artsy stuff) and Twitter (CS stuff).