Processing实现动图的临摹与拓展
- 内容
- 临摹旋转的爱心
- 临摹嵌套的立方体
- 爱心动图的拓展
- 总结
内容
临摹旋转的爱心
原动图如下:
1. 找寻规律
【1】整张图片的构图基础是爱心,而爱心的构图基础是立方体,所以我们需要自己写一个画一个爱心的函数。
【2】爱心在旋转的时候是有规律的,爱心为四行四列。(1,1)和(4,4) 位置的爱心旋转是一致的,(2,1)和(1,2)位置的爱心旋转是一致的, (3,1),(2,2),(1,3)旋转的位置是一致的。不难发现,位于平行于y=x直线的一条直线上的爱心旋转都是一致的。
【3】所有爱心的旋转速度一样,只是初始位置摆放的角度不同,从(1,1)位置的爱心到(4,4)位置的爱心刚好旋转过2π的角度,根据旋转是否一致我们把爱心归为六组,每组比上一组多旋转2π/6的角度。
【4】爱心的颜色有深浅,每隔一条斜线就变化。
【5】该动图属于正交投影
2.代码实现画一个爱心
//画爱心的函数,第一个方块在原点,整个爱心在原点右侧
//所以在对爱心进行旋转的时候需要将爱心的中心先移到原点的位置
void drawlove(int cubesize)
{
for(int i=0;i<7;i++)
{
for(int j=0;j<6;j++)
{
if(j==0)
{
pushMatrix();
translate(cubesize*1,0,0);
box(cubesize);
popMatrix();
pushMatrix();
translate(cubesize*2,0,0);
box(cubesize);
popMatrix();
pushMatrix();
translate(cubesize*4,0,0);
box(cubesize);
popMatrix();
pushMatrix();
translate(cubesize*5,0,0);
box(cubesize);
popMatrix();
}
if(j==1||j==2)
{
pushMatrix();
translate(i*cubesize,j*cubesize,0);
box(cubesize);
popMatrix();
}
if(j==3&&i>0&&i<6)
{
pushMatrix();
//translate(i*cubesize,0,j*cubesize);
translate(i*cubesize,j*cubesize,0);
box(cubesize);
popMatrix();
}
if(j==4&&i>1&&i<5)
{
pushMatrix();
translate(i*cubesize,j*cubesize,0);
box(cubesize);
popMatrix();
}
if(j==5&&i==3)
{
pushMatrix();
translate(i*cubesize,j*cubesize,0);
box(cubesize);
popMatrix();
}
}
}
}
3.完整代码
float angle=0;
int x=0;
int y=0;
void setup() {
size(400,400,P3D);
x=400;
y=400;
}
void draw() {
//正交投影
ortho(-width/2, width/2, -height/2, height/2); // Same as ortho()
//黑色背景
background(0,0,0);
//构成爱心的小方块尺寸
int cubesize=10;
fill(249,37,72);
angle += 0.02;
//这两个变量用来控制爱心在画布上均匀分布
int temp=-7*cubesize/2;
int gap=y/8-cubesize*3;
//分组画爱心
//1,16
pushMatrix();
//控制爱心的最终位置
translate(x/8,gap,0);
rotateY(-angle);
//旋转前把爱心的中心移到原点
translate(temp,0,0);
drawlove(cubesize);
popMatrix();
pushMatrix();
translate(x/8*7,y/4*3+gap,0);
rotateY(-angle);
translate(temp,0,0);
drawlove(cubesize);
popMatrix();
fill(251,68,105);
//2,5
pushMatrix();
translate(x/8*3,gap,0);
rotateY(-angle);
//控制初始角度
rotateY(-PI/6);
translate(temp,0,0);
drawlove(cubesize);
popMatrix();
pushMatrix();
translate(x/8,y/4+gap,0);
rotateY(-angle);
rotateY(-PI/6);
translate(temp,0,0);
drawlove(cubesize);
popMatrix();
fill(249,37,72);
//3,6,9
pushMatrix();
translate(x/8*5,gap,0);
rotateY(-angle);
rotateY(-2*PI/6);
translate(temp,0,0);
drawlove(cubesize);
popMatrix();
pushMatrix();
translate(x/8*3,y/4+gap,0);
rotateY(-angle);
rotateY(-2*PI/6);
translate(temp,0,0);
drawlove(cubesize);
popMatrix();
pushMatrix();
translate(x/8,y/4*2+gap,0);
rotateY(-angle);
rotateY(-2*PI/6);
translate(temp,0,0);
drawlove(cubesize);
popMatrix();
fill(251,68,105);
//4,7,10,13
pushMatrix();
translate(x/8*7,gap,0);
rotateY(-angle);
rotateY(-3*PI/6);
translate(temp,0,0);
drawlove(cubesize);
popMatrix();
pushMatrix();
translate(x/8*5,y/4+gap,0);
rotateY(-angle);
rotateY(-3*PI/6);
translate(temp,0,0);
drawlove(cubesize);
popMatrix();
pushMatrix();
translate(x/8*3,y/4*2+gap,0);
rotateY(-angle);
rotateY(-3*PI/6);
translate(temp,0,0);
drawlove(cubesize);
popMatrix();
pushMatrix();
translate(x/8,y/4*3+gap,0);
rotateY(-angle);
rotateY(-3*PI/6);
translate(temp,0,0);
drawlove(cubesize);
popMatrix();
fill(249,37,72);
//8,11,14
pushMatrix();
translate(x/8*7,y/4+gap,0);
rotateY(-angle);
rotateY(-4*PI/6);
translate(temp,0,0);
drawlove(cubesize);
popMatrix();
pushMatrix();
translate(x/8*5,y/4*2+gap,0);
rotateY(-angle);
rotateY(-4*PI/6);
translate(temp,0,0);
drawlove(cubesize);
popMatrix();
pushMatrix();
translate(x/8*3,y/4*3+gap,0);
rotateY(-angle);
rotateY(-4*PI/6);
translate(temp,0,0);
drawlove(cubesize);
popMatrix();
fill(251,68,105);
//12,15
pushMatrix();
translate(x/8*7,y/4*2+gap,0);
rotateY(-angle);
rotateY(-5*PI/6);
translate(temp,0,0);
drawlove(cubesize);
popMatrix();
pushMatrix();
translate(x/8*5,y/4*3+gap,0);
rotateY(-angle);
rotateY(-4*PI/6);
translate(temp,0,0);
drawlove(cubesize);
popMatrix();
}
void drawlove(int cubesize)
{
for(int i=0;i<7;i++)
{
for(int j=0;j<6;j++)
{
if(j==0)
{
pushMatrix();
translate(cubesize*1,0,0);
box(cubesize);
popMatrix();
pushMatrix();
translate(cubesize*2,0,0);
box(cubesize);
popMatrix();
pushMatrix();
translate(cubesize*4,0,0);
box(cubesize);
popMatrix();
pushMatrix();
translate(cubesize*5,0,0);
box(cubesize);
popMatrix();
}
if(j==1||j==2)
{
pushMatrix();
translate(i*cubesize,j*cubesize,0);
box(cubesize);
popMatrix();
}
if(j==3&&i>0&&i<6)
{
pushMatrix();
//translate(i*cubesize,0,j*cubesize);
translate(i*cubesize,j*cubesize,0);
box(cubesize);
popMatrix();
}
if(j==4&&i>1&&i<5)
{
pushMatrix();
translate(i*cubesize,j*cubesize,0);
box(cubesize);
popMatrix();
}
if(j==5&&i==3)
{
pushMatrix();
translate(i*cubesize,j*cubesize,0);
box(cubesize);
popMatrix();
}
}
}
}
最后成果如下:
临摹嵌套的立方体
首先我们来看原动图:
1.找寻规律
【1】图像的基础是一个线框立方体,并且立方体的顶点是小球。
【2】图像的立方体在朝着不同方向旋转,并且旋转有动态模糊,为了简便,这里不做模糊效果,仅实现它的旋转。
【3】假设最小的立方体边长为50,则中间的立方体边长为100,最大的立方体边长为150
【4】该动图属于正交投影
2.分析
首先画出单个顶点为球的立方体,画好的立方体应该中心在原点,函数如下
void drawCube(int size){
int cubesize=size;
int spheresize=3;
//由于在cube里面画圆遇到了问题,这里的立方体改用线条组成
pushMatrix();
stroke(250,170,20);
line(-cubesize/2,cubesize/2,cubesize/2,cubesize/2,cubesize/2,cubesize/2);
line(cubesize/2,cubesize/2,cubesize/2,cubesize/2,-cubesize/2,cubesize/2);
line(cubesize/2,-cubesize/2,cubesize/2,-cubesize/2,-cubesize/2,cubesize/2);
line(-cubesize/2,-cubesize/2,cubesize/2,-cubesize/2,cubesize/2,cubesize/2);
line(-cubesize/2,cubesize/2,-cubesize/2,cubesize/2,cubesize/2,-cubesize/2);
line(cubesize/2,cubesize/2,-cubesize/2,cubesize/2,-cubesize/2,-cubesize/2);
line(cubesize/2,-cubesize/2,-cubesize/2,-cubesize/2,-cubesize/2,-cubesize/2);
line(-cubesize/2,-cubesize/2,-cubesize/2,-cubesize/2,cubesize/2,-cubesize/2);
line(-cubesize/2,cubesize/2,-cubesize/2,-cubesize/2,cubesize/2,cubesize/2);
line(cubesize/2,cubesize/2,-cubesize/2,cubesize/2,cubesize/2,cubesize/2);
line(cubesize/2,-cubesize/2,-cubesize/2,cubesize/2,-cubesize/2,cubesize/2);
line(-cubesize/2,-cubesize/2,-cubesize/2,-cubesize/2,-cubesize/2,cubesize/2);
popMatrix();
//画顶点球
pushMatrix();
fill(250,170,20);
pushMatrix();
translate(-cubesize/2,cubesize/2,cubesize/2);
sphere(spheresize);
popMatrix();
pushMatrix();
translate(cubesize/2,cubesize/2,cubesize/2);
sphere(spheresize);
popMatrix();
pushMatrix();
translate(-cubesize/2,-cubesize/2,cubesize/2);
sphere(spheresize);
popMatrix();
pushMatrix();
translate(cubesize/2,-cubesize/2,cubesize/2);
sphere(spheresize);
popMatrix();
pushMatrix();
translate(-cubesize/2,cubesize/2,-cubesize/2);
sphere(spheresize);
popMatrix();
pushMatrix();
translate(cubesize/2,cubesize/2,-cubesize/2);
sphere(spheresize);
popMatrix();
pushMatrix();
translate(-cubesize/2,-cubesize/2,-cubesize/2);
sphere(spheresize);
popMatrix();
pushMatrix();
translate(cubesize/2,-cubesize/2,-cubesize/2);
sphere(spheresize);
popMatrix();
popMatrix();
}
接下来我们考虑旋转,在这里值得注意的是立方体的不同方向旋转应该如何实现,这里的旋转和上面爱心的旋转有着很大不同,爱心都是绕着y轴旋转而,而立方体不是。
首先,我们只看最大的立方体,使其绕y轴旋转,大概是这个样子:
这和原动图有很大的区别,想要达到原动图的效果,关键是需要将该立方体的位置发生改变,绕x轴逆时针转动π/4我们便可俯视立方体,绕y轴转动顺时针转动π/4才符合原动图的大立方体位置。经过处理后便可以得到如下效果:
同样的道理,中间的立方体可以先绕z轴旋转,然后移动位置:绕x轴逆时针转π/4,绕y轴逆时针转π/4。最小的立方体先绕z轴旋转,然后移动位置:绕x逆时针旋转π/4,绕y轴顺时针旋转π/4。完成上面的工作,我们得到的结果是这样的,觉有些怪怪的,这是因为我们没有使用动态模糊。
3.完整代码
float angle=0;
void setup() {
size(400, 400,P3D);
}
void draw() {
//正交投影
ortho(-width/2, width/2, -height/2, height/2); // Same as ortho()
background(0,0,0);
angle += 0.02;
pushMatrix();
translate(width/2, height/2, 0);
rotateX(-PI/4);
rotateY(PI/4);
//先旋转
rotateY(2*(angle));
drawCube(150);
popMatrix();
pushMatrix();
translate(width/2, height/2, 0);
rotateX(-PI/4);
rotateY(-PI/4);
rotateZ(1.5*(angle));
drawCube(100);
popMatrix();
pushMatrix();
translate(width/2, height/2, 0);
rotateX(-PI/4);
rotateY(PI/4);
rotateZ(angle);
drawCube(50);
popMatrix();
}
void drawCube(int size){
int cubesize=size;
int spheresize=3;
//画线框
pushMatrix();
stroke(250,170,20);
line(-cubesize/2,cubesize/2,cubesize/2,cubesize/2,cubesize/2,cubesize/2);
line(cubesize/2,cubesize/2,cubesize/2,cubesize/2,-cubesize/2,cubesize/2);
line(cubesize/2,-cubesize/2,cubesize/2,-cubesize/2,-cubesize/2,cubesize/2);
line(-cubesize/2,-cubesize/2,cubesize/2,-cubesize/2,cubesize/2,cubesize/2);
line(-cubesize/2,cubesize/2,-cubesize/2,cubesize/2,cubesize/2,-cubesize/2);
line(cubesize/2,cubesize/2,-cubesize/2,cubesize/2,-cubesize/2,-cubesize/2);
line(cubesize/2,-cubesize/2,-cubesize/2,-cubesize/2,-cubesize/2,-cubesize/2);
line(-cubesize/2,-cubesize/2,-cubesize/2,-cubesize/2,cubesize/2,-cubesize/2);
line(-cubesize/2,cubesize/2,-cubesize/2,-cubesize/2,cubesize/2,cubesize/2);
line(cubesize/2,cubesize/2,-cubesize/2,cubesize/2,cubesize/2,cubesize/2);
line(cubesize/2,-cubesize/2,-cubesize/2,cubesize/2,-cubesize/2,cubesize/2);
line(-cubesize/2,-cubesize/2,-cubesize/2,-cubesize/2,-cubesize/2,cubesize/2);
popMatrix();
//画球
pushMatrix();
fill(250,170,20);
pushMatrix();
translate(-cubesize/2,cubesize/2,cubesize/2);
sphere(spheresize);
popMatrix();
pushMatrix();
translate(cubesize/2,cubesize/2,cubesize/2);
sphere(spheresize);
popMatrix();
pushMatrix();
translate(-cubesize/2,-cubesize/2,cubesize/2);
sphere(spheresize);
popMatrix();
pushMatrix();
translate(cubesize/2,-cubesize/2,cubesize/2);
sphere(spheresize);
popMatrix();
pushMatrix();
translate(-cubesize/2,cubesize/2,-cubesize/2);
sphere(spheresize);
popMatrix();
pushMatrix();
translate(cubesize/2,cubesize/2,-cubesize/2);
sphere(spheresize);
popMatrix();
pushMatrix();
translate(-cubesize/2,-cubesize/2,-cubesize/2);
sphere(spheresize);
popMatrix();
pushMatrix();
translate(cubesize/2,-cubesize/2,-cubesize/2);
sphere(spheresize);
popMatrix();
popMatrix();
}
爱心动图的拓展
1.首先我们来看效果:
2.分析
比起原动图,拓展的动态点击爱心就可以放大,爱心放大后两秒钟后会自动变回原来的样子。并且每次鼠标点击后画爱心的线框颜色会发生随机改变。
为了实现这个效果,我们需要写一个函数来判断鼠标是否点击,以及点击了哪一个区域,每画一个立方体之前需要调用该函数获取爱心的小方块尺寸。
//函数参数用于判断鼠标的点是否在该范围
int enlarge(int minx,int miny,int maxx,int maxy,int num)
{
//返回值就是方块尺寸
int cubesize1=15;
int cubesize2=10;
if(px>minx&&py>miny&&px<maxx&&py<maxy&&flag==1)
{
select=num;
flag=0;
}
newtime=millis();
temptime=newtime-oldtime;
if(temptime>0&&temptime<2000&&select==num)
{
return cubesize1;
}
else if(temptime>0&&temptime>2000&&select==num)
{
select=0;
return cubesize2;
}
else
{
return cubesize2;
}
}
鼠标相应函数
void mousePressed()
{
//随机改变线的颜色
stroke(int(random(0,255)),
int(random(0,255)),
int(random(0,255)));
px=mouseX;
py=mouseY;
flag=1;
oldtime= millis();
}
完整代码
float angle=0;
float px=-1,py=-1;//保存鼠标点击位置
int flag=0;//等于1时鼠标按下
int oldtime=0;//用于计算时间
int newtime=0;
int temptime=0;
int select=0;//标记选择的区域
void mousePressed()
{
stroke(int(random(0,255)),
int(random(0,255)),
int(random(0,255)));
px=mouseX;
py=mouseY;
flag=1;
oldtime= millis();
}
int x=0;
int y=0;
void setup() {
size(400,400,P3D);
x=400;
y=400;
}
int enlarge(int minx,int miny,int maxx,int maxy,int num)
{
int cubesize1=15;
int cubesize2=10;
if(px>minx&&py>miny&&px<maxx&&py<maxy&&flag==1)
{
select=num;
flag=0;
}
newtime=millis();
temptime=newtime-oldtime;
if(temptime>0&&temptime<2000&&select==num)
{
return cubesize1;
}
else if(temptime>0&&temptime>2000&&select==num)
{
select=0;
return cubesize2;
}
else
{
return cubesize2;
}
}
void draw() {
ortho(-width/2, width/2, -height/2, height/2); // Same as ortho()
//black background
background(0,0,0);
//cube is used to make up love
int cubesize=10;
fill(249,37,72);
angle += 0.02;
int temp=-7*cubesize/2;
int gap=y/8-cubesize*3;
//1,16
int cubesize1=enlarge(0,0,x/4,y/4,1);
pushMatrix();
translate(x/8,gap,0);
rotateY(-angle);
translate(temp,0,0);
drawlove(cubesize1);
popMatrix();
int cubesize16=enlarge(x/4*3,y/4*3,x,y,16);
pushMatrix();
translate(x/8*7,y/4*3+gap,0);
rotateY(-angle);
translate(temp,0,0);
drawlove(cubesize16);
popMatrix();
fill(251,68,105);
//2,5
int cubesize2=enlarge(x/4,0,x/4*2,y/4,2);
pushMatrix();
translate(x/8*3,gap,0);
rotateY(-angle);
rotateY(-PI/6);
translate(temp,0,0);
drawlove(cubesize2);
popMatrix();
int cubesize5=enlarge(0,y/4,x/4,y/4*2,5);
pushMatrix();
translate(x/8,y/4+gap,0);
rotateY(-angle);
rotateY(-PI/6);
translate(temp,0,0);
drawlove(cubesize5);
popMatrix();
fill(249,37,72);
//3,6,9
int cubesize3=enlarge(x/4*2,0,x/4*3,y/4,3);
pushMatrix();
translate(x/8*5,gap,0);
rotateY(-angle);
rotateY(-2*PI/6);
translate(temp,0,0);
drawlove(cubesize3);
popMatrix();
int cubesize6=enlarge(x/4,y/4,x/4*2,y/4*2,6);
pushMatrix();
translate(x/8*3,y/4+gap,0);
rotateY(-angle);
rotateY(-2*PI/6);
translate(temp,0,0);
drawlove(cubesize6);
popMatrix();
int cubesize9=enlarge(0,y/4*2,x/4,y/4*3,9);
pushMatrix();
translate(x/8,y/4*2+gap,0);
rotateY(-angle);
rotateY(-2*PI/6);
translate(temp,0,0);
drawlove(cubesize9);
popMatrix();
fill(251,68,105);
//4,7,10,13
int cubesize4=enlarge(x/4*3,0,x,y/4,4);
pushMatrix();
translate(x/8*7,gap,0);
rotateY(-angle);
rotateY(-3*PI/6);
translate(temp,0,0);
drawlove(cubesize4);
popMatrix();
int cubesize7=enlarge(x/4*2,y/4,x/4*3,y/4*2,7);
pushMatrix();
translate(x/8*5,y/4+gap,0);
rotateY(-angle);
rotateY(-3*PI/6);
translate(temp,0,0);
drawlove(cubesize7);
popMatrix();
int cubesize10=enlarge(x/4,y/4*2,x/4*2,y/4*3,10);
pushMatrix();
translate(x/8*3,y/4*2+gap,0);
rotateY(-angle);
rotateY(-3*PI/6);
translate(temp,0,0);
drawlove(cubesize10);
popMatrix();
int cubesize13=enlarge(0,y/4*3,x/4,y,13);
pushMatrix();
translate(x/8,y/4*3+gap,0);
rotateY(-angle);
rotateY(-3*PI/6);
translate(temp,0,0);
drawlove(cubesize13);
popMatrix();
fill(249,37,72);
//8,11,14
int cubesize8=enlarge(x/4*3,y/4,x,y/4*2,8);
pushMatrix();
translate(x/8*7,y/4+gap,0);
rotateY(-angle);
rotateY(-4*PI/6);
translate(temp,0,0);
drawlove(cubesize8);
popMatrix();
int cubesize11=enlarge(x/4*2,y/4*2,x/4*3,y/4*3,11);
pushMatrix();
translate(x/8*5,y/4*2+gap,0);
rotateY(-angle);
rotateY(-4*PI/6);
translate(temp,0,0);
drawlove(cubesize11);
popMatrix();
int cubesize14=enlarge(x/4,y/4*3,x/4*2,y,14);
pushMatrix();
translate(x/8*3,y/4*3+gap,0);
rotateY(-angle);
rotateY(-4*PI/6);
translate(temp,0,0);
drawlove(cubesize14);
popMatrix();
fill(251,68,105);
//12,15
int cubesize12=enlarge(x/4*3,y/4*2,x,y/4*3,12);
pushMatrix();
translate(x/8*7,y/4*2+gap,0);
rotateY(-angle);
rotateY(-5*PI/6);
translate(temp,0,0);
drawlove(cubesize12);
popMatrix();
int cubesize15=enlarge(x/4*2,y/4*3,x/4*3,y,15);
pushMatrix();
translate(x/8*5,y/4*3+gap,0);
rotateY(-angle);
rotateY(-4*PI/6);
translate(temp,0,0);
drawlove(cubesize15);
popMatrix();
}
void drawlove(int cubesize)
{
for(int i=0;i<7;i++)
{
for(int j=0;j<6;j++)
{
if(j==0)
{
pushMatrix();
translate(cubesize*1,0,0);
box(cubesize);
popMatrix();
pushMatrix();
translate(cubesize*2,0,0);
box(cubesize);
popMatrix();
pushMatrix();
translate(cubesize*4,0,0);
box(cubesize);
popMatrix();
pushMatrix();
translate(cubesize*5,0,0);
box(cubesize);
popMatrix();
}
if(j==1||j==2)
{
pushMatrix();
translate(i*cubesize,j*cubesize,0);
box(cubesize);
popMatrix();
}
if(j==3&&i>0&&i<6)
{
pushMatrix();
//translate(i*cubesize,0,j*cubesize);
translate(i*cubesize,j*cubesize,0);
box(cubesize);
popMatrix();
}
if(j==4&&i>1&&i<5)
{
pushMatrix();
translate(i*cubesize,j*cubesize,0);
box(cubesize);
popMatrix();
}
if(j==5&&i==3)
{
pushMatrix();
translate(i*cubesize,j*cubesize,0);
box(cubesize);
popMatrix();
}
}
}
}
总结
1、在使用Proessing临摹的时候需要注意的是processing的坐标系,面对屏幕,processing的默认坐标系是x轴朝屏幕右边,y轴朝屏幕下面,z轴朝屏幕外面。
2.画图最好用pushMatrix()和popMatrix()进行处理,防止当前画图的变换操作影响到下一个图。
3.pushMatrix()和popMatrix()里面的平移旋转等变换是越靠近popMatrix()就越先执行,这是因为pushMatrix()是压栈,popMatrix()是出栈,栈结构后进先出的。
4.画3D图要注意自己想要的效果是正交投影还是透视投影。
5.Processing使用起来还是很方便的,之前使用opengl画过一些图,感觉还是processing比较简单。