【3D游戲基礎(chǔ)】蒙皮骨骼動(dòng)畫與骨架
目標(biāo)!畫出蒙皮動(dòng)畫的骨架。
https://www.bilibili.com/video/BV1pM411m7Yw
https://zfxdvouj61.feishu.cn/file/boxcnwgESO6zdQetO7oNhKboNsd
以下為PPT文字稿,建議還是看視頻
講講自己對蒙皮骨骼動(dòng)畫的理解,并在 Cocos Creator 3.6 中繪制出骨架~希望對大家有幫助~

這是我們今天實(shí)現(xiàn)效果,在 Cocos Creator 中把骨架畫出來,圖中綠色的線就是繪制的骨架

在開始之前,我們介紹一下如何導(dǎo)入模型?長按3d資源拖入到資源管理器中.

讓我們看看gltf資源包括什么(介紹最下方),mesh 網(wǎng)格,texture 貼圖,material 材質(zhì),animation 動(dòng)畫 ,skeleton 骨架數(shù)據(jù)。根節(jié)點(diǎn)有個(gè)動(dòng)畫組件,cips是動(dòng)畫列表,default clip 是默認(rèn)動(dòng)畫, play on load 是加載后自動(dòng)播放。Sockets 是掛點(diǎn)系統(tǒng)。啟用 useBakedAnimation 時(shí)會使用預(yù)烘焙骨骼動(dòng)畫系統(tǒng)(所有動(dòng)畫數(shù)據(jù)都會按照指定幀率提前預(yù)采樣、烘焙到全局復(fù)用的骨骼動(dòng)畫貼圖合集上),禁用 useBakedAnimation 后會使用實(shí)時(shí)計(jì)算骨骼動(dòng)畫系統(tǒng)(動(dòng)畫數(shù)據(jù)會輸出到場景的骨骼節(jié)點(diǎn)樹中)。

mesh 就是網(wǎng)格,由三角形拼成的網(wǎng)格,可以看到這個(gè)網(wǎng)格有7325個(gè)頂點(diǎn),和11186個(gè)三角形,minpos 就是包圍盒最小值, maxpos 就是包圍盒最大的值

關(guān)于網(wǎng)格,這張圖會看的清楚一點(diǎn),由多個(gè)三角形構(gòu)成。

材質(zhì)就像是給剛才的網(wǎng)格穿上的衣服,貼圖就像是衣服上好看的圖案

可以在這個(gè)模型上看到貼圖的部件,是通過紋理映射的方式顯示這張圖片。例如手套映射到貼圖的右下角。

還有一種常見的貼圖叫法線貼圖,簡單來說這張貼圖可以讓模型更有凹凸感

骨架,就像是人體的骨骼一樣。蒙皮,蒙的就是網(wǎng)格相對于骨架的位置

事實(shí)上,在實(shí)現(xiàn)中并沒有骨架,并不是一條一條的線,而是一個(gè)一個(gè)的點(diǎn)。叫做關(guān)節(jié)或者說骨骼點(diǎn)。蒙皮就是根據(jù)根據(jù)這個(gè)點(diǎn),計(jì)算網(wǎng)格點(diǎn)的位置。

選中場景中的3d模型,點(diǎn)擊動(dòng)畫編輯器,進(jìn)入動(dòng)畫編輯模式??梢灶A(yù)覽動(dòng)畫效果。

點(diǎn)擊播放按鈕就可以播放了??梢钥吹接疑辖堑膶傩悦姘宀]有變化,那么這個(gè)動(dòng)畫是移動(dòng)的是什么呢?

前面幾個(gè)點(diǎn)的 postion rotation 并沒有發(fā)生變化

可以看到后面幾個(gè)點(diǎn)位移旋轉(zhuǎn)發(fā)生了變化,這些點(diǎn)就是骨骼(關(guān)節(jié))。蒙皮動(dòng)畫的本質(zhì)是改變骨骼的節(jié)點(diǎn)信息,網(wǎng)格再根據(jù)骨骼點(diǎn)實(shí)時(shí)計(jì)算網(wǎng)格的形狀。

材質(zhì),網(wǎng)格,骨架是由蒙皮網(wǎng)格渲染器(skinmeshrenderer) 組織在一起。

總結(jié)一下,一個(gè)3d資源拖入到場景中的結(jié)構(gòu)是怎么樣的。根節(jié)點(diǎn)有一個(gè)骨骼動(dòng)畫組件。他的兒子中包含了骨骼蒙皮渲染組件(將材質(zhì),網(wǎng)格,骨骼組織在一起)。還有種兒子是骨骼(關(guān)節(jié)),蒙皮動(dòng)畫實(shí)際上是對這些骨骼點(diǎn)進(jìn)去移動(dòng)旋轉(zhuǎn),然后網(wǎng)格再根據(jù)這個(gè)點(diǎn)的信息,再計(jì)算網(wǎng)格的形狀(蒙皮)。

了解了上面的知識,我們現(xiàn)在開始實(shí)戰(zhàn)吧。我們的目標(biāo)是畫出這個(gè)骨骼!

我們該如何繪制骨架呢?只要在這些骨骼點(diǎn)和其父節(jié)點(diǎn)畫一條線就行了。

我們要畫的是這些骨骼,這些骨骼的數(shù)據(jù)應(yīng)該在哪里獲取呢?是的,就在骨架數(shù)據(jù)中獲取,骨架數(shù)據(jù)就是在蒙皮網(wǎng)格渲染器中。

model 就是對應(yīng)這初始節(jié)點(diǎn)。先拿到蒙皮網(wǎng)格渲染器的組件,找到骨架數(shù)據(jù),再找到骨骼點(diǎn),并做上標(biāo)記。最后再遞歸按深度優(yōu)先的順序把所有骨骼點(diǎn)保存起來。這個(gè)bones就是所有的關(guān)節(jié)點(diǎn)了。

那么我們應(yīng)該用什么組件畫骨架呢?這里用了 Cocos Creator 的線段組件。對有爸爸的骨骼們創(chuàng)建線段組件。把每個(gè)骨骼和他爸爸的世界坐標(biāo)告訴線段組件,就能畫出骨架了。

還需要注意的是,要想把這東西畫到最前!需做到 透 深 優(yōu)!透是指透明渲染隊(duì)列。在 Cocos Creator 默認(rèn)的前向渲染管線中,是先渲染不透明隊(duì)列再渲染透明隊(duì)列。選擇透明隊(duì)列就可以再更后的階段繪制。深 指的是 深度讀寫都關(guān)閉,深度寫是影響之后東西的繪畫,深度讀是只被之前繪畫的深度影響,當(dāng)然我們都不想影響就都關(guān)了。優(yōu) 是只 優(yōu)先級,要在最后畫!

最后,介紹一下這個(gè)腳本怎么使用。將這個(gè)腳本拖入到場景中,再掛上場景中的3D模型就可以了。

小結(jié)~ 1.動(dòng)畫驅(qū)動(dòng)骨骼 2.骨骼決定網(wǎng)格 3.畫最前,透深優(yōu)
代碼
import { _decorator, Component, Node, SkinnedMeshRenderer, Line, SkeletalAnimation, Color, gfx, GradientRange, log } from 'cc';
const { ccclass, property } = _decorator;
@ccclass('SkeletonHelper')
export class SkeletonHelper extends Component {
@property(Node)
model: Node = null!
private bones: Node[] = []
private lines: Line[] = []
start() {
log('歡迎關(guān)注微信公眾號【白玉無冰】 https://mp.weixin.qq.com/s/-I6I6nG2Hnk6d1zqR-Gu2g')
const skeletalAnimation = this.model.getComponent(SkeletalAnimation)
skeletalAnimation.useBakedAnimation = false; // maybe todo
const skinMeshRds = this.model.getComponentsInChildren(SkinnedMeshRenderer)
skinMeshRds.forEach(element => {
const skinningRoot = element.skinningRoot
element.skeleton.joints.forEach((v) => {
const node = skinningRoot.getChildByPath(v)
node['isBone'] = true;
})
});
const bones = this.getBoneList(this.model);
this.bones = bones;
for (let i = 0; i < bones.length; i++) {
const bone = bones[i];
if (bone.parent && bone.parent['isBone']) {
const line = this.addComponent(Line);
const state = { priority: 255, depthStencilState: new gfx.DepthStencilState(false, false) }
// @ts-ignore
line._materialInstance.overridePipelineStates(state)
line.worldSpace = true;
line.width.constant = 0.01;
line.color.mode = GradientRange.Mode.TwoColors //there are some bugs in cocos creator // engine\cocos\particle\models\line-model.ts // engine\cocos\particle\animator\gradient.ts
line.color.colorMin = Color.BLUE
line.color.colorMax = Color.GREEN
line.positions = [bone.worldPosition, bone.parent.worldPosition] as never[]
this.lines.push(line)
}
}
}
showSkeleton(show: boolean) {
this.lines.forEach(l => l.enabled = show)
}
private getBoneList(object: Node) {
const boneList: Node[] = [];
if (object['isBone']) {
boneList.push(object);
}
for (let i = 0; i < object.children.length; i++) {
boneList.push.apply(boneList, this.getBoneList(object.children[i]));
}
return boneList;
}
lateUpdate(deltaTime: number) {
let lineIndex = 0;
for (let i = 0; i < this.bones.length; i++) {
const bone = this.bones[i];
if (bone.parent && bone.parent['isBone']) {
const line = this.lines[lineIndex++];
line.positions = [bone.worldPosition, bone.parent.worldPosition] as never[]
}
}
}
}
視頻
“點(diǎn)贊“ ”在看”?鼓勵(lì)一下
▼
