前言
Processing是一種靈活的軟件寫(xiě)生本,是一種學(xué)習(xí)如何在視覺(jué)藝術(shù)的環(huán)境中編寫(xiě)代碼的語(yǔ)言。自2001年以來(lái),Processing促進(jìn)了視覺(jué)藝術(shù)中的軟件素養(yǎng)和技術(shù)中的視覺(jué)素養(yǎng)。有成千上萬(wàn)的學(xué)生、藝術(shù)家、設(shè)計(jì)師、研究人員和業(yè)余愛(ài)好者使用加工來(lái)學(xué)習(xí)和制作原型。
恰逢正在上《互動(dòng)媒體技術(shù)》一課,剛好可以用到Processing來(lái)進(jìn)行創(chuàng)意編程,即“碼繪”,廢話不多說(shuō),接下來(lái)介紹我的第一幅“碼繪”作品。
臨摹作品
首先根據(jù)老師的要求,我選擇了其中一幅作品進(jìn)行臨摹。

在編寫(xiě)代碼前,先觀察其規(guī)律性??梢钥闯觯m然動(dòng)圖看上去是由不斷變化的圓和正方形組成,但是仔細(xì)觀察可以看出,其實(shí)這幅圖是由36個(gè)圓和36個(gè)扇形組成的,變化的僅僅是扇形的位置。
觀察完畢后,開(kāi)始編寫(xiě)代碼。
首先,根據(jù)面向?qū)ο缶幊痰乃枷耄梢詫⒚總€(gè)圓形和扇形看作一個(gè)對(duì)象,這樣將減少很多的代碼量,還可以輕松利用圖片的規(guī)律來(lái)簡(jiǎn)化編程。
public class Circle
{
Circle(int col,int row)
{
noStroke();
fill(255);
ellipse(row*100+50,col*100+50,90,90);
}
}
這里定義了一個(gè)圓的類(lèi),用它的構(gòu)造方法來(lái)繪圖,比較簡(jiǎn)單明了,然后在draw函數(shù)里面生成對(duì)象:
for(int i=0;i<6;i++)
{
for(int j=0;j<6;j++)
{
new Circle(i,j);
}
}
結(jié)果如下圖所示:
下一步是添加扇形,扇形很明顯是四個(gè)一組的,2*2的圓形中,每個(gè)扇形所在圓的位置都不同,可以用arc函數(shù)來(lái)進(jìn)行扇形的繪制。
arc函數(shù)的語(yǔ)法如下:
arc(a, b, c, d, start, stop)
根據(jù)圓形所處的位置不同,扇形所在的位置也不同,得出如下代碼:
public class Circle
{
Circle(int col,int row)
{
noStroke();
fill(255);
ellipse(row*100+50,col*100+50,90,90);
if(col%2==0 && row%2==0)
{
noStroke();
fill(0);
arc(row*100+50,col*100+50,90,90,0,0+HALF_PI);
}
if(col%2==0 && row%2==1)
{
noStroke();
fill(0);
arc(row*100+50,col*100+50,90,90,0+HALF_PI,0+PI);
}
if(col%2==1 && row%2==1)
{
noStroke();
fill(0);
arc(row*100+50,col*100+50,90,90,0+PI,0+PI+HALF_PI);
}
if(col%2==1 && row%2==0)
{
noStroke();
fill(0);
arc(row*100+50,col*100+50,90,90,0+PI+HALF_PI,0+2*PI);
}
}
}
輸出結(jié)果如下:
然后就是想辦法讓他動(dòng)起來(lái),也很簡(jiǎn)單,讓arc函數(shù)中的最后兩個(gè)參數(shù),也就是起始點(diǎn)和結(jié)束點(diǎn)動(dòng)態(tài)變化就行了,代碼如下:
float time;
public class Circle
{
Circle(int col,int row,float curAngle)
{
noStroke();
fill(255);
ellipse(row*100+50,col*100+50,90,90);
if(col%2==0 && row%2==0)
{
noStroke();
fill(0);
curAngle=2*PI-curAngle;
arc(row*100+50,col*100+50,90,90,curAngle,curAngle+HALF_PI);
}
if(col%2==0 && row%2==1)
{
noStroke();
fill(0);
arc(row*100+50,col*100+50,90,90,curAngle+HALF_PI,curAngle+PI);
}
if(col%2==1 && row%2==1)
{
noStroke();
fill(0);
curAngle=2*PI-curAngle;
arc(row*100+50,col*100+50,90,90,curAngle+PI,curAngle+PI+HALF_PI);
}
if(col%2==1 && row%2==0)
{
noStroke();
fill(0);
arc(row*100+50,col*100+50,90,90,curAngle+PI+HALF_PI,curAngle+2*PI);
}
}
}
void setup()
{
size(600,600);
}
void draw()
{
background(600,600);
background(0);
time=millis()/10%360;
for(int i=0;i<6;i++)
{
for(int j=0;j<6;j++)
{
new Circle(i,j,time*3.1415926/180);
}
}
}
這里,使用了millis函數(shù),millis的官方文檔如下:
millis() :Returns the number of milliseconds (thousandths of a second) since starting the program. This information is often used for timing events and animation sequences.
簡(jiǎn)單來(lái)說(shuō),就是獲取當(dāng)前的微秒數(shù),但是因?yàn)槲⒚顔挝惶×耍晕页?0,在取余360,這樣一來(lái)time變量的值就會(huì)在0到360的范圍內(nèi)變化。
至此,最基本的樣子已經(jīng)有點(diǎn)出來(lái)了:

