目录
最终作品效果可以看上传在B站的视频:https://www.bilibili.com/video/av36523799
第一次码绘VS手绘指路:https://blog.csdn.net/qq_37787956/article/details/84200103
一、代码逻辑
在第一次尝试中,绘制了一个十分简单的小人,它不会动,也不能和用户进行交互。
对于艺术创意编程来说,实在是一个无趣的作品,如何让静止的作品动起来,是这次尝试主要要实现的目标。
首先,准备一幅静态的作品:
这只小鸟激发了我创作的热情[○・`Д´・ ○],贴出我的手绘作品。
然后 ,你需要把你的创作对象放在坐标轴里肢解,规划好各个部分的排列顺序,因为上一次实践中并没有尝试任意曲线的绘制,这次尝试一下。
设计好了之后,就可以创建一张画布开始绘制了。
真正绘制起来就很随心随性了,你会发现既不像你找的图,也不像你画的图,这就是创作的乐趣所在,永远有你想不到的惊喜emmm
接下来,我们要怎么让它动起来,应该实际来说有两种可行的解决方法,一是让小鸟自己动,二是让背景动起来。
在这里我尝试的是第二种方法,如果我想让这只小鸟飞翔在群山之上,只需要让后面的群山动就好了。
这时候就要用到一个非常实用的函数了!
noise(x):返回指定坐标处的Perlin噪声值,结果值始终在0.0到1.0之间。
Perlin噪声是随机序列发生器,产生比标准random()函数更自然,谐波序列的数字。它是由Ken Perlin在20世纪80年代开发的,并已用于图形应用程序,以生成程序纹理,形状,地形和其他看似有机的形式。(引用)
总之生成的噪声图案十分的层次分明,就很美。
我们要做的就是不断的按照一定的速度向前更新x的坐标值,每次刷新画布的时候就会产生不断运动的感觉。
类似于:
但是现在是不是发现鸟飞的很尴尬,像吊在半空中一样,我们怎么来做的更像呢?
现实中鸟飞的时候翅膀都会动,显然,我们这只并不,好,我们来解决一下翅膀这个问题。绕一个点上下摆动,啊,我们有一个现成的模型可以套用,单摆运动,
我们确定了上下摆动的范围,具体的x值我们带到sin函数中算就好了。
一个有趣的交互,当鼠标靠近小鸟的时候,小鸟就躲开,狡猾的小别致。很简单,检测到鼠标位置时,移动坐标轴就好了,当然不要移动太大,小鸟就飞没了。
看是不是十分的和谐。
进入完善阶段,为了丰富一下我的画面,加了一个模拟雪花的“假粒子系统”。
很通俗的来讲,我准备了200半径随机个小白球,一开始在随机位置生成,不断改变y值,落到最下面的时候,回到固定位置重新开始新一轮的降落循环。
为了更鬼畜一点,加了一个跟着音乐跳动的太阳。
只是进行了一下简单的尝试,最最简单的音乐可视化,加载了minim库,加载进音乐来,用player.left.get(i)获取它左声道的值,这个值是介于(-1,1)之间的一个值‘’只需要乘一个系数放大这种变化,传递给画圆函数,就收获到一个闪烁着的太阳效果。这里友情提示一下,一开始我选择的是sound这个库,怎么弄也不出声,后来我一百度,原来这个库不支持processing 3这个版本,果断换了minim。
最终效果,可以点击下面链接看录屏效果。
https://www.bilibili.com/video/av36523799
附上代码:
import ddf.minim.* ;
Minim minim;
AudioPlayer player;
float theta=0.0;
float angle=PI/2;
int count=200;
float[] x=new float[count];
float[] y=new float[count];
float[] r=new float[count];
float[] s=new float[count];
float step,theta1;
int num=5, frames = 1200;
Layer[] layers = new Layer[num];
float value=0;
void setup() {
size(800, 600);
surface.setResizable(true);
minim =new Minim(this);
player=minim.loadFile("1.mp3");
smooth();
step = (height-120)/num;
for (int i=0; i<num; i++) {
layers[i] = new Layer(-20+i*step, random(1000), i+1);
}
for (int i=0; i<count; i++)
{
x[i]=random(0, width);
y[i]=random(0, height);
r[i]=random(3, 7);
s[i]=random(1, 2);
}
}
void draw() {
background(255);
for(int i=0;i<player.bufferSize()-1;i++)
{
value=abs(player.left.get(i));
}
sunoutside(value);
suninside(value);
for (int i=0; i<layers.length; i++) {
color col = lerpColor(#00C322,0, 0.8-0.8/num*i);
fill(col);
layers[i].display();
}
theta1 += TWO_PI/frames;
//snow
fill(255);
for (int i=0; i<count; i++) {
if (y[i]>height) {
y[i]=-5;
}
if (dist(mouseX, mouseY, x[i], y[i])<20) {
y[i]=-5;
x[i]=random(0, width);
}
y[i]+=s[i];
ellipse(x[i], y[i], r[i], r[i]);
}
translate(width/2, height/2);
float x= -(mouseX-150)/7;
float y= -(mouseY-150)/7;
translate(x,y);
bird();
translate(-40, 40);//arm
rotate(theta);
beginShape();
vertex(0, 0);
bezierVertex(0, 0, -50, 0, -50, -30);
bezierVertex(-50, -30, -90, 0, 0, 0);
endShape();
theta+=sin(angle)*0.05;
angle+=0.05;
}
void bird()
{
fill(126);//tail
noStroke();
ellipse(-80, 0, 20, 10);
rotate(PI/40);
ellipse(-80, 0, 20, 10);
rotate(-PI/20);
ellipse(-80, 0, 20, 10);
rotate(PI/40);
rotate(PI/4);//mouth
rect(0, -50, 30, 30);
rotate(-PI/4);
fill(200);
beginShape();//body
vertex(-80, 0);
bezierVertex(-80, 0, 0, -60, 30, -50);
bezierVertex(30, -50, 70, -30, 30, 40);
bezierVertex(30, 40, 0, 60, -80, 0);
endShape();
fill(126);//eye
translate(40, -40);
ellipse(-5, 5, 30, 30);
fill(0);
ellipse(0, 0, 10, 10);
}
void suninside(float value)
{
float wid=40;
wid=wid+value*100;
if(wid>60)
wid=40;
fill(255,0,0,200);
ellipse(150,100,wid,wid);
fill(255,0,0,100);
ellipse(150,100,60,60);
}
void sunoutside(float value)
{
float wid=70;
wid=wid+value*100;
if(wid>100)
wid=70;
fill(255,255,0,200);
ellipse(150,100,wid,wid);
}
class Layer {
float start, noize, speed;
float yOff, yOff2;
Layer(float _start, float _noize, float _speed) {
start = _start;
noize = _noize;
speed = _speed;
}
void display() {
yOff = yOff2;
noStroke();
for (int x=0; x<width; x+=1) {
float y = start + noise(noize+sin(yOff)*3)*step*3.5;
rect(x, height, 1, -height+y);
yOff+=TWO_PI/(width);
}
yOff2=theta1*speed;
}
}
void mousePressed()
{
if(player.isPlaying())
{
player.pause();
}
else
player.play();
}
二、理论对比
下面是心得体会时间。
手绘VS码绘,动态表现手法到底有哪些不同。
(一)手绘
-
风
这种变现手法,在漫画中很常见,风是一个很抽象的概念,看不见也摸不着,但是这个名词带给我们的感觉就是流动的,画面就是动起来的,绘画中对于风的表现也有多种,粗细分明的线条,或者是明暗分明的光影。
-
材质
不同的材质在人们认知中是不同的,铁是硬的,棉花是软的,在画画中作者表现好这种材质的感觉,飘起的衣服,头发,往往给人一种代入感,让人下意识的就在脑海中浮现出动态的画面。
-
体态的表现
人体的动作的表现在漫画创作中,是每一个漫画技师需要磨练的技术,怎么画的像,评判“像”的标准又是什么呢?当然是和现实中我们日常见到的各种动作相像,当表现好了这种方式的时候,就是给看的人一种潜意识的联想,也就是自己已经脑补出画面了。
说到底,从我的理解角度来看,手绘之所以能够变现出动态的效果,实际上是在用画面刺激我们的大脑去联想,见到风我们就联想到吹过的树叶,见到水流,我们就会想到被拍打的石头。
画面是静止的,但是我们的联想会越来越具有代入感的把你拉进,所有这些静态画面构建的虚拟动态世界中去。
(二)码绘
反过来看码绘,其实更接近与我们经常看的电影,一帧一帧排列在一起,按照时序,一秒多少帧,放出来,我们看起来就是动起来的,但是和真正的制作动画片的流程不一样的,我们不需要亲自一帧一帧的制作,我们只需要负责一个画面的绘制,然后,告诉计算机,这一帧和后面的关系,计算机就会不知疲倦的帮你把所有都画出来。
这么说来,其实码绘也算是艺术创作的一种,你绘制出来的东西是随机的,当在经历了许多的迭代之后,你赋予计算机的链接关系,带来的结果更是具有惊喜的感觉。
好了,就写到这里吧。
三、参考资料
1.《用代码画画》:
0.1 用代码画画——搞艺术的学编程有啥用?
https://blog.csdn.net/magicbrushlv/article/details/77922119
1.1 开始第一幅“码绘”——以编程作画的基本方法
https://blog.csdn.net/magicbrushlv/article/details/77840565
2. 以编程的思想来理解绘画—— (一)用”一笔画“表现“过程美”
https://blog.csdn.net/magicbrushlv/article/details/82634189
4.噪声noise()