自定义shader顶点信息在模拟器和手机端显示的问题

  • Creator 版本:2.4.7

  • 目标平台:web 以及编辑器中显示正常,模拟器和安卓端不显示,苹果没试

  • 首个报错: 无报错

  • 源代码下载

需求是纹理中有些像素是有透明值的,渲染的时候我需要对这些有透明值的像素进行处理,如果自定义mapSprite.isMask 属性为true,我就将没有任何透明度的像素全部丢弃,将有透明度的像素渲染出来,如果属性为false,就将所有像素的透明度都改成1并全部显示出来。

有没有大佬指点指点?同时会有七八张图应用该效果,如果用材质实现的话不会合批。。。。

自定义的Assembler代码如下:

    let gfx = cc.gfx;

    let vfmtMap = new gfx.VertexFormat([
        { name: gfx.ATTR_POSITION, type: gfx.ATTR_TYPE_FLOAT32, num: 3 },
        { name: gfx.ATTR_UV0, type: gfx.ATTR_TYPE_FLOAT32, num: 2 } 
    ]);

    export default class MapAssemble extends cc.Assembler {
        constructor() {
            super();

            this._renderData = new cc.RenderData();
            this._renderData.init(this);
            
            this.initData();
            this.initLocal();
        }

        get uvOffset() {
            return 3;
        }

        get floatsPerVert() {
            return 5;
        }

        get verticesCount() {
            return 4;
        }

        get indicesCount() {
            return 6;
        }

        get verticesFloats() {
            return this.verticesCount * this.floatsPerVert;
        }

        initLocal() {
            this._local = [];

            this._local.length = 4;
        }

        initData() {
            let data = this._renderData;
            
            data.createFlexData(0, this.verticesCount, this.indicesCount, this.getVfmt());

            // createFlexData不会填充顶点索引信息,手动补充一下
            let indices = data.iDatas[0];

            let count = indices.length / 6;

            for (let i = 0, idx = 0; i < count; i++) {
                let vertextID = i * 4;

                indices[idx++] = vertextID;
                indices[idx++] = vertextID+1;
                indices[idx++] = vertextID+2;
                indices[idx++] = vertextID+1;
                indices[idx++] = vertextID+3;
                indices[idx++] = vertextID+2;
            }
        }

        updateColor(sprite, color) {}

        updateWorldVerts(sprite) {
            if (CC_NATIVERENDERER) {
                this.updateWorldVertsNative(sprite);
            } else {
                this.updateWorldVertsWebGL(sprite);
            }
        }

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

            let matrix = sprite.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 justTranslate = a === 1 && b === 0 && c === 0 && d === 1;
            let zValue = sprite.isMask ? 1 : 0;

            if (justTranslate) {
                // left bottom
                verts[vertexOffset] = vl + tx;
                verts[vertexOffset + 1] = vb + ty;
                verts[vertexOffset + 2] = zValue;
                vertexOffset += floatsPerVert;
                // right bottom
                verts[vertexOffset] = vr + tx;
                verts[vertexOffset + 1] = vb + ty;
                verts[vertexOffset + 2] = zValue;
                vertexOffset += floatsPerVert;
                // left top
                verts[vertexOffset] = vl + tx;
                verts[vertexOffset + 1] = vt + ty;
                verts[vertexOffset + 2] = zValue;
                vertexOffset += floatsPerVert;
                // right top
                verts[vertexOffset] = vr + tx;
                verts[vertexOffset + 1] = vt + ty;
                verts[vertexOffset + 2] = zValue;
            } else {
                let al = a * vl, ar = a * vr,
                bl = b * vl, br = b * vr,
                cb = c * vb, ct = c * vt,
                db = d * vb, dt = d * vt;

                // left bottom
                verts[vertexOffset] = al + cb + tx;
                verts[vertexOffset + 1] = bl + db + ty;
                verts[vertexOffset + 2] = zValue;
                vertexOffset += floatsPerVert;
                // right bottom
                verts[vertexOffset] = ar + cb + tx;
                verts[vertexOffset + 1] = br + db + ty;
                verts[vertexOffset + 2] = zValue;
                vertexOffset += floatsPerVert;
                // left top
                verts[vertexOffset] = al + ct + tx;
                verts[vertexOffset + 1] = bl + dt + ty;
                verts[vertexOffset + 2] = zValue;
                vertexOffset += floatsPerVert;
                // right top
                verts[vertexOffset] = ar + ct + tx;
                verts[vertexOffset + 1] = br + dt + ty;
                verts[vertexOffset + 2] = zValue;
            }
        }

        // native场景下使用的updateWorldVerts
        // copy from \jsb-adapter-master\engine\assemblers\assembler-2d.js
        updateWorldVertsNative(sprite) {
            let local = this._local;
            let verts = this._renderData.vDatas[0];
            let floatsPerVert = this.floatsPerVert;
            let zValue = sprite.isMask ? 1 : 0;
        
            let vl = local[0],
                vr = local[2],
                vb = local[1],
                vt = local[3];
        
            let index = 0;
            // left bottom
            verts[index] = vl;
            verts[index+1] = vb;
            verts[index + 2] = zValue;
            index += floatsPerVert;
            // right bottom
            verts[index] = vr;
            verts[index+1] = vb;
            verts[index + 2] = zValue;
            index += floatsPerVert;
            // left top
            verts[index] = vl;
            verts[index+1] = vt;
            verts[index + 2] = zValue;
            index += floatsPerVert;
            // right top
            verts[index] = vr;
            verts[index+1] = vt;
            verts[index + 2] = zValue;
        }

        fillBuffers(sprite, renderer) {
            if (renderer.worldMatDirty) {
                this.updateWorldVerts(sprite);
            }

            let renderData = this._renderData;
            let vData = renderData.vDatas[0];
            let iData = renderData.iDatas[0];

            let buffer = this.getBuffer(renderer);
            let offsetInfo = buffer.request(this.verticesCount, this.indicesCount);

            // buffer data may be realloc, need get reference after request.

            // fill vertices
            let vertexOffset = offsetInfo.byteOffset >> 2,
                vbuf = buffer._vData;

            if (vData.length + vertexOffset > vbuf.length) {
                vbuf.set(vData.subarray(0, vbuf.length - vertexOffset), vertexOffset);
            } else {
                vbuf.set(vData, vertexOffset);
            }

            // fill indices
            let ibuf = buffer._iData,
                indiceOffset = offsetInfo.indiceOffset,
                vertexId = offsetInfo.vertexOffset;
            for (let i = 0, l = iData.length; i < l; i++) {
                ibuf[indiceOffset++] = vertexId + iData[i];
            }
        }

        updateRenderData(sprite) {
            if (sprite._vertsDirty) {
                this.updateUVs(sprite);
                this.updateVerts(sprite);

                sprite._vertsDirty = false;
            }
        }

        updateUVs(sprite) {
            let uv = sprite._spriteFrame.uv;
            let uvOffset = this.uvOffset;
            let floatsPerVert = this.floatsPerVert;
            let verts = this._renderData.vDatas[0];
            for (let i = 0; i < 4; i++) {
                let srcOffset = i * 2;
                let dstOffset = floatsPerVert * i + uvOffset;
                verts[dstOffset] = uv[srcOffset];
                verts[dstOffset + 1] = uv[srcOffset + 1];
            }
        }

        updateVerts(sprite) {
            let node = sprite.node,
                cw = node.width, ch = node.height,
                appx = node.anchorX * cw, appy = node.anchorY * ch,
                l, b, r, t;

            if (sprite.trim) {
                l = -appx;
                b = -appy;
                r = cw - appx;
                t = ch - appy;
            }

            let local = this._local;

            local[0] = l;
            local[1] = b;
            local[2] = r;
            local[3] = t;

            this.updateWorldVerts(sprite);
        }

        getVfmt() {
            return vfmtMap;
        }

        getBuffer() {
            //@ts-ignore
            return cc.renderer._handle.getBuffer("mesh", this.getVfmt());
        }
    }

