木匣子

Web/Game/Programming/Life etc.

兼容 cocos2d-html5 与 cocos2d-x 的 shader

使用 jsb 将 cocos2d-html5 项目移植到 cocos2d-x 的时候,会出现原来在网页上正常运行的 shader 编译失败、出现位移等情况。于是需要额外的工作来解决一些兼容性问题。

本文的开发环境为: cocos2d-js 3.0 final

编译失败

是由于 cpp 中初始化 shader 的代码与 html5 有些许差异。其中 cpp 中 uniform 预置了 uniform sampler2D CC_Texture0; 而 html5 中没有相应的处理,导致 shader 中的 CC_Texture0 重复出现。应对此问题,可以参考此 commit 对 cocos2d-js 3.0 final 做出修改。如果是开发版,直接更新到最新代码即可。

图像位移

在 cocos2d-x 环境下,原来在 html5 中正常显示的图像被渲染到了不正确的位置。这是由于 cocos2d-x 3.0 新引入的渲染机制,为了优化渲染过程,不得不修改 CCSprite 的渲染方式,将原来的 MVP 变换作了修改,去掉了 ModelView 变换,只保留 Projection 变换。CCSprite 的默认 shader 改为了 GLProgram::SHADER_NAME_POSITION_TEXTURE_COLOR_NO_MVP, 而 *_NO_MVP 的 vertex shader 中使用 gl_Position = CC_PMatrix * a_position; 来计算坐标,而不是通常的 gl_Position = CC_MVPMatrix * a_position; 于是就导致了图像位移。

我猜测 cocos2d-x 这样做是为了在新的渲染过程中更容易地合并相似的渲染指令——少了使每个 CCSprite 个性化的 ModelViewMatrix 就可以把使用相同 Texture2D 的 CCSprites 同时渲染,以减少 OpenGL Call 而达到优化的效果。不过代价是原有的 CCCamera 相关功能被移除,可以从 cocos2d-html5 的 CCNode.getCamera() /* @deprecated since v3.0, no alternative function */ 得到印证。

solution 1

既然如此,要使 shader 能够兼容两个环境,只能做两手准备了。 将原有的 shader.vsh 复制并修改为 shader_no_MVP.vsh 同时共用 shader.fsh 。加载的时候使用 cc.sys.isNative 条件载入正确的 shader 即可。

solution 2

还有一种方法,创建 CCProgram 的时候,以字符串方式读取 shader.vsh 根据运行环境,选择性地 将 CC_PMatrix * CC_MVMatrix 替换为 CC_PMatrix再使用 CCProgram.prototype.initWithString() 对 shader 进行初始化。