Blenderyard

July 4, 2015

A study of secondary feature lines in Freestyle

Filed under: Blender, Freestyle — blenderyard @ 4:29 PM

Highly detailed 3D scans of live humans and other objects have been increasingly available in the computer graphics market these days, providing an interesting venue for cost-effective generation of non-photorealistic (NPR) illustrations with a variety of artistic styles.  A follow-up of the previous case study using a 3D-scanned model, here is another Freestyle line stylization study using a full body model of a woman (RP Wom Rosy 0095) from Renderpeople.  The input mesh consists of 100K triangular faces.

rosy_sketchy_1_tn rosy_sketchy_2_tn

This time a specific focus was put on silhouettes, suggestive contours and ridges/valleys the aim of capturing subtle surface curvatures and small bumps.  Silhouettes in Freestyle are often clean and easy to capture.  In contrast, suggestive contours and ridges/valleys tend to be jagged and noisy.  However, they are also known to be useful for highlighting minor (secondary) features of smooth surfaces.  Hence how to post-process these types of secondary feature edges is a challenge.

rosy_sketchy_3_tn rosy_sketchy_4_tn

In this study, the secondary feature edges were smoothed by a Bezier Curve geometry modifier, in combination with chain splitting conditioned by a minimum angle threshold to ease the non-linear curve fitting (prone to numerical stability that leads to unexpected results).  The smoothed secondary lines were then grouped into shorter and longer ones by a length threshold.  The shorter lines are here considered less important, so they are drawn with a light gray color.  The longer lines are instead supposed to be of more importance in delivering geometry information to the audience.  Hence they are drawn in a solid black color, with a quick color gradient toward the light gray at both ends to smoothly get connected with the shorter lines.  Silhouettes on the other hand were simply drawn in black with slight thickness variability along stroke.

rosy_sketchy_5_tn rosy_sketchy_6_tn

All the renders in this blog article are results of the same model and the documented line stylization approach, with minor case-by-case tuning of selected parameters depending on viewing geometry (i.e., camera positions).  Specifically, close-up renders often have less line density, so that the length threshold was decreased to get more secondary lines drawn in the solid black color.

rosy_sketchy_7_tn

May 12, 2015

Fixing undesired gaps in Freestyle lines

Filed under: Blender, Freestyle — blenderyard @ 4:08 AM

Freestyle line drawing may contain undesired gaps that show discontinuity in otherwise consistent outlines.  Undesired gaps are often observed in silhouette lines.  The problem is that silhouette lines can be partly occluded by the faces that define the silhouettes.  The first remedy of the issue is to enable the Face Smoothness option found as part of the Freestyle edge detection options.  When this option is disabled, silhouette edges are foreseen to be jaggy (as described in a separate post in the Freestyle Integration into Blender blog) and hence more prone to undesired gaps.  With the Face Smoothness option enabled, resulting silhouette lines will be much smoother and better suited for line visibility tests.

Turning the Face Smoothness option on may still fail to fix some of the line gaps.  This is a technical limitation of the current smooth silhouette line detection algorithm implemented in Freestyle.  Interested readers are addressed to an academic research paper by Bénard et al. (2014), “Computing smooth surface contours with accurate topology”, in which smooth silhouette lines computed by Freestyle are referred to as interpolated contours.  As the word “interpolation” suggests, Freestyle silhouette lines are somehow “guessed” from other existing mesh edges, so that some degree of computational inaccuracy is expected.

Inaccurate silhouette lines mostly come from coarse mesh faces.  Smooth silhouettes are computed from existing mesh edges, so that the coarser the mesh faces are, the less accurate the interpolated silhouette lines are.

Undesired gaps in smooth silhouette lines can be addressed to some extent by manually subdividing mesh faces locally around the places where the gaps are observed.  It is noted that artists usually have their own preferred polygon counts for given mesh models, so subdividing mesh faces of the entire models is unlikely a practical solution.

Below a case study on fixing undesired gaps by manual mesh face subdivision is presented.  In the following test render, a small gap (highlighted in red and enlarged) is observed in a silhouette line of the white shirt.  This is a typical example of line gaps due to inaccurate guesses of smooth silhouettes.

test_scene_tn

