将画布看作是640x640的网格,在每个网格中绘制直径为1的圆形,而每个圆形填充颜色的透明度与该位置y坐标值成正相关,即y值越大,透明度越大。
将画面中间裁去一个圆圈,在位于圆圈里面的点的位置绘制直径为1的圆形,每个圆形填充颜色的透明度与该位置y坐标值成负相关,即y值越大,透明度越小。
同时为了让渐变看的更自然,对每个位置填充颜色的透明度加上了randomGaussian(0,10)的高斯噪声。
代码如下
function setup() { createCanvas(640,640); background(255); } function draw(){ push(); var rc = color(random(255),random(255),random(255)); for(var i = 0;i < width;i++){ for(var j = 0;j < height;j++){ // 绘制背景 if (dist(i,j,width/2,height/2)>150) { var ra = constrain(map(j,0,height,-100,150),0,150)+randomGaussian(0,10); rc.setAlpha(ra); fill(rc); noStroke(); ellipse(i,j,1,1); }else { // 绘制玻璃球 var ra = constrain(map(j,0,height,100,-50),0,150)+randomGaussian(0,10); rc.setAlpha(ra); fill(rc); noStroke(); ellipse(i,j,1,1); } } } pop(); noLoop(); } 123456789101112131415161718192021222324252627282930
效果如下
这里有个很大的缺陷,就是每一次绘制背景都需要计算640x640次,故生成时间很慢!!!所以才需要在代码最后加上一个noLoop()防止占用过多进程。
二、花瓣绘制 1. 获取花瓣的贝塞尔曲线起始点和控制点工具:PS
新建画布,大小最好和processing或p5js的画布大小一致;使用钢笔工具绘制出想要的花瓣形状;将工具换成直接选择工具(快捷键 A),打开信息面板,将鼠标移至曲线控制点或起始点处,记录下信息面板中的x与y值(如下图所示)// 存放了四朵不同形状花瓣的集合 var bezierFlowerSet = [ [[261.99,323.60],[258.37,310.12],[246.21,233.20],[267.91,255.88],[288.29,278.56], [258.37,310.12],[261.99,323.60]], [[261.99,323.60],[263.31,292.04],[242.60,254.89],[266.92,254.57],[294.20,255.88], [263.31,292.04],[261.99,323.60]], [[268.06,251.14],[271.77,264.11],[242.74,296.84],[263.12,319.08],[284.94,341.93], [271.77,264.11],[268.06,251.14]], [[284.87,311.02],[317.59,302.05],[315.28,294.52],[322.81,284.67],[331.78,275.11], [347.13,269.61],[351.19,271.35],[354.66,273.67],[363.64,279.75],[361.90,291.91], [360.45,304.07],[348.87,308.71],[339.60,310.15],[332.36,311.31],[317.88,301.76], [284.87,311.02]], ] 12345678910111213 通过坐标转换将花瓣的起始点从PS中的坐标位置移到我们所想要的坐标位置,即将花瓣的所有点坐标(包括起始点和控制点)全部转化为相对于第一个起始点位置的相对坐标(而不是绝对坐标),函数如下所示
// i为花瓣数组的索引,即选择第i种花瓣形状 function calShape(arrSet,i) { var points = []; for(var j = 0;j < arrSet[i].length;j++){ points.push(new Point(arrSet[i][j][0]-arrSet[i][0][0], arrSet[i][j][1]-arrSet[i][0][1])); } return points; } 123456789
花瓣绘制部分完整代码如下所示。
// 存放了四朵不同形状花瓣的集合 var bezierFlowerSet = [ [[261.99,323.60],[258.37,310.12],[246.21,233.20],[267.91,255.88],[288.29,278.56], [258.37,310.12],[261.99,323.60]], [[261.99,323.60],[263.31,292.04],[242.60,254.89],[266.92,254.57],[294.20,255.88], [263.31,292.04],[261.99,323.60]], [[268.06,251.14],[271.77,264.11],[242.74,296.84],[263.12,319.08],[284.94,341.93], [271.77,264.11],[268.06,251.14]], [[284.87,311.02],[317.59,302.05],[315.28,294.52],[322.81,284.67],[331.78,275.11], [347.13,269.61],[351.19,271.35],[354.66,273.67],[363.64,279.75],[361.90,291.91], [360.45,304.07],[348.87,308.71],[339.60,310.15],[332.36,311.31],[317.88,301.76], [284.87,311.02]], ] function setup() { createCanvas(640,640); background(255); } function draw(){ for(var i = 0;i < bezierFlowerSet.length;i++){ var shape = calShape(bezierFlowerSet,i); push(); noStroke(); fill(255,0,0,100); beginShape(); var x = width/bezierFlowerSet.length*i+20; var y = height/2 vertex(x,y); for(var k = 1;k < shape.length;k+=3){ bezierVertex(shape[k].x + x,shape[k].y + y, shape[k+1].x + x,shape[k+1].y + y, shape[k+2].x + x,shape[k+2].y + y,) } endShape(); pop(); } noLoop(); } function calShape(arrSet,i) { var points = []; for(var j = 0;j < arrSet[i].length;j++){ points.push(new Point(arrSet[i][j][0]-arrSet[i][0][0], arrSet[i][j][1]-arrSet[i][0][1])); } return points; } class Point { constructor(tempX,tempY) { this.x = tempX; this.y = tempY; } } 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162
生成花瓣如下所示,由于之前每一种花瓣并不是一起绘制的,所以每种花瓣的大小和方向都有些不同。
有了花瓣以后,将每一种花瓣绕其中心点旋转一周,便可得到完整的花朵,同时放大花瓣并调低其透明度,按照刚才的旋转方案进行再一次旋转,便可绘制更具艺术感的花朵。
为了添加花朵形态的多样性,我还添加了aside属性,来标明这朵花是否是以侧面来面对我们的,此属性可通过减少花瓣的数量和限制花瓣的旋转角度来实现。
具体代码如下所示。
class Flower { constructor(tempX,tempY,petalsNum,tempC,tempS) { var flowerNum = bezierFlowerSet.length + bigFlowerSet.length; //不同形态花瓣的数量 this.x = tempX; this.y = tempY; this.petals = int(petalsNum); // 在一朵花朵中花瓣的数量 this.color = tempC; this.size = tempS; // 调整花朵的大小,使更加有多样性 // 判断是否为侧向花朵 if (random(1) < 0.7) { this.aside = false; }else { this.aside = true; } var shapeIndex = int(random(flowerNum)); // 选择花瓣的形态 if (shapeIndex < bezierFlowerSet.length) { this.shape = calShape(bezierFlowerSet,shapeIndex); } else { this.shape = calShape(bigFlowerSet,shapeIndex-bezierFlowerSet.length); this.petals = int(random(8,10)); if (!this.aside) { this.size /= 2; } } if (this.aside) { // 设置侧向花朵的属性,即减少花瓣数量和限制旋转角度 this.rotateA = random(PI/3,PI/2); this.petals = int(random(10,12)); this.rotateAs = random(-PI/4,PI/4); // 改变花瓣的起始角度,来使得侧面花具备多样性 }else { this.rotateA = TWO_PI; // 完整的花朵 } } display(){ // 缩放花朵大小 translate(this.x,this.y); scale(this.size); translate(-this.x,-this.y); for(var j = 0;j < this.petals-1;j++){ // 绘制花朵 push(); translate(this.x,this.y); rotate(map(j,0,this.petals-1,0,this.rotateA)); // 根据不同的j来计算当前该旋转的角度 translate(-this.x,-this.y); translate(this.x,this.y); // 根据花朵是否是侧向花来决定是否需要缩放 if (this.aside) { scale(randomGaussian(1,0.05)); }else { scale(randomGaussian(1,0.03)); } translate(-this.x,-this.y); if (this.aside) { // 若为侧向花,则调整初始角度 translate(this.x,this.y); rotate(this.rotateAs); translate(-this.x,-this.y); } push(); translate(this.x,this.y); // 根据实际情况,再次调整花朵大小。其实感觉这边有点重复了,但懒得改了。。xD scale(randomGaussian(0.4,0.01)); translate(-this.x,-this.y); this.color.setAlpha(200); // 设置花瓣内围透明度 noStroke(); fill(this.color); beginShape(); // 绘制内围花瓣 vertex(this.x,this.y); for(var k = 1;k < this.shape.length;k+=3){ bezierVertex(this.shape[k].x+this.x,this.shape[k].y+this.y, this.shape[k+1].x+this.x,this.shape[k+1].y+this.y, this.shape[k+2].x+this.x,this.shape[k+2].y+this.y,) } endShape(); pop(); translate(this.x,this.y); if (this.aside) { scale(randomGaussian(1,0.05)); }else { scale(randomGaussian(1,0.02)); } translate(-this.x,-this.y); beginShape(); // 绘制外围花瓣 this.color.setAlpha(100); fill(this.color); noStroke(); vertex(this.x,this.y); for(var k = 1;k < this.shape.length;k+=3){ bezierVertex(this.shape[k].x+this.x,this.shape[k].y+this.y, this.shape[k+1].x+this.x,this.shape[k+1].y+this.y, this.shape[k+2].x+this.x,this.shape[k+2].y+this.y,) } endShape(); pop(); } } } 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111 四、树枝绘制
树枝绘制就比较简单了,就是创建一个数组,其中包含了一段树枝中的各个连接点的信息,通过将连接点以不同strokeWeight连接起来,便可实现树枝的绘制,多说无益,可以看看代码来进行理解。
class Branch { constructor(tempX,tempY,tempL) { this.x = tempX; // 树枝的最底下顶点坐标 this.y = tempY; this.l = tempL; // 树枝的长度 this.points = []; // 树枝连接点数组 this.num = 6; // 树枝连接点数量 this.points.push(new Point(this.x,this.y)); // 将第一个点压入points数组 for(var i = 0;i < this.num;i++){ // 越往上的树枝点就越偏离树枝中心轴的位置 this.points.push(new Point(randomGaussian(this.x,map(i,0,this.num-1,0,5)),this.y-this.l/this.num*i+randomGaussian(0,5))); } ''' new Point(x,y) 其中x为randomGaussian(this.x,map(i,0,this.num-1,0,5)) 后面这个map是用来控制正态分布的方差,简单理解就 是偏离树枝中心轴的位置 y为this.y-this.l/this.num*i+randomGaussian(0,5) ''' this.points = this.points.sort(function (a, b) { // 按照y值大小对连接点进行排序 return b.y - a.y; }) } display(){ push(); noFill(); stroke(75,87,62,random(100,255)); strokeWeight(6); for(var i = 1;i < this.points.length;i++){ // 使用不同的strokeWeight来绘制树枝,越往上的树枝越细 strokeWeight(6-6/this.points.length*i); line(this.points[i-1].x,this.points[i-1].y,this.points[i].x,this.points[i].y); } pop(); } } function draw(){ for (var i = 0; i < branches.length; i++) { push(); var roA = map(flowerNum,8,19,PI/25,PI/10); // 根据花朵的数量多少来控制树枝的旋转角度,防止太密集或者太稀疏 // 将树枝绕其从下往上数第三个节点旋转一定的角度 translate(branches[i].points[2].x,branches[i].points[2].y); var angle = map(i,0,branches.length-1,-roA,roA); rotate(angle); translate(-branches[i].points[2].x,-branches[i].points[2].y); branches[i].display(); // 绘制树枝 pop(); } } 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556 五、绘制蝴蝶结
蝴蝶结主要就是通过贝塞尔曲线来绘制,由于想要增加艺术感,我使蝴蝶结的贝塞尔曲线以不同的大小来进行叠加,从而呈现出一种油画的感觉。
// 蝴蝶结的贝塞尔曲线 var bezierButterflySet = [ [[284.87,311.02],[277.92,303.78],[235.93,315.37],[224.06,314.21],[214.50,313.05], // 蝴蝶结左半边形状 [209,299.73],[209.87,295.10],[211.02,291.91],[206.68,280.04],[226.08,272.51], [238.54,268.45],[277.92,304.94],[284.87,311.02]], [[284.87,311.02],[317.59,302.05],[315.28,294.52],[322.81,284.67],[331.78,275.11], // 蝴蝶结右半边形状 [347.13,269.61],[351.19,271.35],[354.66,273.67],[363.64,279.75],[361.90,291.91], [360.45,304.07],[348.87,308.71],[339.60,310.15],[332.36,311.31],[317.88,301.76], [284.87,311.02]], [[284.87,311.02],[284.87,311.02],[253.30,327.75],[242.01,380.75]], // 蝴蝶结左下角带子 [[284.87,311.02],[284.87,311.02],[342.21,356.13],[328.89,380.45]] // 蝴蝶结右下角带子 ] // 绘制蝴蝶结 function drawButterfly() { var x_all = 0; // 蝴蝶结的位置 var y_all = 0; var butterc = fc; // 蝴蝶结的颜色 var lineDelta = map(flowerNum,8,19,2,5); // 蝴蝶结中心的块 for(var i = 0;i < branches.length;i++){ // 通过树枝的第三个节点即旋转节点位置来计算蝴蝶结的中心位置 x_all += branches[i].points[2].x; y_all += branches[i].points[2].y; } x_all /= branches.length; y_all /= branches.length; for(var i = 0;i < branches.length;i++){ push(); butterc.setAlpha(120); // 设置不透明度 stroke(butterc); strokeWeight(3); noFill(); var deltaY = map(i,0,branches.length-1,-5,5)+randomGaussian(0,2); line(x_all-lineDelta,y_all + deltaY,x_all+lineDelta,y_all+deltaY); // 绘制蝴蝶结中心的块 pop(); } push(); var butternum = 1000; // 叠加数量 for(var i = 0;i < butternum;i++){ var s = (butternum-i)/butternum/2; // 计算当前的缩放大小 var a = constrain(map(i,0,butternum,0,200),0,random(200)); // 计算当前的不透明度 push(); // 对蝴蝶结进行缩放 translate(x_all,y_all); scale(s); translate(-x_all,-y_all); noFill(); strokeWeight(1); butterc.setAlpha(a); stroke(butterc); beginShape(); // 开始绘制蝴蝶结的左半边形状 vertex(x_all,y_all); for(var k = 1;k < butterflySet[0].length;k+=3){ bezierVertex(butterflySet[0][k].x+x_all,butterflySet[0][k].y+y_all, butterflySet[0][k+1].x+x_all,butterflySet[0][k+1].y+y_all, butterflySet[0][k+2].x+x_all,butterflySet[0][k+2].y+y_all) } endShape(); beginShape(); // 开始绘制蝴蝶结的右半边形状 vertex(x_all,y_all); for(var k = 1;k < butterflySet[1].length;k+=3){ bezierVertex(butterflySet[1][k].x+x_all,butterflySet[1][k].y+y_all, butterflySet[1][k+1].x+x_all,butterflySet[1][k+1].y+y_all, butterflySet[1][k+2].x+x_all,butterflySet[1][k+2].y+y_all) } endShape(); // 绘制蝴蝶结的左下的带子 bezier(x_all,y_all,x_all,y_all,butterflySet[2][2].x+x_all,butterflySet[2][2].y+y_all, butterflySet[2][3].x+x_all,butterflySet[2][3].y+y_all); // 绘制蝴蝶结的右下的带子 bezier(x_all,y_all,x_all,y_all,butterflySet[3][2].x+x_all,butterflySet[3][2].y+y_all, butterflySet[3][3].x+x_all,butterflySet[3][3].y+y_all); pop(); } } 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990 作品展示
相关知识
插画教程:怎么画情人节花束
玫瑰花束包装教程,如何制作情人节花束?情人节花束包装视频课程
使用p5.js实现神经网络与遗传算法示例
玫瑰花束钩针 织法教程
包装花束教程花束制作教学鲜花花束包装玫瑰花束扎花教程
玫瑰花束简笔画教程
情人节玫瑰花束简笔画画法图片步骤
适合爱人的花束,七夕情人节送11支玫瑰搭配百合花,详细包装教程
折纸玫瑰花束教程 手工折纸大全
简单易学⭐粘土教程❤️巨可爱手工花束。收藏起来,都有教程哦❤
网址: [p5.js作品教程] 情人节花束 https://m.huajiangbk.com/newsview1362134.html
上一篇: 如何利用扭扭棒制作手工花束 |
下一篇: 简单不织布花束制作教程 |