但是仔細(xì)觀察不難發(fā)現(xiàn),這個(gè)圖和原圖還是有點(diǎn)差別,原圖中扇形的旋轉(zhuǎn)并不是勻速的,而是一種類(lèi)似正弦運(yùn)動(dòng)的形式。
不過(guò),直接用整段的正弦函數(shù)是不行的,原因是正弦函數(shù)的變化是周期性的,而且函數(shù)值先增加后減少。而我們只需要[0,π/2]上的這一段就可以了。
由于在四個(gè)象限都要進(jìn)行變化,所以要進(jìn)行四次[0,π/2]上的正弦變換。代碼如下:
if(time>0&&time<=90)
{
angle=90*sin(time*PI/180);
}
if(time>90&&time<=180)
{
angle=90+90*sin((time-90)*PI/180);
}
if(time>180&&time<=270)
{
angle=180+90*sin((time-180)*PI/180);
}
if(time>270&&time<=360)
{
angle=270+90*sin((time-270)*PI/180);
}
最后效果如下:

最后附上所有源代碼:
float time;
float angle;
public class Circle
{
Circle(int col,int row,float curAngle)
{
noStroke();
fill(255);
ellipse(row*100+50,col*100+50,90,90);
if(col%2==0 && row%2==0)
{
noStroke();
fill(0);
curAngle=2*PI-curAngle;
arc(row*100+50,col*100+50,90,90,curAngle,curAngle+HALF_PI);
}
if(col%2==0 && row%2==1)
{
noStroke();
fill(0);
arc(row*100+50,col*100+50,90,90,curAngle+HALF_PI,curAngle+PI);
}
if(col%2==1 && row%2==1)
{
noStroke();
fill(0);
curAngle=2*PI-curAngle;
arc(row*100+50,col*100+50,90,90,curAngle+PI,curAngle+PI+HALF_PI);
}
if(col%2==1 && row%2==0)
{
noStroke();
fill(0);
arc(row*100+50,col*100+50,90,90,curAngle+PI+HALF_PI,curAngle+2*PI);
}
}
}
void setup()
{
size(600,600);
//frameRate(60);
}
void draw()
{
background(600,600);
background(0);
time=millis()/10%360;
if(time>0&&time<=90)
{
angle=90*sin(time*PI/180);
}
if(time>90&&time<=180)
{
angle=90+90*sin((time-90)*PI/180);
}
if(time>180&&time<=270)
{
angle=180+90*sin((time-180)*PI/180);
}
if(time>270&&time<=360)
{
angle=270+90*sin((time-270)*PI/180);
}
for(int i=0;i<6;i++)
{
for(int j=0;j<6;j++)
{
new Circle(i,j,angle*3.1415926/180);
}
}
}
原創(chuàng)作品
在臨摹完第一幅作品之后,我開(kāi)始制作第一幅原創(chuàng)作品。在openprocessing上看了很多其他人的作品后,有了一點(diǎn)思路。
先來(lái)看看最后的效果吧:

