【processing】追
非常喜欢星星,非常喜欢特别美特别棒的东西,所以我的自画像是一只一直在追逐光明的兔子。
希望自己永远好奇,永远追逐,赤诚得与在世界上的每一天相恋。
也希望可以有星星点点,在追逐的路上与我相伴。
一追再追
兔子
静态图像
兔子的身子组成部分是头、身体、左耳、右耳、前腿、后退与尾巴,每个部分都是一条bezier曲线。
本来深受平时个人用AI画图的习惯影响,准备所有的图形都用圆来绘制。这里实名感谢yc小天使安利的bezier tool,让我放弃了这个危险的想法。事实证明,当使兔子动起来时,bezier曲线只要移动对应的端点与控制点就可以了,而用圆,即便可以在静态时绘制地好,动态时还是很难控制的。(其实是因为不能像AI中修改锚点)
由于想要天真随意的手绘风格,所以采用的方法是将兔子的代码封装在一个类中,创建多个对象,同时绘制。给每个顶点与控制点都添加一个较小的噪声,就可以使四条线条比较随意地重叠交叉。
绘制方法以身体为例,首先使用bezier tool获得满意的曲线,输出顶点信息:
beginShape();
vertex(633,406);
bezierVertex(521,552,478,382,352,456);
bezierVertex(125,692,697,746,673,458);
endShape();
由于获得的顶点学习是在固定位置的,而我们希望能够动态调整兔子的绘制位置,所以添加x与y到每一个顶点位置坐标中去,实现对位置信息动态修改。
同时,因为希望每一次绘制时的线条都是不同的,而且线条比较连贯顺畅,所以使用了噪声,也添加到每个顶点的位置坐标中。
为了代码的简洁性【大雾,将噪声封装起来。
nosie()函数每次返回一个0-1之间的柏林噪声,与偏移量offset相乘,得到顶点的偏移距离。
由于每绘制一个顶点都会使 i 自增,为了防止长时间运行使浮点数 i 超出取值范围,每次判断,当 i 等于2的23次时,将i重新置零。
float change(){if(i == 2 << 23){i = 0;}return noise(i++) * offset;}
beginShape();float x = xR;float y = yR;vertex(633 + x + change(),406 + y + change());bezierVertex(521 + x + change() + xOffset * 0.5,510 + y + change() + yOffset * 0.8,478 + x + change() + xOffset * 0.8,382 + y + change() + yOffset * 0.8,352 + x + change() + xOffset,456 + y + change() + yOffset);bezierVertex(125 + x + change() + xOffset * 0.8,692 + y + change() + yOffset * 0.8,707 + x + change() + xOffset * 0.3,746 + y + change() + yOffset * 0.3,673 + x + change(),458 + y + change());endShape();
像这样依次绘制可以获得兔子的静态图像。
但是这张图上耳朵并没有画上去
动态图像
那么下一步就是使兔子动起来。
由于听了yc小天使的话,使用的是bezier曲线,所以要动起来只需要动态改变顶点与控制点的坐标即可。
但是要调整到满意的效果还是非常非常非常非常令人绝望的
还是以身体为例,由于兔子在奔跑时,身体会随着奔跑动作拉伸或收缩,所以对最右边的那个点及其控制点添加一个随时间变化的偏移量。
在每次绘制兔子时,都将记录绘制次数的updateCount增加一。
每绘制10次,使其动作变化一次,将变化次数记为runCount。
updateCount++;runCount = updateCount % 10;
我们希望每个动作的周期中动作变化10次,因此将runCount乘上36,每增加10次,回到起点。
float xOffset = 50 - cos(PI - radians(36 * runCount)) * 50;float yOffset = sin(PI - radians(36 * runCount)) * 10 - 10;
将偏移量乘上相对的值,添加在身体后的点及其控制点上。
更新后代码如下:
void drawBody(){beginShape();float x = xR;float y = yR;float xOffset = 50 - cos(PI - radians(36 * runCount)) * 50;float yOffset = sin(PI - radians(36 * runCount)) * 10 - 10;vertex(633 + x + change(),406 + y + change());bezierVertex(521 + x + change() + xOffset * 0.5,510 + y + change() + yOffset * 0.8,478 + x + change() + xOffset * 0.8,382 + y + change() + yOffset * 0.8,352 + x + change() + xOffset,456 + y + change() + yOffset);bezierVertex(125 + x + change() + xOffset * 0.8,692 + y + change() + yOffset * 0.8,707 + x + change() + xOffset * 0.3,746 + y + change() + yOffset * 0.3,673 + x + change(),458 + y + change());endShape();}
同时,由于奔跑时前腿及后退也要前后摆动,此处使用旋转的方法绘制。
以后腿为例:
首先,后腿需要随着身体的伸缩前后移动,所以后腿也需要加上与之前相同的身体部分的偏离量。
float xOffset = 20 - cos(PI - radians(36 * runCount)) * 50;float yOffset = sin(PI - radians(36 * runCount)) * 20 - 70;
其次,后腿自身也应该绕某一点转动。
先使用translate函数将画布平移至轴心,再对画布进行旋转,最后用相对的坐标画图。(此处的坐标相当于局部坐标,图形学忘记了,也第一次用popMatrix、pushMatrix的我debug了大半节课
旋转角度的选择,由于想要有个速度由快到慢再到快的变化过程,所以同样使用了三角函数。
float xStart = 339 + x + change();float yStart = 471 + y + change();float rotation = 1 - abs(sin(radians(18 * runCount)));pushMatrix();translate(xStart + 10, yStart + 80);rotate(-backDeg * rotation);beginShape();...endShape();popMatrix();
void drawBackLeg(){float xOffset = 20 - cos(PI - radians(36 * runCount)) * 50;float yOffset = sin(PI - radians(36 * runCount)) * 20 - 70;float x = xR + 20 + xOffset + cos(PI - radians(36 * runCount)) * 100;float y = yR + yOffset + sin(PI - radians(36 * runCount)) * 30;float xStart = 339 + x + change();float yStart = 471 + y + change();float rotation = 1 - abs(sin(radians(18 * runCount)));pushMatrix();translate(xStart + 10, yStart + 80);rotate(-backDeg * rotation);beginShape();vertex(0, 0);bezierVertex(300 + x + change() - xStart - rotation * 20,466 + y + change() - yStart- rotation * 50,267 + x + change() - xStart - rotation * 20,502 + y + change() - yStart - rotation * 20,221 + x + change() - xStart - rotation * 30,541 + y + change() - yStart - rotation * 40);bezierVertex(175 + x + change() - xStart,548 + y + change() - yStart,104 + x + change() - xStart,556 + y + change() - yStart,104 + x + change() - xStart + rotation * 20 + 10,577 + y + change() - yStart);bezierVertex(121 + x + change() - xStart,593 + y + change() - yStart,173 + x + change() - xStart,569 + y + change() - yStart - rotation * 10,250 + x + change() - xStart - rotation * 80,576 + y + change() - yStart - rotation * 10);bezierVertex(288 + x + change() - xStart - rotation * 50,596 + y + change() - yStart - rotation * 30,340 + x + change() - xStart - rotation * 120,600 + y + change() - yStart - rotation * 30,360 + x + change() - xStart - rotation * 120,583 + y + change() - yStart - rotation * 20);endShape();popMatrix();}
像这样依次绘制可以获得兔子的动态图像
这一张是死机前唯一的一张遗照
光源
光源是用pixel逐点计算得到(总觉得这样性能不太好,但好像确实是使用最频繁的方法之一
首先获得屏幕上的所有像素
loadPixels();
然后计算每个像素与光源位置的距离,并用2000 / distance得到一个值作为该点光照强度的依据。
distance = dist(i, j, lightPos.y, lightPos.x);distance = 2000 / distance;
如果直接将2000 / distance的值赋给该像素,若光源在兔子之后绘制,则会将兔子的图形完全抹去,形成两个图像交替闪烁的结果。
解决方法是判断2000 / distance是否小于1,当小于1时,由于本身作为参数传入color也是黑色,所以直接跳过不绘制,还可以提高性能。
若光源在兔子之前绘制,则兔子的线条上就没有光照(处女座…)
因此此处解决方法为首先绘制兔子等,再绘制光源。
在绘制光源时,先获取本身的颜色,再将光照的颜色添加,可以获得物体被光照后的效果。
if(distance > 1){c = pixels[i * width + j];pixels[i * width + j] = color(distance + red(c), distance + green(c), blue(c), 200);}
最后将添加光照后的图片updatePixel
updatePixels();
完整代码如下:
void drawLight(PVector lightPos){loadPixels();float distance;int c;for(int i = 0; i < height; i++){for(int j = 0; j < width; j++){distance = dist(i, j, lightPos.y, lightPos.x);distance = 2000 / distance;if(distance > 1){c = pixels[i * width + j];pixels[i * width + j] = color(distance + red(c), distance + green(c), blue(c), 200);}}}updatePixels();
}
星星
封装了一个星星的类,每次同时创建30个对象。
对于每一个星星,初始化时随机赋位置值(x\y)、星星半径®、星星向内收缩(size)的程度,创建时随机星星每次旋转角度,即旋转速度(rotateSpeed)
for(int i = 0; i < 30; i++){star[i] = new Star(1100, 200, random(20) + 5, random(0.3) + 0.4);}
Star(float tempX, float tempY, float tempR, float tempSize){xOriginal = tempX;yOriginal = tempY;r = tempR;size = tempSize;rotateSpeed = random(60);init();}
在每颗星星刚刚在起点重新生成时,都实时动态随机其x轴速度(xSpeed),y轴速度(ySpeed)。
因为想要实现星星向上喷涌而出再随风飘落的效果,因此再随机每颗星星初始受到向上的力时上升的最大高度(yHeight)。
同时,因为想要星星受到风的作用后向兔子身上飘落的效果,所以在随机x轴速度时,使用了random(60) - 40,使其向负方向的概率较大,向负方向的速度较大。
void init(){x = xOriginal;y = yOriginal;xSpeed = random(60) - 40;ySpeed = random(30);yHeight = random(500);isReached = 0;deg = 0;force = 0;}
每次更新星星时,按情况分两种:星星刚刚生成,正在飞向最高点,以及星星已经到达最高点,正在下落。
飞向最高点的过程中,由于受到重力作用,速度会逐渐减小,因此将目前y坐标值与最高点y坐标之差作为飞行速度的依据。
到达最高点后,同样由于受到重力作用,速度会逐渐增大,因此同样目前y坐标值与最高点y坐标之差作为飞行速度的依据。
当星星飞出屏幕时,重新调用init函数,使其在光源处随机重新生成。
void update(){checkMouse();if(isReached == 0){x += (y + yHeight - yOriginal) / yHeight * xSpeed;y -= (y + yHeight - yOriginal) / yHeight * ySpeed + force;if(y < yOriginal - yHeight * 0.5){isReached = 1;y += ySpeed;}drawStar();}else{x += (y + yHeight - yOriginal) / yHeight * xSpeed;y += (y + yHeight - yOriginal) / yHeight * ySpeed - force;if((y < 0) || (y > height) || (x < 0) || (x > width)){init();}else{drawStar();}}}
星星的绘制
void drawStar(){deg += rotateSpeed;noStroke();fill(#ffda2b);beginShape();for(int i = 0; i < 360; i += 72){vertex(x + r * cos(radians(i + deg)), y + r * sin(radians(i + deg)));vertex(x + r * cos(radians(i + deg + 36)) * size, y + r * sin(radians(i + deg + 36)) * size);vertex(x + r * cos(radians(i + deg + 72)), y + r * sin(radians(i + deg + 72)));}endShape();}
}
交互
实现了鼠标接触到星星,星星受到向上的力弹起的效果。
每次更新星星时,判断鼠标是否接触到星星。如果接触到,就添加一个力。同时每次更新时,对有力作用的星星都将其作用力减小部分。
绘制时,将力添加到y坐标上。
void checkMouse(){if(force > 0){force -= 4;}if(dist(x, y, mouseX, mouseY) < r * 1.5){force += 40;}}
后记
第一次快要写完时,刚刚和yc说觉得自己可以做完,合上电脑换了教室,再打开电脑就已经死机了。强制关机后打开processing,看着只剩下几十行画曲线的代码,愣 住 了!
然后才发现processing运行后居然不会自动保存。
重新写代码,重新调参数,生无可恋。
【processing】追相关推荐
- 追源索骥:透过源码看懂Flink核心框架的执行流程
https://www.cnblogs.com/bethunebtj/p/9168274.html 追源索骥:透过源码看懂Flink核心框架的执行流程 前言 1.从 Hello,World WordC ...
- 斯坦福大学NLP公开课CS224n上映啦!华人助教陪你追剧
一只小狐狸带你解锁NLP/DL/ML秘籍 作者:小鹿鹿鹿,QvQ,夕小瑶 CS224n: Natural Language Processing with Deep Learning Stanford ...
- 追源索骥:透过源码看懂Flink核心框架的执行流程--来自GitHub
追源索骥:透过源码看懂Flink核心框架的执行流程 联系qq2499496272可进行删除,需要文件版本的私聊!!~ 文章目录 追源索骥:透过源码看懂Flink核心框架的执行流程 前言 1.从 ~~H ...
- Processing 模拟池塘生态系统
Processing 模拟生态系统 博文简介 生物知识 生态系统 实现内容 效果展示 操作说明 相关类 技术讲解 遗传 自治智能体 群集行为 个体行为 问题与思考 参考 其他作品推荐 布料模拟 融入动 ...
- Processing互动编程开发实践之动态文字打乱功能(别嫌长,代码多,图片多)
这篇文章有什么价值? 简单介绍Processing编程 提供一份基于鼠标响应的动画的Processing代码 重头戏:描述动态文字打乱功能实现的重要细节,并将源码公开 1.什么是Processing编 ...
- Processing 案例 | 用粒子系统谱写冰与火之歌
文章目录 引言 FireBrush 分析作品 大概流程 父类:Particle 子类:Fire 子类:Smoke IceAndFire 更多拓展 Frozen Brush 愤怒的小鸟 小结 引言 前不 ...
- 基于《代码本色》的processing学习与拓展
目录 1. 第0章 引言 2. 第1章 向量 3. 第2章 力 4. 第3章 振荡 5. 第4章 粒子系统 1. 第0章 引言 <代码本色>在这一章节的主要内容是模拟随机游走. 讲了利用r ...
- 优秀的 Verilog/FPGA开源项目介绍(二十八)- DSP(Digital Signal Processing)
优秀的 Verilog/FPGA开源项目介绍(二十八)- DSP(Digital Signal Processing) 介绍 FPGA在数字信号处理领域一直有着比通用CPU得天独厚的优势,所以一直都受 ...
- 手机GPU光追新解:详谈Imagination刚发布的DXT架构
最近Imagination Technologies发布新一代IMG DXT架构GPU IP--这次发布的DXT产品主要是面向手机设备的.如果你对Imagination的GPU IP熟的话,应该知道2 ...
- Deep Learning in Natural Language Processing中文连载(三)
第二章 对话语言理解中的深度学习 Gokhan Tur, Asli Celikyilmaz, 何晓东,Dilek Hakkani-Tür 以及邓力 摘要 人工智能的最新进展导致对话助手的可用性增加, ...
最新文章
- 崇阳计算机技校,湖北省崇阳县龙翔技工学校
- 在linux环境下启动es,linux上Elasticsearch 安装配置、网页访问
- 用于磁盘I / O性能SQL Server监视工具
- 使用XML及XSL生成简单HTML
- 单链表java实现及相关操作(版本1)
- 【ML】特征归一化、为什么归一化、归一化的方法、归一化方法选择依据
- 若依ruoyi框架整合magic-api增删改查Demo
- java找不到符号或方法,java 找不到符号解决方法
- 压缩文件下载后无法打开问题解决方法
- 360极速浏览器取消默认迅雷下载的正确方法
- Matlab【路径规划】—— 无人机药品配送路线最优化
- jquery 中 $(document).ready() 与window.onload 的区别
- java 实现word转pdf
- 为什么使用React作为云平台的前端框架(PPT)
- p2psearcher 2013 绿色版下载
- 网上学什么副业赚钱?分享多个赚钱的副业,总有一个适合你
- 【密码学基础】混淆电路(Garbled Circuit)
- CPU mask机制变化
- Pr:文本面板之转录文本
- 在Google Daydream上用VR观看Youtobe视频吧!