在 p5.js
里使用 box()
方法可以创建立方体。
根据官网的说明,box()
语法如下:
box([width], [height], [depth], [detailX], [detailY])
官网给出的参数解释我觉得有点绕,以下是我的理解
width
:立方体的宽度(选填),默认值是 50。height
:立方体的高度(选填)。depth
:立方体的深度(选填)。detailX
:一个用于指定立方体在x轴方向上的细分级别的数字,数值越大,立方体的表面越平滑。(选填)detailY
:一个用于指定立方体在y轴方向上的细分级别的数字,数值越大,立方体的表面越平滑。(选填)首先需要了解 width
、height
和 depth
这3个参数,它们都是可选参数,传参时会出现以下几种情况:
width
的情况:height
和 depth
都会跟着使用 width
的值。width
和 height
的情况:depth
会使用 height
的值。先试试创建一个基础立方体。
<script> function setup() { createCanvas(200, 200, WEBGL) // 创建 200 * 200 的画布 background(200) // 设置画布背景色(灰色) box(100) // 创建立方体 } </script>
这个例子使用 box()
创建出来的立方体,看上去不像立方体,只是一个平面。主要原因是我们是正对着它,所以只能看到它的一个面。
旋转一下角度就看到它是一个立方体了。
<script> function setup() { createCanvas(200, 200, WEBGL) // 创建 200 * 200 的画布 background(200) // 设置画布背景色(灰色) // 旋转角度 rotateX(10) rotateY(10) box(100) // 创建立方体 } </script>
给立方体设置样式,要把样式函数写在 box()
前!!!
使用 fill()
方法可以设置填充色。
<script> function setup() { createCanvas(200, 200, WEBGL) // 创建 200 * 200 的画布 background(200) // 设置画布背景色(灰色) // 旋转角度 rotateX(10) rotateY(10) // 填充色 fill(255, 0, 0) box(100) // 创建立方体 } </script>
box()
的默认填充色是白色,如果你不需要填充色,可以使用 noFill()
方法进行修改。
<script> function setup() { createCanvas(200, 200, WEBGL) // 创建 200 * 200 的画布 background(200) // 设置画布背景色(灰色) // 旋转角度 rotateX(10) rotateY(10) // 不使用填充颜色 noFill() box(100) // 创建立方体 } </script>
使用 stroke()
方法可以设置立方体的描边颜色。
<script> function setup() { createCanvas(200, 200, WEBGL) background(200) noFill() stroke(255, 0, 0) // 设置红色边框颜色 rotateX(10) rotateY(10) box(100) } </script>
使用 strokeWeight()
方法可以设置立方体边框宽度,需要传入一个数值型数据。
<script> function setup() { createCanvas(200, 200, WEBGL) background(200) noFill() stroke(255, 0, 0) // 设置红色边框颜色 strokeWeight(4) // 设置边框宽度 rotateX(10) rotateY(10) box(100) } </script>
除了基础的填充和描边外,立方体还可以设置纹理。
纹理可以是图片,也可以是视频。我先用图片资源举例。
将纹理贴到立方体上,有以下几个步骤:
<script> let myTexture = null function preload() { myTexture = loadImage('texture.gif') // 加载纹理 } function setup() { createCanvas(200, 200, WEBGL) background(200) rotateX(0.5) rotateY(0.5) texture(myTexture) // 设置纹理 box(100) // 创建立方体 } </script>
在这个例子中,我加载了一个 gif 纹理,但这个纹理贴到立方体上是不会动的,因为立方体是在 setup()
里创建的,如果需要它会动,我们需要在 draw()
声明周期里设置纹理和创建立方体。这部分我会放到后面“动画”章节讲。
你没看错,p5.js
也有提供了光照效果的,我在前面的文章没讲过光照效果,本文也不会讲这部分(我要留到下一篇水文里讲),但工友们也可以先了解一下这部分内容。
我使用了 环境光 ambientLight()
和 定向光 directionalLight()
打在立方体上。
<script> function setup() { createCanvas(200, 200, WEBGL) background(200) rotateX(10) rotateY(10) ambientLight(255) // 设置环境光照 directionalLight(255, 255, 255, 0, 0, -1) // 设置定向光照 box(100) } </script>
可以看出,不同面的颜色是有点不一样的。
要做动画非常简单,只需要在 draw()
生命周期里改变立方体的属性即可。
除此之外,我们还要了解 frameCount
,这是 p5.js
提供的一个全局系统变量,它记录了 p5.js
运行了多少帧。在 setup()
时,frameCount
的值是0,之后每执行一次 draw()
都会给 frameCount
加1。
比如想做旋转动画,只要在 draw()
里不断的改变 rotateX
、 rotateY
或 rotateX
就能出一个不错的效果。
<script> function setup() { createCanvas(200, 200, WEBGL) } function draw() { // 每次刷新都要重新填充画布颜色,不然会留下上一次绘制的立方体 background(200) // 旋转 rotateX(frameCount * 0.01) rotateY(frameCount * 0.01) rotateZ(frameCount * 0.01) // 绘制立方体 box(100) } </script>
在前面的纹理例子中我们已经知道怎么贴图了,如果你贴的是gif动图,又希望这个图是真的能在运行时动起来,就需要在 draw()
设置纹理贴图了。
<script> let myTexture = null function preload() { myTexture = loadImage('texture.gif') // 加载gif } function setup() { createCanvas(200, 200, WEBGL) } function draw() { background(200) rotateX(frameCount * 0.01) rotateY(frameCount * 0.01) rotateZ(frameCount * 0.01) // 设置纹理贴图 texture(myTexture) box(100) } </script>
设置视频纹理其实和设置图片纹理差不多,只是加载的资源类型不同。
使用 createVideo()
方法加载视频资源,然后要将视频隐藏,不然它会在页面中占位。
<script> let video = null function preload() { video = createVideo('video.mp4') // 加载 mp4 video.hide() // 隐藏视频元素 } function setup() { createCanvas(640, 480, WEBGL) video.loop() // 循环播放 video.volume(0) // 设置音量 } function draw() { background(200) rotateX(frameCount * 0.01) rotateY(frameCount * 0.01) // 设置视频纹理 texture(video) box(200) } </script>
上面的代码还是用了 video.loop()
和 video.volume()
方法。
video.loop()
:循环播放。video.volume()
:设置视频音量,取值范围是 0 ~ 1
。p5.js
是一个偏艺术类的 canvas
库,我们已经掌握了 box()
基础用法创建出立方体,接下来再理解几个小案例应该就有能力自己去实现一些特效了。非常适合在掘金整活~
第一个案例叫《Rotate Push Pop》,是 processing 的一个例子,我把他的代码转成使用 p5.js
编写。
先提一嘴 processing
和 p5.js
的关系:processing
是用 Java
编写的,而 p5.js
是 processing
的 JS
版。
先看看本例效果和代码
<script> let a = 0 let offset = Math.PI / 24 let num = 12 function setup() { createCanvas(440, 460, WEBGL) noStroke() } function draw() { lights() background(200, 200, 200) for(let i = 0; i < num; i++) { // map 映射 let gray = map(i, 0, num - 1, 0, 255) push() fill(gray) rotateY(a + offset * i) rotateX(a / 2 + offset * i) box(200) pop() } a += 0.01 } </script>
<script> function setup() { createCanvas(400, 400, WEBGL) } let letter = [ [0, 0, 0], [0, 0, 50], [25, 0, 25], [0, 50, 0], [0, 50, 50], [25, 25, 25], [50, 0, 0], [50, 0, 50], [50, 50, 0], [50, 50, 50] ] function draw() { background(200) rotateX(frameCount * 0.01) rotateY(frameCount * 0.01) for (let i = 0; i < letter.length; i++) { push() translate(letter[i][0], letter[i][1], letter[i][2]) box(10) pop() } } </script>
letter
创建了一堆坐标点,他们记录了立方体们的位置。在 draw()
里不断的改变他们的位置。
为了让立方体们在 translate()
时不会相互影像,需要使用 push()
和 pop()
让它们“相互隔离开”。
<script> function setup() { createCanvas(400, 400, WEBGL) } function draw() { background(200); rotateX(frameCount * 0.01); rotateY(frameCount * 0.01); for (let x = -50; x <= 50; x += 10) { for (let y = -50; y <= 50; y += 10) { for (let z = -50; z <= 50; z += 10) { push(); translate(x, y, z); box(5); pop(); } } } } </script>
如果你理解了“案例2”,那么“案例3”这个例子相对起来会简单很多,它有点像我们刚学 JS
时做的 “九九乘法表” 的练习。
再编一个立方体案例吧,尽力了。。。
<script> function setup() { createCanvas(400, 400, WEBGL) } function fractalBox(size, level) { box(size) if (level > 1) { level-- push() translate(-size/2, -size/2, -size/2) fractalBox(size/2, level) pop() push() translate(-size/2, -size/2, size/2) fractalBox(size/2, level) pop() push() translate(-size/2, size/2, -size/2) fractalBox(size/2, level) pop() push() translate(-size/2, size/2, size/2) fractalBox(size/2, level) pop() push() translate(size/2, -size/2, -size/2) fractalBox(size/2, level) pop() push() translate(size/2, -size/2, size/2) fractalBox(size/2, level) pop() push() translate(size/2, size/2, -size/2) fractalBox(size/2, level) pop() push() translate(size/2, size/2, size/2) fractalBox(size/2, level) pop() } } function draw() { background(200) rotateX(frameCount * 0.01) rotateY(frameCount * 0.01) fractalBox(200, 3) } </script>
还是使用了前面例子中的方法,瞎改了一下。
我实在是没有艺术感😭