簡單概述:
自定義assembler效床,通過修改傳遞到著色器的頂點數(shù)量剩檀,達(dá)成類似TiledSprite的平鋪效果沪猴。
如圖:
Assembler的作用是記錄頂點數(shù)據(jù)运嗜,對于普通Sprite組件担租,一般是如下的頂點位置奋救、頂點紋理坐標(biāo)反惕、頂點顏色
在渲染流中背亥,Assembler主要參與了3個環(huán)節(jié),分別是更新顏色程癌、更新渲染數(shù)據(jù)嵌莉、填充緩沖數(shù)據(jù):
其中updateColor是由Sprite等渲染組件間接調(diào)用:
我們今天關(guān)心的就是其中updateRenderData這一環(huán)節(jié)锐峭。
updateRenderData沿癞,更新渲染數(shù)據(jù)椎扬,其實就是更新四個頂點的位置蚕涤、紋理坐標(biāo)铣猩。
最上面的笑臉圖中达皿,一個精靈變成對稱平鋪的2x2個精靈,4個頂點變9個頂點龄寞,2個三角形變8個三角形物邑,我們首先需要更新頂點數(shù)量和頂點索引數(shù)量,以便創(chuàng)建足夠大的緩沖區(qū)
row:number = 2;
col = 2;
/**把一張圖變成平鋪的row*col張 */
setGird(row:number,col:number){
this.row = row;
this.col = col;
this.verticesCount = (row+1)*(col+1);
this.indicesCount = 6*row*col;
}
然后我們處理頂點數(shù)據(jù)拂封,首先是頂點索引冒签,默認(rèn)只有四個頂點的時候萧恕,分割成兩個三角形,索引分別是{0,1,2}票唆,{1,2,3}走趋,這一部分在core/render/webgl/render-data.js下
但是我們現(xiàn)在有9個頂點簿煌,4個矩形姨伟,8個三角形,所以要重新劃分
如上圖瞒渠,現(xiàn)在我們的三角形伍玖,從左到右淮摔,從下到上和橙,應(yīng)該是
{0,1,3}{1,3,4},{1,2,4},{2,4,5},{3,4,6},{4,6,7},{4,5,7},{5,7,8}
初始化創(chuàng)建頂點數(shù)據(jù)后造垛,重新更新一下索引緩沖
/**更新頂點索引 */
_updateIndices () {
let indices = this._renderData.iDatas[0];
let indexOffset = 0;
let _col = this.col;
let _row = this.row;
for (let j = 0; j < _row; ++j) {
for (let i = 0; i < _col; ++i) {
let start = j * (_col+1) + i;
indices[indexOffset++] = start;
indices[indexOffset++] = start + 1;
indices[indexOffset++] = start + _col + 1;
indices[indexOffset++] = start + 1;
indices[indexOffset++] = start + _col + 1;
indices[indexOffset++] = start + 1 + _col + 1;
}
}
}
然后是更新頂點坐標(biāo)五辽,在assembler-2d.js中杆逗,是先計算出四個頂點的本地坐標(biāo),存到_local中蠕蚜,然后與世界矩陣相乘(native下沒有這一步悔橄,矩陣變換好像是在c++端),得到四個頂點的世界坐標(biāo):
updateWorldVerts (comp) {
let local = this._local;
let verts = this._renderData.vDatas[0];
////省略若干行
//矩形四個角的本地坐標(biāo)變換成世界坐標(biāo)
// left bottom
verts[vertexOffset] = al + cb + tx;
verts[vertexOffset + 1] = bl + db + ty;
vertexOffset += floatsPerVert;
// right bottom
verts[vertexOffset] = ar + cb + tx;
verts[vertexOffset + 1] = br + db + ty;
vertexOffset += floatsPerVert;
// left top
verts[vertexOffset] = al + ct + tx;
verts[vertexOffset + 1] = bl + dt + ty;
vertexOffset += floatsPerVert;
// right top
verts[vertexOffset] = ar + ct + tx;
verts[vertexOffset + 1] = br + dt + ty;
我們要做的是一樣的事情潮酒,只不過頂點多了一點而已
仍然是從左到右急黎,從下到上侧到,9個頂點的本地坐標(biāo)為:
{-50,-50},{0,-50},{50,-50},{-50,0},{0,0},{50,0},{-50,50},{0,50},{50,50},
let vertexOffset = 0;
let _col = this.col;
let _row = this.row;
for(let j=0;j<=_row;j++){
let _y = vb + j/_row*(vt-vb);
for(let i=0;i<=_col;i++){
let _x = vl + i/_col*(vr-vl);
verts[vertexOffset] = a*_x + c*_y + tx;
verts[vertexOffset + 1] = b*_x + d*_y + ty;
vertexOffset += floatsPerVert;
}
}
以上代碼的意義就是按照從左到右荣回,從下到上的順序遍歷9個頂點
然后插值出當(dāng)前頂點的本地坐標(biāo)心软,然后模擬一下矩陣乘法,得出世界坐標(biāo)删铃。
計算紋理坐標(biāo)時猎唁,遍歷順序要保持一致诫隅。
紋理有個要注意的點帐偎,因為有些頂點是兩個或者三個矩形公用的,所以頂點紋理坐標(biāo)要注意不能紊亂豁生,對于同一個頂點甸箱,不能在一個矩形里預(yù)期采樣紋理右上角芍殖,卻在隔壁矩形預(yù)期采樣紋理左上角仪际。
應(yīng)該如下如:
應(yīng)該是左右對稱、上下對稱的变秦。
可以找到規(guī)律框舔,最下面第一行,從左往右依次是0,1,0,1,0……樱溉,第二行從左往右依次是2,3,2,3……
updateUVs (sprite) {
let uv = sprite._spriteFrame.uv;
let uvOffset = this.uvOffset;
let floatsPerVert = this.floatsPerVert;
let verts = this._renderData.vDatas[0];
let _col = this.col;
let _row = this.row;
let index = uvOffset;
for(let j=0;j<=_row;j++){
let arr = j%2==0?[0,1]:[2,3];
for(let i=0;i<=_col;i++){
let k = arr[i%2]
let srcOffset = k * 2;
verts[index] = uv[srcOffset];
verts[index + 1] = uv[srcOffset + 1];
index+=floatsPerVert;
}
}
}
按照同樣的順序遍歷所有頂點福贞,然后找出當(dāng)前頂點使用原紋理哪個角的紋理停士,然后依次賦值u恋技、v
以上基本就搞定了。
設(shè)置不同的行列:
實際價值比較有限骄崩,主要是學(xué)習(xí)用要拂,加深我對頂點的理解宇弛。后面有空考慮做一個拼圖小游戲源请,通過改變頂點數(shù)量可以實現(xiàn)圖片分割成不同的多邊形碎片谁尸。
下面是完整類:
import HYZSimpleSpriteAssembler from "./HYZSimpleSpriteAssembler";
export default class HYZAssemblerGird extends HYZSimpleSpriteAssembler {
verticesCount = 4;
indicesCount = 6;
init(comp) {
this.setGird(5,5)
super.init(comp)
this._updateIndices();
}
row:number = 2;
col = 2;
/**把一張圖變成平鋪的row*col張 */
setGird(row:number,col:number){
this.row = row;
this.col = col;
this.verticesCount = (row+1)*(col+1);
this.indicesCount = 6*row*col;
}
/**更新頂點索引 */
_updateIndices () {
let indices = this._renderData.iDatas[0];
let indexOffset = 0;
let _col = this.col;
let _row = this.row;
for (let j = 0; j < _row; ++j) {
for (let i = 0; i < _col; ++i) {
let start = j * (_col+1) + i;
indices[indexOffset++] = start;
indices[indexOffset++] = start + 1;
indices[indexOffset++] = start + _col + 1;
indices[indexOffset++] = start + 1;
indices[indexOffset++] = start + _col + 1;
indices[indexOffset++] = start + 1 + _col + 1;
}
}
}
updateWorldVertsWebGL(comp) {
let local = this._local;
let verts = this._renderData.vDatas[0];
let matrix = comp.node._worldMatrix;
let matrixm = matrix.m,
a = matrixm[0], b = matrixm[1], c = matrixm[4], d = matrixm[5],
tx = matrixm[12], ty = matrixm[13];
let vl = local[0], vr = local[2],
vb = local[1], vt = local[3];
let floatsPerVert = this.floatsPerVert;
let vertexOffset = 0;
let _col = this.col;
let _row = this.row;
for(let j=0;j<=_row;j++){
let _y = vb + j/_row*(vt-vb);
for(let i=0;i<=_col;i++){
let _x = vl + i/_col*(vr-vl);
verts[vertexOffset] = a*_x + c*_y + tx;
verts[vertexOffset + 1] = b*_x + d*_y + ty;
vertexOffset += floatsPerVert;
}
}
}
updateWorldVertsNative(comp) {
let local = this._local;
let verts = this._renderData.vDatas[0];
let floatsPerVert = this.floatsPerVert;
let vl = local[0],
vr = local[2],
vb = local[1],
vt = local[3];
let index = 0;
let _col = this.col;
let _row = this.row;
for(let j=0;j<=_row;j++){
let _y = vb + j/_row*(vt-vb);
for(let i=0;i<=_col;i++){
let _x = vl + i/_col*(vr-vl);
verts[index] = _x;
verts[index+1] = _y;
index += floatsPerVert;
}
}
}
updateUVs (sprite) {
let uv = sprite._spriteFrame.uv;
let uvOffset = this.uvOffset;
let floatsPerVert = this.floatsPerVert;
let verts = this._renderData.vDatas[0];
let _col = this.col;
let _row = this.row;
let index = uvOffset;
for(let j=0;j<=_row;j++){
let arr = j%2==0?[0,1]:[2,3];
for(let i=0;i<=_col;i++){
let k = arr[i%2]
let srcOffset = k * 2;
verts[index] = uv[srcOffset];
verts[index + 1] = uv[srcOffset + 1];
index+=floatsPerVert;
}
}
}
}
其中HYZSimpleSpriteAssembler 是完全模仿core\renderer\webgl\assemblers\sprite\2d\simple.js實現(xiàn)的