Assembler自定义顶点数量

简单概述:
自定义assembler,通过修改传递到着色器的顶点数量,达成类似TiledSprite的平铺效果。
如图:

image.png

Assembler的作用是记录顶点数据,对于普通Sprite组件,一般是如下的顶点位置、顶点纹理坐标、顶点颜色

image.png

在渲染流中,Assembler主要参与了3个环节,分别是更新颜色、更新渲染数据、填充缓冲数据:


image.png

其中updateColor是由Sprite等渲染组件间接调用:


image.png

我们今天关心的就是其中updateRenderData这一环节。
updateRenderData,更新渲染数据,其实就是更新四个顶点的位置、纹理坐标。
最上面的笑脸图中,一个精灵变成对称平铺的2x2个精灵,4个顶点变9个顶点,2个三角形变8个三角形,我们首先需要更新顶点数量和顶点索引数量,以便创建足够大的缓冲区

    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;
    }

然后我们处理顶点数据,首先是顶点索引,默认只有四个顶点的时候,分割成两个三角形,索引分别是{0,1,2},{1,2,3},这一部分在core/render/webgl/render-data.js下


image.png

但是我们现在有9个顶点,4个矩形,8个三角形,所以要重新划分


image.png

如上图,现在我们的三角形,从左到右,从下到上,应该是
{0,1,3}{1,3,4},{1,2,4},{2,4,5},{3,4,6},{4,6,7},{4,5,7},{5,7,8}
初始化创建顶点数据后,重新更新一下索引缓冲

/**更新顶点索引 */
    _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;
            }
        }
    }

然后是更新顶点坐标,在assembler-2d.js中,是先计算出四个顶点的本地坐标,存到_local中,然后与世界矩阵相乘(native下没有这一步,矩阵变换好像是在c++端),得到四个顶点的世界坐标:

updateWorldVerts (comp) {
        let local = this._local;
        let verts = this._renderData.vDatas[0];

        ////省略若干行
            //矩形四个角的本地坐标变换成世界坐标
            // 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;
       

我们要做的是一样的事情,只不过顶点多了一点而已


image.png

仍然是从左到右,从下到上,9个顶点的本地坐标为:
{-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个顶点
然后插值出当前顶点的本地坐标,然后模拟一下矩阵乘法,得出世界坐标。

计算纹理坐标时,遍历顺序要保持一致。

纹理有个要注意的点,因为有些顶点是两个或者三个矩形公用的,所以顶点纹理坐标要注意不能紊乱,对于同一个顶点,不能在一个矩形里预期采样纹理右上角,却在隔壁矩形预期采样纹理左上角。
应该如下如:


image.png

应该是左右对称、上下对称的。
可以找到规律,最下面第一行,从左往右依次是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;
            }
        }
    }

按照同样的顺序遍历所有顶点,然后找出当前顶点使用原纹理哪个角的纹理,然后依次赋值u、v

以上基本就搞定了。
设置不同的行列:


image.png

实际价值比较有限,主要是学习用,加深我对顶点的理解。后面有空考虑做一个拼图小游戏,通过改变顶点数量可以实现图片分割成不同的多边形碎片。

下面是完整类:

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实现的

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,185评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,445评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,684评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,564评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,681评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,874评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,025评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,761评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,217评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,545评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,694评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,351评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,988评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,778评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,007评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,427评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,580评论 2 349

推荐阅读更多精彩内容