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