Hair Particle Drawing Project – OpenFrameWorks version
Meret Oppenheim hair particle portrait final
For the new Openframworks version of my old hair particle drawing project I have chosen to switch gears from the politcal satire for now and I have chosen a new subject for this exploration. I decided to do a large portrait of Meret Oppenheim. Oppenheim was the the artist who made the very famous fur lined teacup. The photo this image is sampled from was taken in 1937 by Man Ray.
hair particle sketch of Meret Oppenheim from Don Relyea on Vimeo.
The below sketches and the above video are early tests. I went through several iterations before I got it just right. In addition to the sketches below I posted some others in a set in my flickr account.
Big thanks to Bruce Sterling for mentioning this project in his Wired blog, Beyond the Beyond!
various Sketches of Meret Oppenheim in OF
Click on the above thumbnails to enlarge
Source Code
So you can get an idea of how this works I have included the main source code for the hair particle class with comments. The class is pretty simple and works well. In the update function of testapp I am just poll an array of particles to see which ones are active and which ones are not. The ones that are not active get assigned to draw a new hair particle.
I was really surprised how easy it was to port this project over from Director. I was even more surprised how much faster it runs in OF. The performance boost moving to OF is truly impressive.
1: #include "hair_particle.h" 2: // hair particle drawing class by don relyea 3: // www.donrelyea.com 4: // July 22 2008 5: 6: hair_particle::hair_particle(void){ 7: pActive=false; //init particle not active 8: } 9: 10: hair_particle::~hair_particle(void){ 11: } 12: void hair_particle::update_frame(){ 13: 14: //if im active lets draw 15: if (pActive=true){ 16: domyangle(); //decide which way to grow 17: doforward(); //grow 18: pScale=pScale-0.125; //Decay the scale 19: 20: if (pScale < 1){ //keep it at least 1 pixel size 21: pScale=1; 22: } 23: 24: pBlend=pBlend-pDecay; //Decay the blend 25: 26: //Draw Routine here 27: ofSetColor(pBorW,pBorW,pBorW,pBlend); //set color and alpha blend 28: ofEnableAlphaBlending(); //enable alpha blending 29: ofCircle(myX,myY, pScale); //draw a circle 30: ofDisableAlphaBlending(); //disable alpha blending 31: 32: //reset if particle gets so transparent as to not be visible 33: if (pBlend < 1){ 34: reset_me(); //reset particle 35: } 36: } 37: } 38: void hair_particle::reset_me(){ 39: //reset so we can recyle the particle 40: //why have in sep function? 41: //Bcuz we might want to be able to kill particles by calling this function 42: pActive=false; 43: } 44: bool hair_particle::active(){ 45: // am i busy or ready for a new assignment 46: return pActive; 47: } 48: void hair_particle::doforward(){ 49: //making progress every frame 50: myX = myX +cos(myAngle)*myspeed; 51: myY = myY +sin(myAngle)*myspeed; 52: 53: } 54: void hair_particle::domyangle(){ 55: //which way am I going during my brief existence 56: mydelta=mydelta+ofRandom(-myKink,myKink); 57: 58: float adelta = mydelta/57.2958; 59: 60: myAngle = myAngle +adelta; //increment the angle 61: 62: //keep the angle between 0 and 360 63: if (myAngle > 360){ 64: myAngle= myAngle-360; 65: } 66: if (myAngle < 0){ 67: myAngle= myAngle+360; 68: } 69: 70: } 71: void hair_particle::draw_me( float ablend, float asize, float aloch, float alocv){ 72: //----this code initializes the particle------// 73: pActive=true; //its alive! 74: myKink=ofRandom(0,3); //Kinky is better =) 75: myX = aloch; //give the particle a starting point 76: myY = alocv; //notice veiled reference to old director lingo lol 77: myAngle = ofRandom(0,360); //grow hair in random direction 78: myspeed = 1; //move one pixel a frame 79: 80: if (ablend > 210){ 81: //pBorW=0; //black hair, pBorW=255; for white hair 82: pBorW=255-ablend; //greyscale hair 83: pScale=asize*3.50; //root of hair is thicker, drypoint print effect 84: pDecay=1.95; // how fast the particle decays 85: } else if(ablend > 100 && ablend < 210){ 86: //pBorW=0; 87: pBorW=(255-ablend)/2; //greyscale hair tweaked darker 88: pScale=asize*1.25; 89: pDecay=1.25; 90: } else if(ablend > 65 && ablend < 100){ 91: //pBorW=0; 92: pBorW=(255-ablend)/3; //greyscale hair tweaked darker 93: pScale=asize*0.45; 94: pDecay=0.85; 95: } else if(ablend > 35 && ablend < 65){ 96: //pBorW=255; 97: pBorW=(255-ablend)/4; //greyscale hair tweaked darker 98: pScale=asize*0.35; 99: pDecay=0.85; 100: } else if(ablend > 2 && ablend < 35){ 101: //pBorW=255; 102: pBorW=(255-ablend)/5; //greyscale hair tweaked darker 103: pScale=asize*0.35; 104: pDecay=0.85; 105: } else { 106: pBorW=0; 107: pScale=0.15; 108: pDecay=0.85; 109: } 110: 111: pBlend=ablend; //set the starting the alpha blend this will be reduced by the pDecay 112: mydelta = ofRandom(-10,10); //initial hair kink 113: 114: }
|