本文簡介
帶尬猴固阁,我嗨德育處主任
前面寫了幾篇 p5.js
文章 都還沒涉及到3D圖形,但其實(shí) p5.js
是提供了基礎(chǔ)的3D圖形的城菊。
本文就從最簡單的立方體講起备燃,并做幾個(gè)小demo和各位工友一起掌握立方體的用法。
立方體的基礎(chǔ)用法
在 p5.js
里使用 box()
方法可以創(chuàng)建立方體凌唬。
基礎(chǔ)語法說明
根據(jù)官網(wǎng)的說明并齐,box()
語法如下:
box([width], [height], [depth], [detailX], [detailY])
官網(wǎng)給出的參數(shù)解釋我覺得有點(diǎn)繞,以下是我的理解
-
width
:立方體的寬度(選填)客税,默認(rèn)值是 50况褪。 -
height
:立方體的高度(選填)。 -
depth
:立方體的深度(選填)更耻。 -
detailX
:一個(gè)用于指定立方體在x軸方向上的細(xì)分級(jí)別的數(shù)字窝剖,數(shù)值越大,立方體的表面越平滑酥夭。(選填) -
detailY
:一個(gè)用于指定立方體在y軸方向上的細(xì)分級(jí)別的數(shù)字赐纱,數(shù)值越大,立方體的表面越平滑熬北。(選填)
首先需要了解 width
疙描、height
和 depth
這3個(gè)參數(shù),它們都是可選參數(shù)讶隐,傳參時(shí)會(huì)出現(xiàn)以下幾種情況:
- 3個(gè)參數(shù)都不傳的情況:它們的值默認(rèn)為50起胰。
- 只傳
width
的情況:height
和depth
都會(huì)跟著使用width
的值。 - 傳了
width
和height
的情況:depth
會(huì)使用height
的值巫延。 - 3個(gè)參數(shù)都有傳的情況:各自使用各自的值效五。
動(dòng)手試試
先試試創(chuàng)建一個(gè)基礎(chǔ)立方體。
<script>
function setup() {
createCanvas(200, 200, WEBGL) // 創(chuàng)建 200 * 200 的畫布
background(200) // 設(shè)置畫布背景色(灰色)
box(100) // 創(chuàng)建立方體
}
</script>
這個(gè)例子使用 box()
創(chuàng)建出來的立方體炉峰,看上去不像立方體畏妖,只是一個(gè)平面。主要原因是我們是正對(duì)著它疼阔,所以只能看到它的一個(gè)面戒劫。
旋轉(zhuǎn)一下角度就看到它是一個(gè)立方體了。
<script>
function setup() {
createCanvas(200, 200, WEBGL) // 創(chuàng)建 200 * 200 的畫布
background(200) // 設(shè)置畫布背景色(灰色)
// 旋轉(zhuǎn)角度
rotateX(10)
rotateY(10)
box(100) // 創(chuàng)建立方體
}
</script>
設(shè)置樣式
給立方體設(shè)置樣式婆廊,要把樣式函數(shù)寫在 box()
前Q赶浮!淘邻!
填充色 fill
使用 fill()
方法可以設(shè)置填充色茵典。
<script>
function setup() {
createCanvas(200, 200, WEBGL) // 創(chuàng)建 200 * 200 的畫布
background(200) // 設(shè)置畫布背景色(灰色)
// 旋轉(zhuǎn)角度
rotateX(10)
rotateY(10)
// 填充色
fill(255, 0, 0)
box(100) // 創(chuàng)建立方體
}
</script>
不使用填充顏色
box()
的默認(rèn)填充色是白色,如果你不需要填充色宾舅,可以使用 noFill()
方法進(jìn)行修改统阿。
<script>
function setup() {
createCanvas(200, 200, WEBGL) // 創(chuàng)建 200 * 200 的畫布
background(200) // 設(shè)置畫布背景色(灰色)
// 旋轉(zhuǎn)角度
rotateX(10)
rotateY(10)
// 不使用填充顏色
noFill()
box(100) // 創(chuàng)建立方體
}
</script>
描邊顏色 stroke
使用 stroke()
方法可以設(shè)置立方體的描邊顏色枚尼。
<script>
function setup() {
createCanvas(200, 200, WEBGL)
background(200)
noFill()
stroke(255, 0, 0) // 設(shè)置紅色邊框顏色
rotateX(10)
rotateY(10)
box(100)
}
</script>
設(shè)置邊框?qū)挾?/h3>
使用 strokeWeight()
方法可以設(shè)置立方體邊框?qū)挾龋枰獋魅胍粋€(gè)數(shù)值型數(shù)據(jù)砂吞。
<script>
function setup() {
createCanvas(200, 200, WEBGL)
background(200)
noFill()
stroke(255, 0, 0) // 設(shè)置紅色邊框顏色
strokeWeight(4) // 設(shè)置邊框?qū)挾?
rotateX(10)
rotateY(10)
box(100)
}
</script>
紋理
除了基礎(chǔ)的填充和描邊外署恍,立方體還可以設(shè)置紋理。
紋理可以是圖片蜻直,也可以是視頻盯质。我先用圖片資源舉例。
加載資源需要在 preload()
這個(gè)生命周期里處理概而,我在 《p5.js 光速入門》 里有講到呼巷,忘記這知識(shí)點(diǎn)的工友可以去看看。
將紋理貼到立方體上赎瑰,有以下幾個(gè)步驟:
- 加載紋理資源(圖片或者視頻)
- 設(shè)置紋理
- 創(chuàng)建立方體
<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) // 設(shè)置紋理
box(100) // 創(chuàng)建立方體
}
</script>
在這個(gè)例子中王悍,我加載了一個(gè) gif 紋理,但這個(gè)紋理貼到立方體上是不會(huì)動(dòng)的餐曼,因?yàn)榱⒎襟w是在 setup()
里創(chuàng)建的压储,如果需要它會(huì)動(dòng),我們需要在 draw()
聲明周期里設(shè)置紋理和創(chuàng)建立方體源譬。這部分我會(huì)放到后面“動(dòng)畫”章節(jié)講集惋。
光照效果
你沒看錯(cuò),p5.js
也有提供了光照效果的踩娘,我在前面的文章沒講過光照效果刮刑,本文也不會(huì)講這部分(我要留到下一篇水文里講),但工友們也可以先了解一下這部分內(nèi)容养渴。
我使用了 環(huán)境光 ambientLight()
和 定向光 directionalLight()
打在立方體上雷绢。
<script>
function setup() {
createCanvas(200, 200, WEBGL)
background(200)
rotateX(10)
rotateY(10)
ambientLight(255) // 設(shè)置環(huán)境光照
directionalLight(255, 255, 255, 0, 0, -1) // 設(shè)置定向光照
box(100)
}
</script>
可以看出,不同面的顏色是有點(diǎn)不一樣的理卑。
動(dòng)畫
要做動(dòng)畫非常簡單翘紊,只需要在 draw()
生命周期里改變立方體的屬性即可。
除此之外傻工,我們還要了解 frameCount
霞溪,這是 p5.js
提供的一個(gè)全局系統(tǒng)變量,它記錄了 p5.js
運(yùn)行了多少幀中捆。在 setup()
時(shí),frameCount
的值是0坊饶,之后每執(zhí)行一次 draw()
都會(huì)給 frameCount
加1泄伪。
還不清楚 draw()
這個(gè)生命周期的工友一定要看看 《p5.js 光速入門》的“drap”章節(jié)。
旋轉(zhuǎn)動(dòng)畫
比如想做旋轉(zhuǎn)動(dòng)畫匿级,只要在 draw()
里不斷的改變 rotateX
蟋滴、 rotateY
或 rotateX
就能出一個(gè)不錯(cuò)的效果染厅。
<script>
function setup() {
createCanvas(200, 200, WEBGL)
}
function draw() {
// 每次刷新都要重新填充畫布顏色,不然會(huì)留下上一次繪制的立方體
background(200)
// 旋轉(zhuǎn)
rotateX(frameCount * 0.01)
rotateY(frameCount * 0.01)
rotateZ(frameCount * 0.01)
// 繪制立方體
box(100)
}
</script>
gif 貼圖
在前面的紋理例子中我們已經(jīng)知道怎么貼圖了津函,如果你貼的是gif動(dòng)圖肖粮,又希望這個(gè)圖是真的能在運(yùn)行時(shí)動(dòng)起來,就需要在 draw()
設(shè)置紋理貼圖了尔苦。
<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)
// 設(shè)置紋理貼圖
texture(myTexture)
box(100)
}
</script>
視頻紋理
設(shè)置視頻紋理其實(shí)和設(shè)置圖片紋理差不多涩馆,只是加載的資源類型不同。
使用 createVideo()
方法加載視頻資源允坚,然后要將視頻隱藏魂那,不然它會(huì)在頁面中占位。
<script>
let video = null
function preload() {
video = createVideo('video.mp4') // 加載 mp4
video.hide() // 隱藏視頻元素
}
function setup() {
createCanvas(640, 480, WEBGL)
video.loop() // 循環(huán)播放
video.volume(0) // 設(shè)置音量
}
function draw() {
background(200)
rotateX(frameCount * 0.01)
rotateY(frameCount * 0.01)
// 設(shè)置視頻紋理
texture(video)
box(200)
}
</script>
上面的代碼還是用了 video.loop()
和 video.volume()
方法稠项。
-
video.loop()
:循環(huán)播放涯雅。 -
video.volume()
:設(shè)置視頻音量,取值范圍是0 ~ 1
展运。
小案例
p5.js
是一個(gè)偏藝術(shù)類的 canvas
庫活逆,我們已經(jīng)掌握了 box()
基礎(chǔ)用法創(chuàng)建出立方體,接下來再理解幾個(gè)小案例應(yīng)該就有能力自己去實(shí)現(xiàn)一些特效了拗胜。非常適合在掘金整活~
案例1:Rotate Push Pop
第一個(gè)案例叫《Rotate Push Pop》划乖,是 processing 的一個(gè)例子,我把他的代碼轉(zhuǎn)成使用 p5.js
編寫挤土。
先提一嘴 processing
和 p5.js
的關(guān)系:processing
是用 Java
編寫的琴庵,而 p5.js
是 processing
的 JS
版。
想了解 processing
可以找 『南方者哥哥』仰美,他寫過 Processing 相關(guān)的文章迷殿。
先看看本例效果和代碼
<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>
這個(gè)例子用到 《p5.js 狀態(tài)管理》 和 《p5.js map映射》 的知識(shí),工友們可以先自行理解咖杂,如果不明白的話我再在評(píng)論區(qū)留下該例子的注解庆寺。
案例2:運(yùn)動(dòng)的立方體們
<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>
這個(gè)例子我結(jié)合了 《p5.js 變換操作》 和 《p5.js 狀態(tài)管理》 里講到的知識(shí)。
letter
創(chuàng)建了一堆坐標(biāo)點(diǎn)诉字,他們記錄了立方體們的位置懦尝。在 draw()
里不斷的改變他們的位置。
為了讓立方體們?cè)?translate()
時(shí)不會(huì)相互影像壤圃,需要使用 push()
和 pop()
讓它們“相互隔離開”陵霉。
案例3:排列立方體
<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”這個(gè)例子相對(duì)起來會(huì)簡單很多伍绳,它有點(diǎn)像我們剛學(xué) JS
時(shí)做的 “九九乘法表” 的練習(xí)踊挠。
案例4:還是立方體,我不知道怎么起名了
再編一個(gè)立方體案例吧冲杀,盡力了效床。睹酌。。
<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>
還是使用了前面例子中的方法剩檀,瞎改了一下憋沿。
我實(shí)在是沒有藝術(shù)感??
推薦閱讀
點(diǎn)贊 + 關(guān)注 + 收藏 = 學(xué)會(huì)了
代碼倉庫