Blenderyard

February 4, 2014

Anisotropic line thickness in Freestyle

Filed under: Blender, Freestyle — blenderyard @ 11:31 AM

anisotropic_thickness_comparison_tn

The image above shows the effects of anisotropic line thickness implemented in Freestyle for Blender in the form of a custom style module written in Python.  The idea is to increase line thickness when the stroke travels in a specific direction (expressed by the angle of the tangent line from the X axis evaluated at individual stroke vertices). Here the mapping from the stroke traveling direction to line thickness was expressed by pairs of angle and thickness values to allow artists to place thickness peaks at arbitrary directions (hard-coded in the script for now), and linear interpolation was used to ensure smooth thickness changes.  In the test renders above, the anisotropic thickness shader was applied only to external contours.  Other lines have a constant thickness.

The full listing of the style module used for the test renders is as follows:

from freestyle import *
from logical_operators import *
from shaders import *
import math

class AnisotropicThicknessShader(StrokeShader):
    def __init__(self):
        StrokeShader.__init__(self)

    def shade(self, stroke):
        if True:
            angles = [-180, -135, -90, -45, 0, 45, 90, 135, 180]
            thickness = [1, 5, 1, 5, 1, 5, 1, 5, 1]
        if True:
            angles = [-180, -120, -60, 0, 60, 120, 180]
            thickness = [1, 5, 1, 5, 1, 5, 1]
        if not True:
            angles = [-180, -90, 0, 90, 180]
            thickness = [1, 5, 1, 5, 1]
        if not True:
            angles = [-180, -45, 45, 135, 180]
            thickness = [1, 1, 5, 1, 1]
        if not True:
            angles = [-180, 0, 180]
            thickness = [1, 5, 1]
        f = Normal2DF0D()
        it = stroke.stroke_vertices_begin()
        while not it.is_end:
            n = -f(Interface0DIterator(it)) # normal
            a = math.atan2(n[1], n[0]) # angle in radians
            a = a / math.pi * 180 # angle in degrees
            # linear interpolation
            for i in range(1, len(angles)):
                if angles[i-1] <= a <= angles[i]:
                    break
            r = (a - angles[i-1]) / (angles[i] - angles[i-1])
            t = thickness[i-1] + r * (thickness[i] - thickness[i-1])
            it.object.attribute.thickness = (t/2, t/2)
            it.increment()

upred = AndUP1D(ExternalContourUP1D(), QuantitativeInvisibilityUP1D(0))
Operators.select(upred)
Operators.bidirectional_chain(ChainSilhouetteIterator(), NotUP1D(upred))
shaders_list = [
    SamplingShader(2),
    AnisotropicThicknessShader(),
    pyMaterialColorShader(0.5),
    ]
Operators.create(TrueUP1D(), shaders_list)

The script was tested with Blender 2.69 (may require code updates for Blender 2.70 and later).

Advertisements

Leave a Comment »

No comments yet.

RSS feed for comments on this post. TrackBack URI

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Blog at WordPress.com.

%d bloggers like this: