4.1 Imppaint Impressionist Paint only effects the user-selected objects, and the Imppaint script acts as a controller for Impressionist Paint ensuring the proper actions are taken for each selected object. First, the script determines if the selected object is valid for painting. Imppaint queries the object to establish whether or not the object has previously been painted; if so the Paint group of scripts is called. If the object has yet to be painted, the Prepaint sections are called. Once Prepaint is complete, Imppaint invokes the procedures necessary to paint the object. If the object is not a NURBS surface, the user is asked to choose another object for painting. The Imppaint Script, then adds and stores, to each object, attributes from the userinterface including: curve length, curve direction, stroke width, perturb direction, perturb amount and perturb position. These values are used to calculate shadows and highlighs which are explained in section 4.5.3. 4.2 Prepaint The preparation modules of code prepare the object and the scene to be painted. Prepaint prepares each object for painting by creating nodes and ensuring that the proper relationships are present for the painting process. Below we describe in detail the purpose of each node created, each attribute set, and the impact of our variables throughout the Prepaint routines of Impressionist Paint. 4.2.1 Creating Nodes The Prepaint script first determines if the object is connected to a closestPointOnSurface node. If one exists the Prepaint portions of the script are skipped, ensuring that Prepaint is executed only one time per object. If one does not exist, a closestPointOnSurface node is created to supply data in a usable form containing the starting position of the curve. The world space attribute of the object Shape Node is connected to the input attribute of the closestPointOnSurface. Connecting the two attributes serves a dual purpose crucial to the final portions of the script. First, the closestPointOnSurface node will provide the point on the surface closest to the point from which a particle is emitted. Throughout the script particles are used to establish the starting point for each curve. To obtain the particle position in UV texture space we must use the value returned from the closestPointOnSurface node. Particles are emitted from an exact position directly on a polygonal surface in world space, yet when particles are emitted from a NURBS object, the particles are near the surface but not always on the surface. Second, the newly created node takes each particle’s emission point and determines the point on the surface that is closest to the emission position. Also, the curveOnSurface command, used to create curves attached to the object, calls for UV coordinates. The closestPointOnSurface node returns the surface point in both world space and UV space so that the particle position may be used to create a brush stroke. 4.2.2 Adding Particle objects to Point Emitters As mentioned earlier, particles are utilized to create semi-random locations for the strokes. In this way, the strokes appear to be painted with a certain degree of spontaneity. Three point emitters are created as surface emitters for each object; in turn and each emitter is connected to a particle group. Figure 4.1 illustrates particles created as a result of the prepaint script as they are emitted from a sphere. Figure 4.1 Particles created by the Prepaint script emit from a sphere 4.2.3 Per Particle and Emitter Attributes Per particle attributes for particle color and opacity are added to each particle group, thus creating new options for the emitters. Attributes are also set for each of the particle groups and each emitter. All three emitters provide both common and unique data for the brush strokes. Each emitter is set to release particles based on the scale of the object. Maya calculates the area of each object containing an emitter and determines a ratio that ensures an even particle emission relative to the size of the object. Scaling the emission rate promotes an even covering of strokes on all of the objects and thus prevents smaller objects from being overly inundated with strokes. Figure 4.2 compares the emission rates by scale and not by scale. The first set of spheres utilized scale by size, and the second set does not. Figure 4.2 emission rates Collectively, the emitters and particles designate the placement of the initial point used to create a curve, as well as color information for the curve, yet each particle group and emitter query different sources to procure the data necessary to instigate the drawing procedure. 4.2.3.1 Emitters The first emitter is seeded to emit particles at unique positions, while the second and third emitters are set to emit particles at coinciding locations. The first emitter, named Mid, holds information used for the strokes that fall within the mid-tones of the object. The second emitter, named Ref, provides a color Reference for strokes which fall in areas of highlight or shadow. The third emitter, Hishad, determines which strokes are drawn to portray the shadowed and highlighted areas of the object. 4.2.3.1.1 Mid Emitter The purpose of the Mid emitter is to control the covering of the object with brushstrokes. The emitter gives off particles evenly over the object’s surface. The intersection point of each particle with the surface marks the starting point of a curve. The goal is to completely cover the object with paint strokes; however, blank spaces will occasionally appear. Alterations to the geoConnector node helped solve this problem. In our case, the geoConnector node provides an interface between the NURBS surface and the dynamics system providing a tessellated polygonal representation of the NURBS object. Increasing the tessellation of the geoConnector causes the particles to be emitted more evenly across the surface, thus allowing the gaps to be filled as showed in Figure 4.3. Figure 4.3 changing geoConnector tessellation Here, the first sphere represents the particle emission pattern with the default geoConnector tessellation, while the second sphere shows a more even emission as a result of increasing the geoConnector tessellation. Since the particles are dispersed in a dense, consistent pattern, the gaps between strokes are decreased and the holes disappear. 4.2.3.1.2 The Ref Emitter Once the object is covered with a base layer of strokes, the Ref emitter acts as a color Reference for the next layer of strokes which are drawn using the Hishad emitter. The Ref and Hishad emitters give off particles at precisely the same positions. In order to avoid creating unnecessary strokes, only the Hishad particles are used to draw strokes, while the Ref emitter supplies the color for each stroke. Figure 3.19 shows how the Ref emitter is used as a Reference to determine the starting position of the shadowed or highlighted strokes. The colores particles are from the Ref emitter, which provides the color information for strokes which fall in highlight or shadow. The grayscale values within the Ref particles are the Highshad particles represent luminosity information for the object. Figure 4.4 purpose of Ref emitter The particle color represents the only difference between the Ref and Hishad particle groups. 4.2.3.1.3 The Hishad Emitter The Hishad emitter determines which strokes will be drawn to illustrate areas of the object that fall in shadow or highlight. Since impressionists avoid using black to illustrate shadows, a modified lighting and shading scheme was developed. Here, the object shadows are based on the color of the object. That is, a mixture of the original object color and the complementary color of the surface area are used along with synched file textures to give the appearance of brush strokes in uniquely colored shadows. The saturation of the color which falls in an area of highlight is decreased inorder to represent the influence of the lights. In order to determine which strokes should be painted using shadow or highlight stokes, the paint portion of Impressionist Paint creates a lambert shader, which is assigned a white color value. The script temporarily applies the shader to the selected object. From this shader, the script creates a file texture storing composed luminosity information for the object and the original shader is reassigned to the object. Figure 4.5 illustrates the file textures which are created through Impressionist Paint. Figure 4.5 file textures Since the file texture provides the luminosity values of the surface, each particle emitted maintains a corresponding grayscale color value. 4.2.4 Prepaint Nodes and Attributes The Prepaint portions of the script result in the creation and connection of the nodes and attributes including the shadow node, closestPointOnsurface node, particle groups, and emitters (see figure 4.6). Figure 4.6 prepaint nodes Once the Prepaint sections of the code are completed, the Paint procedures are called. The paint portion of the code consists of three scripts. Collectively the scripts retrieve variables set by the user to create the prescribed paint strokes. 4.3 Paint Once the Prepaint sections are completed, the three scripts that make up the Paint Procedure are called. Collectively the scripts retrieve variables set by the user to create the desired paint stroke. The paint segment of the code places two layers of strokes on the surface. As noted earlier, the first layer covers the object with mid-tones and the second layer adds highlights and shadows. In order to increase the resemblance between human painting actions and the exactness of a computer’s mathematical precision, the following stroke and curve characteristics are individually emphasized: placement size shape imperfection randomness color texture stroke overlapping 4.3.1 Creating Curves Curves are created according to the particle intersection position upon emission from the object’s surface. The closestPointOnSurface node provides the UV coordinate for the point on the surface which is closest to the emission point. The value returned from the closestPointOnSurface node is used as the starting point of the curve; the U or V value is incremented by adding the user specified distance to the original value along the direction of the curve. Our associated $curve direction variable controls the placement of the UV values to create either a vertical, horizontal, left diagonal, or right diagonal line. The resulting curve length is determined by user input or random curve length. The path of the curve is affected according to the values which the user input inside the GUI. If the perturb value is greater than zero, the perturb amount is added to offset the curve point form the path of the curve. Once the point has been drawn, the perturb amount is subtracted from the new position and the user specified curve distance is then added to the point position. The new value removes the offset and places the next curve point on the correct path. Figure 4.7 illustrates this process in MEL code. switch ($curvedirection){ case 1: { // creates a horizontal curve U position += point distance; V position += perturb value; break; } case 2: { // creates a vertical line V position += point distance; U posiion += perturb value; //Flag true to ensure U position is reset properly $inc=1; break; } case 3: { // creates a diagonal right line U position -= point distance; V position= V position + point distance + perturb value; break; } } case 4: { //creates a diagonal left line U position += point distance; V position = V position + point distance + perturb value; break; } //switch //U or V positions reset so the perturb amount only //affects the specified curve points. if ($inc==1) U position-= perturb value; else V position-= perturb value; Figure 4.7 Method used to determine line direction and curve position The code uses the UV texture values, along with other information to build a string that ultimately results in a Maya command string. This command string can then be executed using the eval command to perform various tasks within our MEL Script. The Maya command, curveOnSurface, creates curves on the exterior of the surface. The properties of the curve created through the Maya command allow the user to alter the curve’s position on the object, and to edit the control vertices, further changing the curve’s shape and placement on the object. The ease in editing curves allows for a greater addition of variance between specified strokes. The definition of each curve is based on the NURBS parametric coordinates. The new curve is selected and the curve is converted to a stroke using the convertCurvesToStrokes command. Next, a stroke is attached to the curve. Then, a unique brush node is attached to the stroke. The brush possesses over forty changeable attributes which are originally set according to the user’s brush choice from the visor. In Figure 4.8, the Hypergraph depicts the relationship of the object and the stroke. Figure 4.8 object/stroke relationship. 4.3.2 Curve Variation and Uniqueness This sequence of creating curves and strokes is repeated until the number of strokes matches the number of emitted particles. As the number of curves increase, the importance of curve variation also rises. The $perturb variable ensures the curve will be drawn resembling the imperfect lines an artist draws. In figure 4.9, the left side illustrates strokes drawn with no perturb value while the curves on the right side use a perturb value to ensure line variation. Figure 4.9 results of perturb values Since the curve length may be random, the position of the curve that is to be perturbed must be recalculated for each stroke. Figure 4.10 demonstrates how the Perturb Position is calculated. int $k=0; //place holder for particlepoint string int $particlepoint[]; //determines the position of the //point or points to be altered switch ($perturbpos){ case 1: //perturbs the position at the //beginning of a stroke { clear($particlepoint); $particlepoint[0]= 1; //list of positions to //be perturbed break; } case 2: //perturbs the U or V position at the //midpoint of a curve { $temppay = $curvelength/2; $pay= `floor $temppay`; clear($particlepoint); $particlepoint[0]= $pay; break; } case 3: //perturbs the end position of a curve { clear($particlepoint); $particlepoint[0]= ($curvelength -1); //list of //positions to be //perturbed break; } case 4: //perturbs the U or V position at the curves //beginning and end { clear($particlepoint); $particlepoint[0]= 1; $particlepoint[1]= ($curvelength -1); break; } } for($m=0; $m<$curvelength; $m++){ if ($particlepoint[$k]= = $m){ //uses user input to determine the curve direction float $direct= $perturbdir < 2 ? 1 : -1; $perturb= $perturbamount * $direct; } else { $perturb=0; } Figure 4.10 MEL code used to form intended inconsistencies in stroke positions During the course of creating brush strokes, the number of strokes and curves increases rapidly; therefore, we implemented a strong naming convention. The script renames the new brush strokes according to the object’s name and the stroke number. These strokes are placed into two groups: one for shadow and highlight strokes and the other for the mid-tone strokes. 4.4 BrushAttr Next, the BrushAttr script is called to set the stroke color. The color of the particle used as the starting point for the curve is also used to determine the line color as shown in figure 4.11. Figure 4.11 The particle color determines the stroke color. The particle color is based on the file texture assigned to the current object. This file texture helps to automate the process of determining the assignment of stroke colors. Figure 4.12 shows how diverse color schemes may be derived from two-dimensional textures. Figure 4.12 Two-dimensional textures allow for diverse color schemes. Additionally, the file texture also increases similarities between colors that an impressionist painter mixes and the computer-generated stroke colors by dictating variations within the stroke color. In the traditional manner, the impressionist painters allowed the viewer’s eye to visually mix the colors, but in computer graphics, additive color mixing creates different color combinations. Figure 4.13 illustrates common techniques for mixing colors using impressionist guidelines as shown in Impressionist Landscape Painting [GRIF94]. Figure 4.13 Impressionist color-mixing techniques The creation of the two dimensional texture is key to the believability of the strokes. The method of sampling the texture creates a variation in the color of the strokes that resembles the color changes found in a painting. Further, the diversity of the stroke attributes assigned within the brush script creates a blending of colors and paint strokes. Figure 4.14 shows that impressionist color mixing techniques can be replicated using file textures. Figure 4.14 file textures for color mixing 4.5 Shadow The user determines how shadows are placed on each object within the scene, by choosing to create the shadows once or on a per-frame basis. The length, style and complexity of the animation play a role in determing the appropriate shading style. Each of the two methods has its strengths and weaknesses. 4.5.1 Beginning Shadow Calculation Option The first method promotes faster render times and is benefical if the character or object does not undergo substantial lighting changes. For example, if an impressionist woman is painted, and during the animation the woman walks under a tree, the shadows are not accurate. For a simple animation, the user may save time by calculating the shadows only once. In this case, the script is run on the first frame of the animation. The file texture which is created, provides a map for three hundred and sixty degrees of lighting, and remains accurate until the object moves drastically from the object’s original position where the shading occurred. This shadow creation technique fails to take into consideration extreme changes in lighting conditions; as mentioned above, the shadows are only calculated one time at the beginning of the animation. 4.5.2 Constant Shadow Calculation Option On the other hand, calculating shadows on a per frame basis helps to ensure accuracy, yet the steps to determine shadow placement and color are time-consuming. When updating shadow and highlight placement throughout the animation, a scriptNode is created which activates the Shadow script before each frame renders. The shadows and highlights remain accurate throughout vast degrees of movement and extreme lighting changes. Choosing to update shadows slightly increases render time. Each time the Shadow script is called any existing shadow strokes are deleted from the scene as are file textures, which provide luminosity information. Then, shadows and highlights are recalculated. New curves, which will be converted into strokes, are placed on the object. Due to the unique properties of each stroke the repainting process creates visible stroke movement on each object which adds to the painterly look of the work. 4.5.3 Calculating Shadow Colors and Placement The derivation of the shadow color and placement is similar for both options. Throughout this work we discuss the importance of creating unique strokes; however, a great difference between the aesthetic qualities and positions of the strokes proves to be distracting. The strokes created via the Shadow and highlight module are based on the length, direction and width of the strokes drawn from the Mid emitter, as well as the pertub amount, position, and direction of the original strokes. As the particles are emitted from the object, each one is produced by the Hishad emitter and tested to determine if the percentage of black in the particle color falls within a user-set range that determines the level of shading. Particles falling outside of the two user-defined ranges are ignored. If a particle falls within the user-defined range, the RGB color value of the corresponding Ref particle determines the shadow color. Figure 4.15 demonstrates how the shadow color is derived. global proc shadow(){ int int int int int $j=1; $testuv=0; $csgfs=1; $numparticles; //maximum number of particles per emitter $imperfectstroke=1; //flag which denotes curve is to be //perturbed int $randomstrokelength=1; //flag which signals random curve lengths int $scontrol=1; //flag determines if created curves are shadow or //mid-tones float $ct=50; string string string string string //current time $ft="ft"; //name of file texture to be created $shadowfiletex=""; $node; //shading node for current object $revn="rev"; $sel= $shadobj; SelectAllNURBSSurfaces; string $shadlist[]=`ls -sl`; //stores list of selected objects string $shadobj= $shadlist[0]; float $rf=`currentTime -q`; //return frame for ($shadobj in $shadlist){ string $bsname="sstr"; string $ssgroup= ($shadobj + "shadgroup"); if (`objExists $ssgroup`){ //deletes shading strokes delete $ssgroup; } $shadowfiletex=$shadobj + $ft; if (`objExists $shadowfiletex`){ //deletes shadow file textures delete $shadowfiletex; } //queries curve attributes to ensure similarities between //mid-tone and shadow curves and strokes float $curvelength= `getAttr ($shadobj + ".maxCurvePoint")`; int $curvedirection= `getAttr ($shadobj + ".curveDirection")`; float $sw= `getAttr ($shadobj + ".strokeWidth")`; int $perturbpos= `getAttr ($shadobj + ".perturbPosition")`; float $tempperturbamount= `getAttr ($shadobj + ".perturbAmount")`; int $perturbdir= `getAttr ($shadobj + ".perturbDirection")`; //returns shapeNode for selected object string $selshape[]= `listRelatives $shadobj`; string $tempselshape= $selshape[0]; $emitcontu=$sel + "|hishad"; //set current emitter to highshad string $allNodes[]= `listConnections -type shadingEngine $tempselshape`; //retuns shading engine of selected shape $node= $allNodes[0]; // procedure call which creates luminosity file texture textpart $sel $tempselshape $emitcontu; //moves currentTime to zero playButtonStart; //forces dynamics to computer up to desired frame runup -mxf $ct -cache; //sets particle group to hishad string $partsel= ($sel + "_hishad"); //gets number of particles being emitted from the current object $numparticles= `getAttr ($partsel + ".count")`; for ($n=0; $n<$numparticles; $n++){ //gets luminosity information from Hishad particle group vector $colors = `partcolor $n $sel $testuv`; float $colorR= ($colors.x); $nvt=$colorR; //calculate shadow color if ($colorR < .20){ $testuv=2; //get particle color from Ref particle group vector $refcolor= `partcolor $n $sel $testuv`; float $rcolorR= ($refcolor.x); float $rcolorG= ($refcolor.y); float $rcolorB= ($refcolor.z); //calculate complemantary color using reverse node setAttr ($revn + ".inputX") $rcolorR; setAttr ($revn + ".inputY") $rcolorG; setAttr ($revn + ".inputZ") $rcolorB; float $tempr=`getAttr ($revn + ".outputX")`; float $tempg=`getAttr ($revn + ".outputY")`; float $tempb=`getAttr ($revn + ".outputZ")`; vector $origcolor= `rgb_to_hsv <<$rcolorR, $rcolorG, $rcolorB>>`; vector $shadcolor= `hsv_to_rgb << $rcolorR,$rcolorG,$nvt>>`; //store new shadow color values $colorR= ($shadcolor.x); $colorG= ($shadcolor.y); $colorB= ($shadcolor.z); Figure 4.15 Explanation of shadow color derivations To calculate shadow strokes, the particle RGB color is first converted to the Hue Saturation Value (HSV) color model which provides a simpler way to manipulate the color properties. In additive color space, using the complementary color of the particle color produces white instead of black for shadows due to additive color mixing principles. To avoid the light color, the shadow color is computed as the average of the original color and its complementary color. The average color falls halfway between the opposing colors, and when mixed with the original color, forms a hue which mimics the look of a the mixture of the complementary color and the original color using subtractive color mixing. The new hue must be darker to create a convincing shadow color; therefore, the value of the Hishad particle is used to derive the new color value since the luminosity provides the correct value. The HSV color is then converted back to RGB color and used as the shadow color. When the shadow stroke blends with the mid-tone stroke, the shadow color resembles the color an impressionist painter may have used to create a shadow. The shadow stroke is created using the Hishad particle position and the color derivation described above to create a colorful shadow. The particles for the starting point of the shadow stroke fall in different locations than the mid-strokes. The strokes overlap, therefore adding to the painterly feel of the rendered image. These two layers of strokes also provide unique color combinations avoiding the harsh precision of computergenerated shadows. 4.5.4 Determining Highlight Color and Placement If the grayscale value of the Hishad particle is less than the default ninety percent (or a user designated value), the particle is used to create a brush stroke representing an area which falls in the lightest portion of the object. The color of the stroke is based on the current particle in the Ref particle array. Figure 4.16 depicts the color changes implemented to create strokes which fall within the highlighted regions of the object. //determines is area falls in highlight and determines color of highlight stroke else if ($colorR > .90){ float $newsat= (1-$colorR); select -r $Refparticleg; partcolor $n; vector $origsat= rgb_to_hsv <<$colorR,$colorG,$colorB>>; vector $newcolor= hsv_to_rgb <<($origsat.x),$newsat,($origsat.z)>>; $colorR= ($newcolor.x); $colorG= ($newcolor.y); $colorB= ($newcolor.z); wherepart $partsel $n; } Figure 4.16 Technique for designating highlight stroke colors Again, the color of the Ref particle is converted from RGB to HSV. The saturation value of the reference color is replaced by the $newsat value, which is determined by subtracting the Red color component value of the Hishad particle from one. Since the RGB components for a particular shade of gray are all equal, we do not need to access the green or blue values. The result is a lighter shade of the Ref particle. Finally the strokes are painted as described with the mid tracker. Once the strokes have been added to the object, the user can choose to add an individual stroke, add another layer of strokes, or to edit the existing strokes. 4.6 Moving On - Animating the scene Once the user is satisfied with the brush stroke coverage, the scene may be animated. To better understand how to animate the scene, let us examine how the object and strokes are organized. Figure 4.17 illustrates the object/stroke relationship created as a result of using Impressionist Paint. Figure 4.17 Object relationships 4.6.1 Understanding Stroke and Object Organization Traditionally, Impressionist Painters avoided harsh outlines. Each brush stroke is assigned a thickness and is placed on the exterior of each shape. As shown in Figure 4.18, the brush strokes alter the shape of the object. The uneven silhouette of the surface disrupts the smooth edges of the object, thus creating an interaction between objects in image space. The edges of objects overlap and provide the illusion of interacting paint strokes. Yet, stroke interpenetration from one object to another may create undesired results. Additionally, the form of the object may be obscured and intended details lost due to the close proximity of the surfaces and the size of the competing strokes. Figure 4.18 Alpha channels of an object before and after stroke application Another issue to consider is that the volume of the paint strokes causes an increase in the overall scale of the object. In order to account for the change in scale, the pre-rendered surface must therefore either be modeled smaller or scaled once the addition of strokes is completed. The object in figure 4.19 changes in proportions as a result of the newly created strokes. Figure 4.19 Proportion changes due to addition of painterly strokes 4.6.2 Scaling Objects During an animation, changing the scale of an object does not alter the scale of the brush strokes. The strokes will maintain the same position on the object, yet they will appear smaller or larger in size depending on the new object size, and will sometimes create empty areas in between the strokes. To avoid the holes in between the strokes, each of the stroke groups that cover the object, along with the object must be selected before scaling. This increases the global scale of the stroke, so the stroke size mirrors the alteration of the object. The global scale of the object need only be altered when extreme changes in scale are necessary. Re-sizing an object to offset the addition of strokes requires no alterations in the global scale of the stroke. Taking advantage of the stroke/object relationship allows the user to rig and animate the object though traditional methods. The curve is parented to the object such that changes to the transformation matrix of the object are inherited by the curve and by the curve child, the stroke; therefore, when the object moves, the strokes follow. Since Impressionist Paint incorporates Maya’s Paint Effects brushes, the strokes may be animated using springs or forces. Alternatively, the curve animation may be scripted. 4.7 Voila, Rendering the Scene Before the scene is rendered, the Impressionist Paint Script causes any previously hidden brushstrokes to be shown. For a short time during the rendering process, the objects appear to be unpainted. During rendering the paint strokes are added during Maya’s post process. Just as with non-stylized animations, the degree of complexity influences render time. The greater the number of strokes and objects present in the scene, the longer the frame takes to render. 4.8 Post-Processing Once the scene is rendered, additional effects may be added to increase the similarities between the computer-generated work and the impressionist style of paintings produced by an artist. The role that the canvas plays in texture and color can be simulated through using the texturizer in Photoshop to add a canvas filter to each frame. Other post processing effects may be explored to further enhance the impressionistic effect.