p5.js绘制一个动态的自画像
第二次使用p5.js,用代码绘制一个自画像(当然,肯定不是自画像。)这是一个非常简单的任务,主要用贝塞尔曲线来绘制。
我们把整个图像分为动态刷新的背景、静态(每一帧重绘的)人像以及叠加在最上方的动态的雨点。
背景
我希望能够拥有一块能够在随机位置生成随机颜色色条并保留的背景画布,这需要我们每一帧计算色块需要被绘制的位置和长度,并判断是否重新绘制一条新的。
这里的思路是,设置一个全局的变量用来记录是否一条色块已经被绘制完。它每一帧增加1,当增加到画布的宽(或高)时重新置为0。每一次对它进行判断,若是小于800,则继续绘制上一个色块。
代码如下:
function drawRects()
{
push();
if(paintRect >800)//画完了
{
paintRect = 0;
//新的一个
for(var i =0;i<4;i++)
{
ranR[i] = Math.ceil(Math.random()*(255-180)+180);
ranG[i] = Math.ceil(Math.random()*(255-180)+180);
ranB[i] = Math.ceil(Math.random()*(255-180)+180);
ranX[i] = Math.ceil(Math.random()*800);
ranY[i] = Math.ceil(Math.random()*800);
ranW[i] = Math.ceil(Math.random()*(100-20)+20);
}
}
else //继续
{
noStroke();
fill(ranR[0], ranG[0], ranB[0], 100);
rect(0, ranY[0], paintRect, ranW[0]);
fill(ranR[1], ranG[1], ranB[1], 100);
rect(0, ranY[1], paintRect, ranW[1]);
fill(ranR[2], ranG[2], ranB[2], 100);
rect(ranX[2], 0, ranW[2], paintRect);
fill(ranR[3], ranG[3], ranB[3], 100);
rect(ranX[3], 0, ranW[3], paintRect);
paintRect += 10;
}
pop();
}
效果:
人像
这一部分非常简单,就是用贝塞尔曲线来拟合人像的线条。虽然繁琐,但是很简单。emmmm就不贴代码了,直接给出结果。
顶部的飘雨效果
因为这一层需要永远浮在最上层,所以只能通过createGrapghics()函数重新开一个图层,然后把这个图层叠加到原来的图上去。这里我遇到了一个很大的问题。因为这里的图层是直接铺上去的,所以和一开始最后的背景产生了冲突。背景板上没有每一帧重绘的部分会被掉落的雨滴的拖影影响。
像这样:
很显然,这不是我们想要的效果。但是如果把顶层图像的透明度通道取消,效果则是正常的:
可知,要想使得图像显示正常,就必须把背景板上的部分每一帧全部重绘。这里有两个解决方法:
1.把每一个绘制过的条形记录下来,每一帧在前一帧图像的基础上(重新绘制绘制过的部分)绘制新的条形。
2.把每一帧的图像保存下来,每一帧先绘制上一帧保留下来的图像,再以它为背景板,绘制新的条形。
但是!我都没有选择。
我最后发现,或许可以舍弃这个每帧叠加条形的效果。
只需在顶层画布中,通过backgroud刷新背景,并给它一些透明度。这样效果非常好。既可以产生一定的拖尾效果,又能够拥有更加动感的背景板。
绘制雨点的代码:
function drawRain()
{
pg.clear();
pg.background(0,0,0);
for (var i = 0; i < numOfDrops; i++)
{
raindrops[i].fall();
raindrops[i].show();
if (raindrops[i].reachedBottom())
{
raindrops[i].y = 0;
}
}
}
function Raindrop(x, y)
{
this.x = random(windowWidth);
this.y = random(-100, -600);
this.z = random(1, 3);
this.speed = random(1, 3);
this.gravity = random(1, 2);
this.len = random(3, 10);
this.rgb = [random(255), random(255), random(255)];
this.fall = function() {
this.y += this.speed;
this.y += this.gravity;
}
this.reachedBottom = function() {
if (this.y > 800) {
return true;
} else {
return false;
}
}
this.show = function() {
pg.strokeWeight(this.z);
pg.stroke(this.rgb[0], this.rgb[1], this.rgb[2]);
pg.line(this.x, this.y, this.x, this.y + this.len);
}
}
当然,需要在初始化的时候进行一些参数设置。
setup()函数中:
pg = createGraphics(800, 800);
frameRate(60);
for(var i=0; i<4; i++)
{
ranR[i] = Math.ceil(Math.random()*(255-160)+160);
ranG[i] = Math.ceil(Math.random()*(255-160)+160);
ranB[i] = Math.ceil(Math.random()*(255-160)+160);
ranX[i] = Math.ceil(Math.random()*800);
ranY[i] = Math.ceil(Math.random()*800);
ranW[i] = Math.ceil(Math.random()*(100-20)+20);
}
numOfDrops = 400;
for (var i = 0; i < 800; i++)
{
raindrops[i] = new Raindrop();
}
差不多了!
最后的效果是这样的:(其实是因为我不想再改了)
心得
还挺好玩的。在这个过程里收到了小汪同学的教导,想在码绘和动态图像处理上继续多学一些(想学很多很多)东西。