Processing 案例 | 由文字构成的球体

文章目录

  • 引言
  • 准备:球体方程
  • 代码
    • 大体结构
    • 构造函数
    • update
    • display
  • 小结

引言

  球体一直被称为最完美的几何体,它是只有一个面并且连续曲面的立体图形,用肉眼看来球体在各个位置观看都是完全一致的。
  用Processing画一个球体是再简单不过了,只需要下面几行短短的代码,就可以绘制一个球体。

void setup(){
	size(500, 500, P3D);
}
void draw(){
	background(0);
	translate(width / 2, height / 2);
	sphere(200);
}

  以上代码的效果如下。
Processing 案例 | 由文字构成的球体_第1张图片
  但今天我们要绘制的球体是与众不同的,因为它是由文字构成的。请大家看下面的效果。这个作品是作者用JavaScript写的,我在这个基础上做了相应的简化,用Processing近似模拟了这个效果,接下来就给大家讲解如何用代码实现。

  

准备:球体方程

  对于球体上的任何的一个点P来说, 我们可以用三个变量来描述:第一个是点P到球心的长度,也就是该球体的半径;第二个是点P和球心的连线与x、y轴所构成的平面所成的夹角的角度;第三个就是点P在x、y轴所构成的平面的投影点与球心的连线和x轴所成夹脚的角度。
  上面所说的三个变量分别对应下面的r、B、A。
Processing 案例 | 由文字构成的球体_第2张图片
  那么接下来我们要做的就是求点P在直角坐标系下的坐标(x,y,z),根据上图我们不难得出以下的等式。

x = r * cos(B) * cos(A)
y = r * cos(B) * sin(A)
z = r * sin(B)

  那么在理解了如何表示球体上的一个点,并且如何求得该点在直角坐标系下的坐标后,我们就可以开始看代码了。
  

代码

大体结构

  代码的大体结构非常的简单。
  首先,在setup函数中将作品设置成3D模式,同时初始化一个球体,并且将这个球体的球心设置为屏幕的中心,然后将该球体的半径设置为500。
  这之后在draw函数中对这个球进行更新和绘制。update函数主要用于对球体进行旋转,display函数主要用于将球体绘制在屏幕上。
  该作品只自定义了一个Ball类,那么接下来我们就看看这个类具体是怎样的。

Ball b;
void setup() {
  fullScreen(P3D);
  b = new Ball(width / 2, height / 2, 500);
}

void draw() {
  background(255);
  b.update();
  b.display();
}
class Ball{}

  

构造函数

  第一步我们来看看Ball类的构造函数。
  首先是设置了球体的位置,这里需要注意的是我们将球体到屏幕的距离设置成了半径的长度。然后是设置了球体的半径。
  这之后是设置了绘制球体的起始角度和球体转动的角速度。

  Ball(float x, float y, float _r) {
    location = new PVector(x, y, -_r);
    r = _r;
    startAngle = 0;
    angleVelocity = -0.1;
  }

  看完了Ball构造函数需要的参数,下面就来看看Ball需要的成员变量。

  // 位置
  PVector location;
  // 半径、起始角度、角速度
  float r, startAngle, angleVelocity;

  

update

  第二步我们来看看update函数。
  前面提到了,update的函数的作用是让球体进行旋转,所以在这个函数中我们要不断的更新绘制球体的初始角度。具体实现如下,在看了接下来的display函数后,大家应该能更加理解update函数的作用。

void update() {
    startAngle += angleVelocity;
  }

  

display

  最后我们来看这个作品中最关键的地方:display 函数,也就是如何绘制这个球体。
  大体思路非常的简单:我们首先在球体上取一些点,然后在这些点绘制相应的文字即可。
  在该函数中首先我们把坐标原点设置为球心,然后将字体设置为黑色,并设置字体的大小和对其方式。
  接下来我们开始在球体上取点。取样的方法很将简单,我们每隔一定的间隔从0到360度枚举角度A,然后每隔一定的间隔从-90到90度枚举角度B,至于枚举的间隔由变量step决定,step越大那么点越稀疏,step越小那么点越密集。
  offset的作用是让同一个角度B的点有一个向上或者向下的偏移,让它们不再同一条水平线上(下图中的红线),形成错落有致的效果。
Processing 案例 | 由文字构成的球体_第3张图片
  因为注意到该作品中球体上只有朝向屏幕这一半有文字,所以只有当这个点的z大于零的时候才绘制文字。
  讲解到这里,大家看懂下面的代码应该就没有问题了。

void display() {
    translate(location.x, location.y, location.z);
    fill(0);
    textSize(40);
    textAlign(CENTER);

    float step = 10; // 控制字的疏密程度
    float offset = step / 4;// 
    for (int i = 0; i < 360; i+=step) {
      offset *= -1;
      for (int j = -90; j < 90; j+=step) {
      	// startAngle控制绘制球体的起始角度
      	// offset让文字不再一条直线上
        float theta1 = radians(i + startAngle), theta2 = radians(j + offset);
        float x = r * cos(theta2) * cos(theta1);
        float y = r * sin(theta2);
        float z = r * cos(theta2) * sin(theta1);
        // 绘制一半的球体
        if (z > 0)
          text("lmao", x, y, z);
      }
    }
  }

  

小结

  该作品同样也有很多可以拓展的地方,比如改变在球体上取点的方式,在每一个点绘制不同的图案等。我就在这个作品的基础上,绘制了一个印有不同表情包的彩虹球,效果如下。

  同时球体彩条的颜色还可以有更多的拓展, 比如参考大名鼎鼎的苹果的logo。
Processing 案例 | 由文字构成的球体_第4张图片
  获得效果如下。

  感兴趣的同学可以去我的openprocessing看在线效果,同时两个作品的完整代码我已经放在我的github上了,欢迎大家查看和提出问题。
  最后祝大家暑假快乐!

你可能感兴趣的:(Processing 案例 | 由文字构成的球体)