是會(huì)生成一些漸變色的點(diǎn),跟隨鼠標(biāo)而移動(dòng)的效果。
那么下面來(lái)介紹這個(gè)作品的制作過(guò)程。
首先,來(lái)介紹一下PVector,描述文檔如下:
A class to describe a two or three dimensional vector, specifically a Euclidean (also known as geometric) vector. A vector is an entity that has both magnitude and direction. The datatype, however, stores the components of the vector (x,y for 2D, and x,y,z for 3D). The magnitude and direction can be accessed via the methods mag() and heading().
In many of the Processing examples, you will see PVector used to describe a position, velocity, or acceleration. For example, if you consider a rectangle moving across the screen, at any given instant it has a position (a vector that points from the origin to its location), a velocity (the rate at which the object's position changes per time unit, expressed as a vector), and acceleration (the rate at which the object's velocity changes per time unit, expressed as a vector). Since vectors represent groupings of values, we cannot simply use traditional addition/multiplication/etc. Instead, we'll need to do some "vector" math, which is made easy by the methods inside the PVector class.
它是一個(gè)封裝好的向量,可以表示位置,速度和加速度,還有封裝的函數(shù),十分方便。于是首先,我先定義了三個(gè)PVector變量,分別表示位置,速度和加速度,將它們放在Point類(lèi)的構(gòu)造方法中,并初始化位置變量為屏幕內(nèi)的隨機(jī)位置:
class Point {
PVector Position, speed, accelerate;
Point(){
Position = new PVector(random(width), random(height));
speed= new PVector();
accelerate= new PVector();
}
}
接著,我在Point類(lèi)里寫(xiě)了兩個(gè)函數(shù),一個(gè)render()用于繪畫(huà)點(diǎn),一個(gè)update()用于更新點(diǎn)的位置。
void render(){
fill(0);
noStroke();
ellipse(Position.x,Position.y,10,10);
}
void update(){
accelerate = new PVector(mouseX-Position.x, mouseY -Position.y);
speed.add(accelerate);
Position.add(speed);
}
但是效果不是很理想,幾乎所有的點(diǎn)都會(huì)因?yàn)殡x鼠標(biāo)的距離太遠(yuǎn)導(dǎo)致加速度過(guò)大,都會(huì)聚集在一點(diǎn),就沒(méi)有環(huán)繞跟隨的感覺(jué)了。我查詢了API,得知PVector類(lèi)里面有一個(gè)limit方法,可以限制向量的大小,于是我添加了這兩句:
accelerate.limit(1);
speed.limit(20);
如此一來(lái),最基礎(chǔ)的效果已經(jīng)實(shí)現(xiàn)了,接下來(lái)添加漸變色的效果。
這個(gè)也不難,只要將render函數(shù)里面的fill(0)語(yǔ)句改動(dòng)一下。為了實(shí)現(xiàn)漸變色的效果,我選擇了在將點(diǎn)實(shí)例化的過(guò)程中改變顏色,將render函數(shù)改為R,G,B三個(gè)形參的形式。然后再在draw函數(shù)里定義abc三個(gè)變量存儲(chǔ)漸變的數(shù)據(jù)。代碼如下:
void render(int R,int G,int B){
fill(R,G,B);
noStroke();
ellipse(Position.x,Position.y,10,10);
}
void draw(){
int a=millis()/10;
int b=millis()/50;
int c=millis()/100;
fill(255,255,255,50);
rect(0,0,800,800);
for( int i =0; i < points.length; i++){
points[i].update();
points[i].render(c,b,a);
}
}
這樣一來(lái)就大功告成了,最后貼出全部源代碼:
class Point {
PVector Position, speed, accelerate;
Point(){
Position = new PVector(random(width), random(height));
speed= new PVector();
accelerate= new PVector();
}
void render(int R,int G,int B){
fill(R,G,B);
noStroke();
ellipse(Position.x,Position.y,10,10);
}
void update(){
accelerate = new PVector(mouseX-Position.x, mouseY -Position.y);
accelerate.limit(1);
speed.add(accelerate);
speed.limit(20);
Position.add(speed);
}
}
Point [] points = new Point[50];
void setup(){
size(800,800);
background(255);
for( int i =0; i < points.length; i++){
points[i] = new Point();
}
}
void draw(){
int a=millis()/10;
int b=millis()/50;
int c=millis()/100;
fill(255,255,255,50);
rect(0,0,800,800);
for( int i =0; i < points.length; i++){
points[i].update();
points[i].render(c,b,a);
}
}
總結(jié)
本次對(duì)processing的嘗試可以說(shuō)遇到了不少的困難,但是最后完成作品也非常的有成就感,讓我深切的感受到了代碼也可以和藝術(shù)很好的結(jié)合,讓人產(chǎn)生美的觀感。