初试Processing——我的第一幅“码绘”作品
前言
Processing是一种灵活的软件写生本,是一种学习如何在视觉艺术的环境中编写代码的语言。自2001年以来,Processing促进了视觉艺术中的软件素养和技术中的视觉素养。有成千上万的学生、艺术家、设计师、研究人员和业余爱好者使用加工来学习和制作原型。
恰逢正在上《互动媒体技术》一课,刚好可以用到Processing来进行创意编程,即“码绘”,废话不多说,接下来介绍我的第一幅“码绘”作品。
临摹作品
首先根据老师的要求,我选择了其中一幅作品进行临摹。
在编写代码前,先观察其规律性。可以看出,虽然动图看上去是由不断变化的圆和正方形组成,但是仔细观察可以看出,其实这幅图是由36个圆和36个扇形组成的,变化的仅仅是扇形的位置。
观察完毕后,开始编写代码。
首先,根据面向对象编程的思想,可以将每个圆形和扇形看作一个对象,这样将减少很多的代码量,还可以轻松利用图片的规律来简化编程。
public class Circle
{
Circle(int col,int row)
{
noStroke();
fill(255);
ellipse(row*100+50,col*100+50,90,90);
}
}
这里定义了一个圆的类,用它的构造方法来绘图,比较简单明了,然后在draw函数里面生成对象:
for(int i=0;i<6;i++)
{
for(int j=0;j<6;j++)
{
new Circle(i,j);
}
}
结果如下图所示:
下一步是添加扇形,扇形很明显是四个一组的,2*2的圆形中,每个扇形所在圆的位置都不同,可以用arc函数来进行扇形的绘制。
arc函数的语法如下:
arc(a, b, c, d, start, stop)
根据圆形所处的位置不同,扇形所在的位置也不同,得出如下代码:
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);
}
}
}
输出结果如下:
然后就是想办法让他动起来,也很简单,让arc函数中的最后两个参数,也就是起始点和结束点动态变化就行了,代码如下:
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函数,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.
简单来说,就是获取当前的微秒数,但是因为微妙单位太小了,所以我除了10,在取余360,这样一来time变量的值就会在0到360的范围内变化。
至此,最基本的样子已经有点出来了:
但是仔细观察不难发现,这个图和原图还是有点差别,原图中扇形的旋转并不是匀速的,而是一种类似正弦运动的形式。
不过,直接用整段的正弦函数是不行的,原因是正弦函数的变化是周期性的,而且函数值先增加后减少。而我们只需要[0,π/2]上的这一段就可以了。
由于在四个象限都要进行变化,所以要进行四次[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);
}
}
}
原创作品
在临摹完第一幅作品之后,我开始制作第一幅原创作品。在openprocessing上看了很多其他人的作品后,有了一点思路。
先来看看最后的效果吧:
是会生成一些渐变色的点,跟随鼠标而移动的效果。
那么下面来介绍这个作品的制作过程。
首先,来介绍一下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.
它是一个封装好的向量,可以表示位置,速度和加速度,还有封装的函数,十分方便。于是首先,我先定义了三个PVector变量,分别表示位置,速度和加速度,将它们放在Point类的构造方法中,并初始化位置变量为屏幕内的随机位置:
class Point {
PVector Position, speed, accelerate;
Point(){
Position = new PVector(random(width), random(height));
speed= new PVector();
accelerate= new PVector();
}
}
接着,我在Point类里写了两个函数,一个render()用于绘画点,一个update()用于更新点的位置。
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);
}
但是效果不是很理想,几乎所有的点都会因为离鼠标的距离太远导致加速度过大,都会聚集在一点,就没有环绕跟随的感觉了。我查询了API,得知PVector类里面有一个limit方法,可以限制向量的大小,于是我添加了这两句:
accelerate.limit(1);
speed.limit(20);
如此一来,最基础的效果已经实现了,接下来添加渐变色的效果。
这个也不难,只要将render函数里面的fill(0)语句改动一下。为了实现渐变色的效果,我选择了在将点实例化的过程中改变颜色,将render函数改为R,G,B三个形参的形式。然后再在draw函数里定义abc三个变量存储渐变的数据。代码如下:
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);
}
}
这样一来就大功告成了,最后贴出全部源代码:
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);
}
}
总结
本次对processing的尝试可以说遇到了不少的困难,但是最后完成作品也非常的有成就感,让我深切的感受到了代码也可以和艺术很好的结合,让人产生美的观感。