As shown below in the screen capture of the mesh data in Blender, the faces around the line gap (smoothed by a level 2 Subdivision Surface modifier) are indeed quite coarse (especially so when compared to a mesh resolution that would be required to represent the quick curve of the outline).  Thick orange lines in the image indicate Freestyle feature edges (so-called view map as a whole) visualized as a curve object by a custom style module (viewmap_to_curve.py, see the post #2894 of the BA Freestyle thread).  This way the gap in the silhouette line can be seen not only from the camera view but also from an arbitrary viewing angle (as in the right and left 3D viewport, respectively), making it straightforward to identify on which mesh face the silhouette line having the gap resides.

screenshot_mesh_tn

Now the quad face causing the gap in the smooth silhouette line is manually divided into two triangles as illustrated below.

screenshot_mesh_fixed_tn

The following rendering result shows that the gap has been successfully fixed by the mesh tweak.

test_scene_fixed_tn

Obviously the applicability of the documented manual solution is limited.  Locally subdividing mesh faces may cause other visual artifacts instead of line gaps.  Model and camera movements might also make it difficult to apply manual mesh tweaks.

The upside is that the manual method gives a guideline for artists to tune input mesh data in a manner favorable to Freestyle.  The present case study illustrates how to locate the spots that need optimizing.  It is always the case that tuning mesh data while keeping Freestyle in mind gives a better line rendering result.

December 28, 2014

DualSPHysics fluid visualization with Blender

Filed under: Blender — blenderyard @ 2:24 PM

CaseMonkey_vimeo

The movie clip above is an experimental product out of a personal pilot project to investigate a CGI workflow that integrates DualySPHysics for GPU-accelerated particle-based fluid simulations and Blender for geometry definition and visualization.

Fluid dynamics simulations are popular Blender applications and visual outcomes are always fascinating.  Blender has a particle-based fluid simulator based on the so-called smoothed particle hydrodynamics (SPH) method.  The SPH simulation code in Blender offers an easy-to-use tool for fluid dynamics visualization.  The main drawback of the SPH implementation in Blender is efficiency, where an external GPU-accelerated SPH code like DualSPHysics comes in handy.

In the present case study, DualSPHysics 3.1 and Blender 2.72 were tested.  Blender was used for defining the simulation domain and the initial geometry of fluid particles.  The prepared mesh data was then exported in STL format and included as part of input parameters to the DualSPHysics program.  Fluid simulations were executed using an NVIDIA Quadro 4000 card on a desktop PC running 64-bit Windows 7.  Finally, polygonized simulation results in VTK format were imported back to Blender for animation rendering and video editing.  The first half of the video shows ray-traced water surface, whereas the second part presents the magnitude of fluid velocity.

Exporting STL files was done using a regular exporting function in Blender.  On the other hand, I was not able to find a working VTK mesh importer for the latest version of Blender, probably because VTK is a general-purpose scientific data format.  I ended up in having to write a VTK impoter myself.  Mesh format conversion was straightforward, while the magnitude of particle velocity was visualized through vertex colors in Blender.  A user-friendly GUI tool called the DualSPHysics Pre-processing Interface (part of the DualSPHysics package) was used for configuring simulation cases.  Before visualizing the SPH simulation results in Blender, ParaView was used for previewing a sequence of generated VTK polygon files.

Performance results of the simulation are summarized below:

  • Particle count: 2 877 560 particles (particle distance set to 0.01 meter).
  • Allocated memory: 1007.76 MB on GPU and 93.37 MB on CPU.
  • Execution time: 14.5 hours.
  • Simulated duration: 2.76 seconds (69 frames at the interval of 0.04 second).

For performance comparisons, a benchmark case (CaseWavemaker in the DualSPHysics package) was executed using the same GPU as well as with a quad-core CPU (Intel Xeon W3656 3.2 GHz).  The execution time for running the same benchmark case (161 809 particles) on GPU and on CPU was 45.68 minutes and 4.03 hours, respectively.  Although the CPU run was multi-threaded, the GPU run was more than 5 times faster.

In conclusion, DualSPHysics offers a reliable and efficient solution for particle-based fluid dynamics simulations in CGI applications for visual art and entertainment.

Related work: GPU-accelerated SPH codes

Besides DualSPHysics there are several GPU-accelerated SPH codes that are freely available, including:

Differences are mainly in features (e.g., support for floating objects) and implementations (obviously CUDA or OpenCL, and support for multiple GPU cards), as well as maturity (code stability, and availability of user friendly tools).

Related work: Particle meshers for Blender

Since SPH simulation results are represented by a number of particles, a particle mesher is necessary to convert the particles to surface meshes for visualization.  There are different approaches proposed to implement a particle mesher in Blender, including Python add-on scripts and built-in mesh modifiers based on metaballs, the OpenVDB library, or the algorithm called marching cubes (MC).

Metaball-based methods are easy to implement but do not scale.  OpenVDB-based methods are likely more efficient but introduce an external library dependency.  MC-based native implementations seem the most promising way to go but also most demanding in terms of coding.  As of this writing none of the options above is applicable to polygonization of millions of particles resulting from realistic SPH simulations.  An upside of DualSPHysics in this regard is that it comes with a fast and efficient MC-based particle mesher.

November 5, 2014

Stereographic projection of line art

Filed under: Blender, Freestyle — blenderyard @ 2:54 PM

I came across an interesting blog post describing how to create lampshades using backward stereographic projection from 2D input images.  Following the detailed instructions given by the blog author, I had a few hours of fun math programming exercise in Matlab to reproduce the same visual result.  As an input 2D image I chose a variation of a Japanese traditional line art pattern called Seigaiha (青海波), here created using Freestyle in Blender.

seigaiha_tn

The input image underwent a series of raster/vector image processing including black/white component labeling, contour simplification, and 2D mesh generation to obtain 3D mesh data of a spherical lampshade as a result of backward stereographic projection described in the reference document.  The 3D geometry data was then imported to Blender and rendered with Cycles.  The screen capture below shows a view port render of the imported mesh data (with the Solidify modifier applied to give physical thickness).

seigaiha_screenshot_tn

A top-view render below with a stronger point lamp shows a clear shadow pattern of the lampshade that nicely matches the input Seigaiha texture image.

seigaiha_output_top_tn

November 2, 2014

Pixelating Freestyle lines

Filed under: Blender, Freestyle — blenderyard @ 3:41 PM

Thin, pixelated lines without anti-aliasing are one of line styles that Blender artists may want to achieve using Freestyle time to time.  This line style is useful for preparing low resolution game assets and imitating old-fashion computer line drawing, just to name a few examples.  Such visual results can be obtained by disabling anti-aliasing and set line width to 1.  However, the resulting quality of pixelated line art may not be satisfactory due to possible thickening of lines here and there depending on input scenes.  Setting line width to a value less than 1 could remove undesirable thick portions of lines, but also may cause broken line in other parts because of sub-pixel line thickness.  Unlike traditional line drawing in old computers, Freestyle renders line art using full-featured 3D rendering engines (namely the Blender Internal and Cycles) that are not specifically designed for producing retro style aliased lines.

An easy way to address this limitation is just to implement a desirable aliased line drawing function within the framework of Freestyle line rendering.  A couple of proof-of-concept renders based on this approach are shown below:

pixelate_suzanne_tn

The left and center panels of the image above show color and line components, while the right panel shows the composite result.  The image has been scaled 400% so as to show the pixelated lines clearly.

pixelate_saturn_tn

This one shows raw render results without scaling.

Now the fun part: these renders were generated using a custom style module written in Python.  In the style module, a traditional aliased line drawing function based on Bresenham’s algorithm has been used to create a set of pixels that comprise pixelated lines.  The computed X and Y coordinates of the pixels are then passed to an external program (the convert command of the ImageMagick graphics package) to rasterize the lines.  This last step can be done in different ways using other 2D graphics libraries (e.g., Cairo), and the use of the convert command is just for testing the idea.  The code is presented below (tested with Blender 2.72b).  This style module can be used in the Python Scripting mode of Freestyle in Blender.  If you want to try it yourself, then you need to modify the full path to ImageMagick’s convert command hard-coded in the program.

# pixelate.py

from freestyle.types import *
from freestyle.chainingiterators import *
from freestyle.predicates import *
from freestyle.shaders import *
from freestyle.utils import *
import bpy
import os

# full path to ImageMagick's "convert" command
CONVERT_COMMAND = r'C:\home\kajiyama\bin\convert.exe'

alpha = 0 # set to 1 for preview render
upred = QuantitativeInvisibilityUP1D(0)
Operators.select(upred)
Operators.bidirectional_chain(ChainSilhouetteIterator(), NotUP1D(upred))
shaders_list = [
    ConstantThicknessShader(1),
    ConstantColorShader(0, 0, 0, alpha),
    ]
Operators.create(TrueUP1D(), shaders_list)

# Based on the implementation of Bresenham's line drawing algorithm
# by Steve Cunningham and Tim Worsham, October 1988, CSU Stanislaus
# http://www.cs.csustan.edu/~rsc/SDSU/Interpolation.pdf
def line(x1, y1, x2, y2):
    pixels = []
    bx = x1
    by = y1
    dx = x2 - x1
    dy = y2 - y1
    if dy == 0: # horizontal line
        pixels.append((bx, by))
        xsign = -1 if dx < 0 else 1
        while bx != x2:
            bx += xsign
            pixels.append((bx, by))
    elif dx == 0: # vertical line
        pixels.append((bx, by))
        ysign = -1 if dy < 0 else 1
        while by != y2:
            by += ysign
            pixels.append((bx, by))
    else: # Bresenham's line drawing algorithm
        pixels.append((bx, by))
        xsign = -1 if dx < 0 else 1
        ysign = -1 if dy < 0 else 1
        dx = abs(dx)
        dy = abs(dy)
        if dx < dy: # the line is more vertical than horizontal
            p = 2 * dx - dy
            const1 = 2 * dx
            const2 = 2 * (dx - dy)
            while by != y2:
                by += ysign
                if p < 0:
                    p += const1
                else:
                    p += const2
                    bx += xsign
                pixels.append((bx, by))
        else: # the line is more horizontal than vertical
            p = 2 * dy - dx
            const1 = 2 * dy
            const2 = 2 * (dy - dx)
            while bx != x2:
                bx += xsign
                if p < 0:
                    p += const1
                else:
                    p += const2
                    by += ysign
                pixels.append((bx, by))
    return pixels

scene = getCurrentScene()
w = scene.render.resolution_x * scene.render.resolution_percentage / 100
h = scene.render.resolution_y * scene.render.resolution_percentage / 100

# write pixels as a set of drawing instructions in the Magick Vector Graphics (MVG) format
# http://www.imagemagick.org/script/magick-vector-graphics.php
pixels = []
nstrokes = Operators.get_strokes_size()
for i in range(nstrokes):
    stroke = Operators.get_stroke_from_index(i)
    points = [(int(svert.point.x), (h-1)-int(svert.point.y)) for svert in stroke]
    prev = points.pop(0)
    for curr in points:
        pixels.extend(line(prev[0], prev[1], curr[0], curr[1]))
        prev = curr
mvg_filename = bpy.path.abspath(scene.render.filepath + "%04d_line.mvg" % scene.frame_current)
with open(mvg_filename, 'wt') as mvg:
    for x, y in pixels:
        mvg.write("point %d,%d\n" % (x, y))
print('Wrote', mvg_filename)

# render the pixels using the ImageMagick convert command
png_filename = bpy.path.abspath(scene.render.filepath + "%04d_line.png" % scene.frame_current)
cmd = '%s -size %dx%d -background none "%s" "%s"' % (CONVERT_COMMAND, w, h, mvg_filename,png_filename)
#print(cmd)
os.system(cmd)
print('Wrote', png_filename)

October 17, 2014

Contour lines as in maps

Filed under: Blender, Freestyle — blenderyard @ 4:40 PM

slices_suzanne_tn

This is a quick proof-of-concept render of contour lines (as in geographical maps) drawn by Freestyle in Blender.  The contour lines are genuine edges in mesh data annotated by Freestyle edge marks.  These edges were auto-generated by a script based on the Intersect mesh-editing command (new in 2.72) to identify contour lines, i.e., cross-section lines with a plane at different heights at a regular interval.

# slices.py

import bpy
import math
from mathutils import Vector

def exec_slices(context, interval):
    print('interval', interval)

    ob = context.active_object
    ob.update_from_editmode()
    print('ob.name', ob.name)

    rad = max(Vector(point).length for point in ob.bound_box)
    print('rad', rad)

    zmin = min(point[2] for point in ob.bound_box)
    zmax = max(point[2] for point in ob.bound_box)
    print('zmin', zmin, 'zmax', zmax)

    imin = int(math.ceil(zmin / interval))
    imax = int(math.floor(zmax / interval))
    zvals = [i * interval for i in range(imin, imax+1)]
    print('zvals', zvals)

    bpy.ops.object.mode_set(mode='EDIT')
    bpy.ops.object.vertex_group_add()

    rot = Vector((0.0, 0.0, 0.0))
    for z in zvals:
        loc = Vector((0.0, 0.0, z)) + ob.location
        do_slice(rad, loc, rot)

    bpy.ops.object.vertex_group_remove(all=False)
    bpy.ops.object.mode_set(mode='OBJECT')

def do_slice(rad, loc, rot):
    bpy.ops.mesh.primitive_plane_add(radius=rad, location=loc, rotation=rot)

    bpy.ops.object.vertex_group_assign()

    bpy.ops.mesh.intersect()
    bpy.ops.mesh.remove_doubles()
    bpy.ops.mesh.mark_freestyle_edge()
    bpy.ops.mesh.select_all(action='DESELECT')

    bpy.ops.object.vertex_group_select()

    bpy.ops.mesh.delete(type='VERT')

class Slices(bpy.types.Operator):
    """Slices"""
    bl_idname = "object.slices"
    bl_label = "Slices"
    bl_options = {'REGISTER', 'UNDO'}

    interval = bpy.props.FloatProperty(name="Slice Interval", default=1.0, min=0.01, max=100)

    @classmethod
    def poll(cls, context):
        ob = context.active_object
        return ob is not None and ob.type == 'MESH'

    def execute(self, context):
        exec_slices(context, self.interval)
        return {'FINISHED'}

def register():
    bpy.utils.register_class(Slices)

def unregister():
    bpy.utils.unregister_class(Slices)

if __name__ == "__main__":
    register()

    # test call
    bpy.ops.object.slices(interval=0.1)

October 9, 2014

Under the hood: Stroke animation with Freestyle for Blender

Filed under: Blender, Freestyle — blenderyard @ 3:40 PM

During the Christmas and new year holidays at the end of 2010, I was experimenting an idea to realize animated strokes using Freestyle for Blender.  The idea was to create an animation movie clip in which a set of stylized lines is gradually drawn as time goes.  After a few attempts of coding in Python using the Freestyle Python API, I got a style module that achieved the desired visual effects.  I made several video clips to demonstrate the new stroke drawing module by the end of new year holidays.  I was quite satisfied with the results.

After a while, I came to know that I had deleted the stroke drawing module file by accident.  Relevant .blend files and output movie clips were salvaged, but the core of the coding work was permanently lost.  Since then it was a long-standing to-do item for me to re-implement the stroke drawing module from scratch.

Recently I got a bit of spare time (for the first time since a long time, after the completion of my intensive freelance work on Freestyle and Blender NPR development supported by the Blender Development Fund).  So I gave another try to stroke animation in Freestyle.  Here is the result (click the image below to jump to the finished product posted on Vimeo):

vimeo107822555

Technically speaking, the stroke animation is implemented as follows.  First a set of stylized strokes is created as usual using Freestyle, namely either through the interactive Parameter Editor mode or in the coder-oriented Python Scripting mode.  The basic strategy for animated strokes then is to make only a fraction of the entire stylized strokes visible, where the fraction is set to the ratio of the number of rendered frames (i.e., the current frame number) to the total number of frames.  To make the visual effects as general as possible, I have implemented the stroke drawing effect as a post-processing procedure as follows:

# stroke_anim.py

from freestyle.types import Operators
from freestyle.utils import getCurrentScene

def postprocess(frame_start=None, frame_end=None, interval=0):
    """
    frame_start: the start frame (default Scene.frame_start)
    frame_end: the end frame (default: Scene.frame_end)
    interval: the number of frames inserted as interval between two strokes
    """

    totlen = 0.0
    nstrokes = Operators.get_strokes_size()
    #print('#strokes', nstrokes)
    for i in range(nstrokes):
        stroke = Operators.get_stroke_from_index(i)
        totlen += stroke.length_2d
    #print('totlen', totlen)

    scene = getCurrentScene()
    sta = scene.frame_start if frame_start is None else frame_start
    end = scene.frame_end if frame_end is None else frame_end
    cur = scene.frame_current
    fac = (cur - sta) / (end - sta)
    #print('fac', fac)

    totDrawingFrames = (end - sta + 1) - interval * (nstrokes - 1)
    if totDrawingFrames < 0:
        raise RuntimeError('The number of frames is too small')

    lengthPerFrame = totlen / totDrawingFrames
    #print('lengthPerFrame', lengthPerFrame)

    thresh = (cur - sta + 1) * lengthPerFrame + 1e-6
    #print('thresh', thresh)

    curlen = 0.0
    for i in range(nstrokes):
        stroke = Operators.get_stroke_from_index(i)
        for svert in stroke:
            svert.attribute.visible = curlen + svert.curvilinear_abscissa < thresh
        curlen += stroke.length_2d + lengthPerFrame * interval
    #print('done')

In the case of Suzanne the monkey drawn by thick brush strokes, the following style module was used (that is, a slightly modified version of japanese_bigbrush.py).

# brush_anim.py

from freestyle.types import *
from freestyle.predicates import *
from freestyle.functions import *
from freestyle.chainingiterators import *
from freestyle.shaders import *

class pyProjectedYBP1D(BinaryPredicate1D):
    def __init__(self, iType=IntegrationType.MEAN):
        BinaryPredicate1D.__init__(self)
        self.func = GetProjectedYF1D(iType)
    def __call__(self, i1, i2):
        return (self.func(i1) > self.func(i2))

Operators.select(QuantitativeInvisibilityUP1D(0))
Operators.bidirectional_chain(ChainSilhouetteIterator(), NotUP1D(QuantitativeInvisibilityUP1D(0)))
## Splits strokes at points of highest 2D curavture
## when there are too many abrupt turns in it
func = pyInverseCurvature2DAngleF0D()
Operators.recursive_split(func, pyParameterUP0D(0.2, 0.8), NotUP1D(pyHigherNumberOfTurnsUP1D(3, 0.5)), 2)
## Keeps only long enough strokes
Operators.select(pyHigherLengthUP1D(100))
## Sorts strokes by the maximum projected Y coordinate value
Operators.sort(pyProjectedYBP1D(IntegrationType.MAX))
shaders_list = [
    pySamplingShader(10),
    BezierCurveShader(30),
    pyNonLinearVaryingThicknessShader(2, 16, 0.6),
    ConstantColorShader(0, 0, 0),
    TipRemoverShader(10),
    ]
Operators.create(TrueUP1D(), shaders_list)

import stroke_anim; stroke_anim.postprocess(interval=10)

Notice the last line in the stylization script where the post-processing procedure is invoked.  By animation rendering from 1 to some frame count (400 in the case of brush-stroked Suzanne), a set of stylized lines created by brush_anim.py will be transformed into a sequence of animation frames where the strokes are “drawn” over time.  The interval parameter specifies a pause between two strokes in units of frames.  Stroke animation can also be done in the Parameter Editor mode of Freestyle by manually modifying script/freestyle/module/parameter_editor.py in your Blender installation as indicated above in the code example.

I believe the idea of having post-processing effects after the creation of Freestyle strokes can be generalized in the future in such a way that artists can specify either user-defined Python procedures or predefined Parameter Editor options to further retouch auto-generated stylized lines.  Some coding effort is required for now, however, to reproduce the documented stroke animation effects.

July 4, 2014

Freestyle renders of a 3D scanned ram skull

Filed under: Blender, Freestyle — blenderyard @ 4:20 PM

The following images are Freestyle renders of a 3D scanned ram skull model (available at the Ten24 3D Scan Store).  The rendering was done with Blender 2.71.  Short hatching-like lines are suggestive contours smoothed by the Bezier Curve geometry modifier and tapered by the Along Stroke thickness modifier.

ram_skull_output1_tn

ram_skull_output2_tn

ram_skull_output3_tn

April 14, 2014

World’s first fanzine on Blender and Freestyle (2013)

Filed under: Blender, Freestyle — blenderyard @ 3:31 PM

suzanneclub_tn

I finally got a copy of the world’s first fanzine on Blender and Freestyle published in February 2013 (note that the publication date was even before the first Blender release that shipped Freestyle).  Many thanks to @blekei, @mato_sus304 and @tksg8086 for the wonderful publication!  This is absolutely rewarding!

April 10, 2014

Fancy line stylization tryouts

Filed under: Blender, Freestyle — blenderyard @ 2:57 PM

fancy_styles_4up_tn

The image above shows the outcome of 4 fancy line styles, all done using Freestyle in the Parameter Editor mode (without Python scripting) in Blender 2.70.

Older Posts »

The WordPress Classic Theme. Blog at WordPress.com.

Follow

Get every new post delivered to your Inbox.