自定义的 Sprite 代码如下:

    import MapAssemble from "./mapAssemble";

    cc.Class({
        extends: cc.Sprite,

        properties: {
            isMask: {
                type: cc.Boolean,
                default: false,
                notify(){
                    this.flushProperties();
                }
            }
        },

        // LIFE-CYCLE CALLBACKS:

        // onLoad () {},

        flushProperties() {
            let assembler = this._assembler;

            if (!assembler)
                return;

            this.setVertsDirty();
        },

        // // 使用cc.Sprite默认逻辑
        _resetAssembler() {
            this.setVertsDirty();

            this._assembler = new MapAssemble();

            this.flushProperties();

            //@ts-ignore
            this._updateColor();        // may be no need
        }
        // update (dt) {},
    });

自定义 Shader 如下:

  CCEffect %{
  techniques:
  - passes:
    - vert: vs
      frag: fs
      blendState:
        targets:
        - blend: true
      rasterizerState:
        cullMode: none
      properties:
        texture: { value: white }
        alphaThreshold: { value: 0.5 }
}%


CCProgram vs %{
  precision highp float;

  #include <cc-global>
  #include <cc-local>

  in vec3 a_position;
  out float v_isMask;

  #if USE_TEXTURE
  in vec2 a_uv0;
  out vec2 v_uv0;
  #endif

  void main () {
    vec4 pos = vec4(a_position, 1);

    #if CC_USE_MODEL
    pos = cc_matViewProj * cc_matWorld * pos;
    #else
    pos = cc_matViewProj * pos;
    #endif

    #if USE_TEXTURE
    v_uv0 = a_uv0;
    #endif

    v_isMask = a_position.z;

    gl_Position = pos;
  }
}%


CCProgram fs %{
  precision highp float;

  #include <alpha-test>
  #include <texture>

  in float v_isMask;

  #if USE_TEXTURE
  in vec2 v_uv0;
  uniform sampler2D texture;
  #endif

  void main () {
    vec4 o = vec4(1, 1, 1, 1);

    #if USE_TEXTURE
      CCTexture(texture, v_uv0, o);
    #endif

    if (v_isMask > 0.5) {
      if (o.a > 0.98) {
        discard; 
      } else {
        o.a = 0.5;
      }
    } else {
      o.a = 1.0;
    }

    #if USE_BGRA
      gl_FragColor = o.bgra;
    #else
      gl_FragColor = o.rgba;
    #endif
  }
}%

你还用的js开发?是不是引擎版本太旧了?可能因为这个原因,cocos原生层,对2d项目,可能不支持position,z的设置?
我只是猜测,不过只有原生有问题的话,多数是原生层的代码有问题。

好像是我舍近求远了,反正我没用到 color 属性,直接用这个 shader 取 color 值就好了,到时候更改 node.color 值就达到这样的效果了。。

还在用js主要是不会ts啊。。