效果展示:传送门
一、实现步骤
具体详解可见B站up主设计师深海的《一动不动》。这里我就简单叙述一下。
简单来说,分为三步:
-
绘制三个一模一样的图像,并排排列;
-
将三个图像按照左中右的顺序,实现颜色的依次变化,如下所示。
-
将三个图像按照中左右的顺序依次叠加,其中左边图像向左偏离中间图像若干像素,右边图像向右偏离中间图像若干像素。如此,便可实现视错觉现象。如下所示。
二、代码实现
1. 基本框架搭建
var backColor =127;
function setup() {
createCanvas(320,320);
background(backColor);
}
function draw() {
translate(width/2,height/2);
background(backColor);
}
2. 创建Graphics类
综上,我们可以知道,在Graphics类中有以下几种属性:
- 中心坐标
- 颜色
- 镂空圆形的大小(指具有边界但没有填充的圆形)
- 等边三角形长度
在Graphics类中有以下几种方法:
- 绘制图形
- 改变颜色
故Graphics类的创建如下所示。
function Graphics(tempX,tempY) {
// 中心坐标
this.x = tempX;
this.y = tempY;
// 三角形中心到各个顶点的边长
this.l = 50;
// 圆形半径大小
this.r = 100;
// 设置颜色,范围(0~255)
this.black = 255;
// 绘制三角形,通过自建类来实现
this.triangle = new Triangle(tempX,tempY,this.l);
// 定义绘制图形方法
this.display = function() {
push();
stroke(this.black);
strokeWeight(50); // 设置圆形线条粗细
fill(this.black,0); // 使圆形填充完全透明
ellipse(this.x,this.y,this.r*2,this.r*2);
pop();
this.triangle.display(this.black);
}
// 定义改变颜色方法
this.changeColor = function(tempC){
this.black = tempC;
}
}
// 自建圆角三角形类
function Triangle(tempX,tempY,tempL) {
this.x = tempX;
this.y = tempY;
this.l = tempL;
this.display = function(tempC){
push();
fill(tempC);
strokeJoin(ROUND); // 使连接处圆滑过渡
strokeWeight(10);
stroke(tempC);
// 用自定义形状绘制等边圆角三角形
beginShape();
vertex(this.x,this.y-this.l);
vertex(this.x+sqrt(3)*this.l/2,this.y+this.l/2);
vertex(this.x-sqrt(3)*this.l/2,this.y+this.l/2);
endShape(CLOSE);
pop();
}
}
其中等边三角形的三个点坐标计算如下所示。由于p5js的y轴正方向与正常坐标系的y轴正方向相反,故计算时需要注意一下。
Graphics类的声明如下所示。我以第二个图形为中心图形,其余两个图形分别以它作为中心左右偏移三个像素。
var graphics = [];
function setup() {
...
// 创建组合图形
graphics[0] = new Graphics(0,-3);
graphics[1] = new Graphics(0,0);
graphics[2] = new Graphics(0,3);
}
3. 设置灰度值改变机制
我以累计帧数对某个数的取模值作为变量与正弦函数一起来实现灰度值的循环变化。其中有两个map映射,依次为
-
map(count,0,changeRate-1,0,TWO_PI)+0/0.25*PI/0.5*PI
-
map(sin(x),-1,1,0,255)
其中第一个映射是将累计帧数取模后的值从 [0,changeRate-1] => [0,2 π \pi π],以实现正弦函数的周期变化,后面加的0/0.25*PI/0.5*PI是为了实现三个图形在同一时间的不同值。第二个map映射是将正弦值从[-1,1]=>[0,255]以实现正弦值与灰度值的转换。
此后,将组合图形按照中左右次序依次显示便可实现视错觉效果。
部分代码如下所示。
function draw() {
...
var changeRate = 20;
var count = frameCount % changeRate;
var angle = PI / 2;
push();
angleMode(RADIANS);
// 旋转某个角度
rotate(angle);
var c = map(sin(map(count,0,changeRate-1,0,TWO_PI)+0.5*PI),-1,1,0,255);
graphics[2].changeColor(c);
graphics[2].display();
c = map(sin(map(count,0,changeRate-1,0,TWO_PI)),-1,1,0,255);
graphics[0].changeColor(c);
graphics[0].display();
c = map(sin(map(count,0,changeRate-1,0,TWO_PI)+1/2*0.5*PI),-1,1,0,255);
graphics[1].changeColor(c);
graphics[1].display();
pop();
}
4. 简单的改进
我们已经实现了在某个方向的视错觉现象,接着我们可以将其改变为任一方向。故我们通过鼠标的位置来控制视错觉现象的“运动方向”。
首先,定义一个计算某个向量与y轴的夹角函数。因为初始时刻,三角形的朝向为向上,故其方向向量为[0,-1] (p5js中以向下为正方向)。定义代码如下所示。由于 a r c c o s ( x ) ∈ [ 0 , π ] arccos(x)\in[0,\pi] arccos(x)∈[0,π],故需要对计算出来的角度进行进一步的处理,使 a r c c o s ( x ) ∈ [ − π , π ] arccos(x)\in[-\pi,\pi] arccos(x)∈[−π,π]。
function calAngle(x,y) {
var angle = acos(y/sqrt(x*x+y*y));
if (x < 0){
angle = -angle;
}else {
angle = angle;
}
return angle;
}
在draw代码中,将原先的angle声明与定义代码替换成以下代码,便可实现图像向着鼠标的位置“运动”。实现效果如B站视频[processing]Reverse Phi Illusion视错觉现象实现所示。
var angle = calAngle(mouseX-width/2,height/2-mouseY);
// var angle = PI / 2;
想要完整项目源码的小伙伴,可以关注公众号【p5js艺术小站】,后台回复 “视错觉” 来获取呀~