Modeling Brush Strokes

The work here is based on Steve Strassman's Hairy Brush model. A brush is modeled as a 1-D array of bristles. To model a stroke, control points are connected linearly with polygons where the width of the polygon is proportional to the "pressure" applied at the control points. The color of a polygon at any point is determined by texture mapping the 1-D array of bristles and determining which bristle passes closest to that point.

Polygonal Model

A stroke is modeled by a set of control points. Each point specifies a location in X-Y space as well as a "pressure" which indicates the width of the stroke at that point. Each stroke has two invisible control points at either end which help for interpolation and polygon edges described below. At each control point, a line is specifed indicating the orientation and width of the brush at that point. To keep the brush perpendicular to the stroke direction at all times, the orientation at a control point is found by determining the bisector of the angle formed by that control point and one control point on either side (hence the need for invisible control points on either end to specify orientation at the endpoints). When the three points are colinear, the brush orientation is defined to be perpendicular to the line. The four points defining the polygon must be sorted into clockwise order (non-trivial!) in order to avoid "bow-tie" strokes and to preserve bristle-mapping (explained below).

Stroke Smoothing

In order to make the strokes appear natural, the stroke is smoothed by an interpolating spline. X, Y, and pressure are parameterized separately and interpolated by blending parabolas (another reason for invisible control points at the ends). The estimated distance along the stroke (time, or t) is used as the parameterization variable. For each segment defined by three control points, coefficients for the equation X = A + Bt + Ct^2 are determined using matrix inversion. Thus, for any segment between two control points, there are two parabolas which could describe the interpolated stroke trajectories. The two parabolas are blended linearly so that each defines the curve completely at its vertex and not at all at the far control point.

Bristle Coloration

The polygons are rendered using a modified scan-line fill. (0, 0, 1, 1) is interpolated across each polygon in the same way depth is interpolated for z-buffer rendering. At each point, this number is multiplied by the number of bristles in the brush to determine the index of the nearest bristle passing by the current pixel in a stroke. The color of this pixels is then plotted on the image. The image at left has a linear color change across the bristles. The image at right has a random coloration of bristles.

End Result

With a little time and effort, strokes can be combined into complex and aesthetically pleasing scenes.

Possible Future Work

Currently, the bristles cannot change coloration with time. This would allow for bristles to dry out as the stroke was painted or for bristles to steal coloration from adjoining bristles. This could be accomplished by interpolating time (distance along the stroke) across each polygon. The scan-line renderer would be modified to place each pixel into a linked list which would be sorted by time. Then, the pixels would be rendered in order, calling an update function for the bristle associated with each pixel. This would be slow, but it would make the paintings more realistic.

Also, the current program is written in C, but the program is conceptually object-oriented. It would be nice to rewrite it in an object-oriented language so that classes could be extended to allow for a wider variation in effects and update rules.