关于spine在cocos2dx使用硬件压缩纹理混合问题总结

问题分析

  • axmol增加了ASTC和ETC2纹理支持,默认情况下Image加载这些纹理视为没有alpha预乘,也就是Image的_hasPremultipliedAlpha保持为初始值false, 引擎核心代码中的类基本都会根据该变量正确设置混合模式即{ backend::BlendFactor::SRC_ALPHA, backend::BlendFactor::ONE_MINUS_SRC_ALPHA},所以渲染没什么问题
  • 然而spine运行库的实现中,强制认为纹理已经alpha预乘,因此在使用ETC2或ASTC时,渲染会异常
void SkeletonRenderer::initialize () {
	_clipper = new (__FILE__, __LINE__) SkeletonClipping();

	_blendFunc = BlendFunc::ALPHA_PREMULTIPLIED;
	setOpacityModifyRGB(true);

	setTwoColorTint(false);

	_skeleton->setToSetupPose();
	_skeleton->updateWorldTransform();
}

解决方案

  1. 在初始化的时候判断
   void SkeletonRenderer::setupGLProgramState (bool twoColorTintEnabled) {
   		Texture2D *texture = nullptr;
   		for (int i = 0, n = _skeleton->getSlots().size(); i < n; i++) {
   			Slot* slot = _skeleton->getDrawOrder()[i];
   			Attachment* const attachment = slot->getAttachment();
   			if (!attachment) continue;
   			if (attachment->getRTTI().isExactly(RegionAttachment::rtti)) {
   				RegionAttachment* regionAttachment = static_cast<RegionAttachment*>(attachment);
   				texture = static_cast<AttachmentVertices*>(regionAttachment->getRendererObject())->_texture;
   			} else if (attachment->getRTTI().isExactly(MeshAttachment::rtti)) {
   				MeshAttachment* meshAttachment = static_cast<MeshAttachment*>(attachment);
   				texture = static_cast<AttachmentVertices*>(meshAttachment->getRendererObject())->_texture;
   			} else {
   				continue;
   			}
   	
   			if (texture != nullptr) {
   				break;
   			}
   		}
   	
   		if (texture != nullptr && texture->hasPremultipliedAlpha()) {
   			_blendFunc = BlendFunc::ALPHA_PREMULTIPLIED;
   			setOpacityModifyRGB(true);
   		}
   		else {
   			_blendFunc = BlendFunc::ALPHA_NON_PREMULTIPLIED;
   			setOpacityModifyRGB(false);
   		}
   }

这种方案,可以解决spine多数渲染问题,然而,当包含 BlendMode_Screen 模式的部件时,渲染依然不正常(和png对比),查看代码如下:

BlendFunc makeBlendFunc(BlendMode blendMode, bool premultipliedAlpha) {
		BlendFunc blendFunc;
		switch (blendMode) {
			case BlendMode_Additive:
				blendFunc.src = premultipliedAlpha ? backend::BlendFactor::ONE : backend::BlendFactor::SRC_ALPHA;
				blendFunc.dst = backend::BlendFactor::ONE;
				break;
			case BlendMode_Multiply:
				blendFunc.src = backend::BlendFactor::DST_COLOR;
				blendFunc.dst = backend::BlendFactor::ONE_MINUS_SRC_ALPHA;
				break;
			case BlendMode_Screen:
				blendFunc.src = backend::BlendFactor::ONE;
				blendFunc.dst = backend::BlendFactor::ONE_MINUS_SRC_COLOR;
				break;
			default:
				blendFunc.src = premultipliedAlpha ? backend::BlendFactor::ONE : backend::BlendFactor::SRC_ALPHA;
				blendFunc.dst = backend::BlendFactor::ONE_MINUS_SRC_ALPHA;
		}
		return blendFunc;
	}

怀疑是BlendMode_Screen没有正确设置混合方式造成,待考究

  1. 修改内置shader, 在postionTextureColor.frag中进行alpha预乘
    void main()
    {
        vec4 texColor = texture2D(u_texture, v_texCoord);
        texColor.rgb *= texColor.a; // Premultiply with Alpha channel
        gl_FragColor = v_fragmentColor * texColor;
    }
    
    同时修改Image::PNG_PREMULTIPLIED_ALPHA_ENABLED为false,并在ASTC和ETC2纹理加载成功后,将Image的_hasPremultipliedAlpha设置为true,这样做可以达到使用png,astc,etc2的spine渲染效果一致的目的,但是修改需要覆盖到引擎的其他内置shader

后记:

  • 早期etc1 alpha为什么没问题
    因为etc1的shader etc1.frag中做了预乘:
      const char* etc1_frag = R"(
     	#ifdef GL_ES
     	    precision mediump float;
     	#endif
     	
     	varying vec4 v_fragmentColor;
     	varying vec2 v_texCoord;
     	
     	uniform sampler2D u_texture;
     	uniform sampler2D u_texture1;
     	
     	void main() {
     	    vec4 texColor = vec4(texture2D(u_texture, v_texCoord).rgb, texture2D(u_texture1, v_texCoord).r);
     	
     	    texColor.rgb *= texColor.a; // Premultiply with Alpha channel
     	
     	    gl_FragColor = v_fragmentColor * texColor;
     	}
     )";
    
  • 工具png转etc2或astc时便进行预乘
    目前笔者只发现TexturePacker支持勾选premultiplyAlpha, 但没试过,后续待验证
    更新: 测试发现TexturePacker最新版本,也就是5.4支持ETC2 RGBA预乘alpha导出,且spine不必修改任何代码渲染效果和png一样
    tips: 暂未发现TexturePacker支持astc
    更新:2021.6.15,ARM官方astcenc-2.3+开始支持生成预乘alpha纹理, 转换命令添加参数: -pp-premultiply 即可。
cocos2dx spine是一个游戏开发框架,可用于创建高度优化的骨骼动画。在使用cocos2dx spine时,我们可以采取一些运行优化措施,以提高游戏的性能和响应速度。 首先,可以对骨骼动画资源进行优化。对于大型和复杂的骨骼动画,在制作和导出时应尽量减少骨骼的数量和动画帧的数量。这样可以降低内存使用量,并减少渲染的复杂度。 其次,可以使用spine的批处理功能来减少渲染调用的次数。在使用多个骨骼动画时,将它们合并到一个批次中,可以减少渲染开销,提高游戏的帧率。 另外,可以对骨骼动画的骨骼绑定进行优化。在设计骨骼动画时,应尽量避免骨骼互相影响,减少骨骼之间的层级关系,这样可以减少运算开销,提高游戏的性能。 此外,还可以合理利用spine提供的缓存机制。在游戏运行过程中,对于一些经常被访问和使用的骨骼动画,可以将其缓存起来,减少每次访问时的加载和解析开销。 最后,可以通过减少不必要的资源加载和卸载、合理使用内存管理等方式来提升游戏的运行效率。在使用cocos2dx spine时,我们还可以进行一些平台特定的优化,例如使用OpenGL ES特性进行渲染优化等。 综上所述,cocos2dx spine运行优化可以从优化骨骼动画资源、使用批处理、优化骨骼绑定、合理利用缓存机制、减少资源加载和卸载等多个方面入手,以提高游戏的性能和响应速度。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值