cocos2dx 3.0 骨骼动画添加shader

cocos2dx 3.0对shader做了一些优化,渲染树也做了一点更改,本来之前刚刚在2.x上摸到了点门路,到了3.0又不能用了。
最近想在一些角色(flash导出的骨骼动画)上添加一些简单的shader效果,本来在2.x上是比较简单的事情,和set到精灵的方法一样。结果到3.0发现不行了。网上也搜了好多,基本都是对精灵的解决方案,没有发现对骨骼动画的解决方法,于是研究了一下,虽然效果出来,但是总觉得不对劲。而且现在对于需要动态改变shader参数,也就是动态改变shader效果并应用在骨骼动画上还没有解决,借此抛砖引大牛,希望帮忙解决一下。
首先上代码

//
//---------shaderArmature.h
///
#include "cocos2d.h"
#include "extensions/cocos-ext.h"
#include "cocostudio/CocoStudio.h"
USING_NS_CC_EXT;
USING_NS_CC;
using namespace cocostudio;
class ShaderArmature : public Armature
{
public:
    ShaderArmature();
    virtual ~ShaderArmature();
    
    bool init(const std::string& name);
    
    static ShaderArmature *create(const std::string& name) ;
    
    virtual void initShader(bool shaderState);
//    void setBackgroundNotification();
    
    virtual void draw(Renderer *renderer, const kmMat4 &transform, bool transformUpdated) override;
//    void listenBackToForeground(Ref *obj);
    void setIceState();
    void setUsuaState();
    
    void setblurState();
    void setbanishState();
    void setfrozenState();
    void setgrayState();
    void setinvisState();
    void setmirrorState();
    void setpoisonState();
    void setstoneState();
protected:
    std::string _fragSourceFile;
    std::string _vertSourceFile;
    GLchar * fragSource;
    GLchar * vertSource;
    
};

//
//------shaderArmature.cpp
//

#include "shaderArmature.h"

ShaderArmature::ShaderArmature(){
   

}
ShaderArmature::~ShaderArmature(){
    
}
bool ShaderArmature::init(const std::string& name)
{
    return Armature::init(name);
}
ShaderArmature *ShaderArmature::create(const std::string& name)
{
    ShaderArmature *armature = new ShaderArmature;
    if (armature && armature->init(name))
    {
        armature->autorelease();
        return armature;
}else{
    CC_SAFE_DELETE(armature);
    return nullptr;
 }
}

void ShaderArmature::initShader(bool shaderState){
    
    for (auto& object : _children)
    {
        if (Bone *bone = dynamic_cast(object))
        {
            Node *node = bone->getDisplayRenderNode();
            
            if (nullptr == node)
                continue;
            
            
            Skin *skin = static_cast(node);
            ///-----addCustome shader
                if(shaderState){
            
            auto program = new GLProgram();
            program->initWithByteArrays(vertSource, fragSource);
                
            skin->setShaderProgram(program);
                    
            program->autorelease();
            
            CHECK_GL_ERROR_DEBUG();
            
            program->bindAttribLocation(GLProgram::ATTRIBUTE_NAME_POSITION, GLProgram::VERTEX_ATTRIB_POSITION);
            program->bindAttribLocation(GLProgram::ATTRIBUTE_NAME_COLOR, GLProgram::VERTEX_ATTRIB_COLOR);
            program->bindAttribLocation(GLProgram::ATTRIBUTE_NAME_TEX_COORD, GLProgram::VERTEX_ATTRIB_TEX_COORDS);
            
            CHECK_GL_ERROR_DEBUG();
            
            program->link();
            
            CHECK_GL_ERROR_DEBUG();
            
            program->updateUniforms();

            CHECK_GL_ERROR_DEBUG();

            }else{//addNormal shader
                skin->setShaderProgram(ShaderCache::getInstance()->getProgram(GLProgram::SHADER_NAME_POSITION_TEXTURE_COLOR_NO_MVP));
            }
        }
        
    }
}
void ShaderArmature::draw(Renderer *renderer, const kmMat4 &transform, bool transformUpdated)
{

       Armature::draw(renderer, transform, transformUpdated);
}

void ShaderArmature::setIceState(){
    _fragSourceFile ="IceShader.fsh";
    _vertSourceFile ="IceShader.vsh";
    fragSource = (GLchar*) String::createWithContentsOfFile(
                                                            FileUtils::getInstance()->fullPathForFilename(_fragSourceFile).c_str())->getCString();
    
    vertSource = (GLchar*) String::createWithContentsOfFile(
                                                            FileUtils::getInstance()->fullPathForFilename(_vertSourceFile).c_str())->getCString();
    initShader(true);
}

void ShaderArmature::setUsuaState(){
    initShader(false);
}

void ShaderArmature::setblurState(){
    _fragSourceFile ="Blur.fsh";
    _vertSourceFile ="Blur.vsh";
    fragSource = (GLchar*) String::createWithContentsOfFile(
                                                            FileUtils::getInstance()->fullPathForFilename(_fragSourceFile).c_str())->getCString();
    
    vertSource = (GLchar*) String::createWithContentsOfFile(
                                                            FileUtils::getInstance()->fullPathForFilename(_vertSourceFile).c_str())->getCString();
    initShader(true);
}

void ShaderArmature::setbanishState(){
    _fragSourceFile ="BanishShader.fsh";
    _vertSourceFile ="BanishShader.vsh";
    fragSource = (GLchar*) String::createWithContentsOfFile(
                                                            FileUtils::getInstance()->fullPathForFilename(_fragSourceFile).c_str())->getCString();
    
    vertSource = (GLchar*) String::createWithContentsOfFile(
                                                            FileUtils::getInstance()->fullPathForFilename(_vertSourceFile).c_str())->getCString();
    initShader(true);
}

void ShaderArmature::setfrozenState(){
    _fragSourceFile ="FrozenShader.fsh";
    _vertSourceFile ="FrozenShader.vsh";
    fragSource = (GLchar*) String::createWithContentsOfFile(
                                                            FileUtils::getInstance()->fullPathForFilename(_fragSourceFile).c_str())->getCString();
    
    vertSource = (GLchar*) String::createWithContentsOfFile(
                                                            FileUtils::getInstance()->fullPathForFilename(_vertSourceFile).c_str())->getCString();
    initShader(true);
}
void ShaderArmature::setgrayState(){
    _fragSourceFile ="GrayScalingShader.fsh";
    _vertSourceFile ="GrayScalingShader.vsh";
    fragSource = (GLchar*) String::createWithContentsOfFile(
                                                            FileUtils::getInstance()->fullPathForFilename(_fragSourceFile).c_str())->getCString();
    
    vertSource = (GLchar*) String::createWithContentsOfFile(
                                                            FileUtils::getInstance()->fullPathForFilename(_vertSourceFile).c_str())->getCString();
    initShader(true);
}
void ShaderArmature::setinvisState(){
    _fragSourceFile ="InvisibleShader.fsh";
    _vertSourceFile ="InvisibleShader.vsh";
    fragSource = (GLchar*) String::createWithContentsOfFile(
                                                            FileUtils::getInstance()->fullPathForFilename(_fragSourceFile).c_str())->getCString();
    
    vertSource = (GLchar*) String::createWithContentsOfFile(
                                                            FileUtils::getInstance()->fullPathForFilename(_vertSourceFile).c_str())->getCString();
    initShader(true);
}
void ShaderArmature::setmirrorState(){
    _fragSourceFile ="MirrorShader.fsh";
    _vertSourceFile ="MirrorShader.vsh";
    fragSource = (GLchar*) String::createWithContentsOfFile(
                                                            FileUtils::getInstance()->fullPathForFilename(_fragSourceFile).c_str())->getCString();
    
    vertSource = (GLchar*) String::createWithContentsOfFile(
                                                            FileUtils::getInstance()->fullPathForFilename(_vertSourceFile).c_str())->getCString();
    initShader(true);
}
void ShaderArmature::setpoisonState(){
    _fragSourceFile ="PoisonShader.fsh";
    _vertSourceFile ="PoisonShader.vsh";
    fragSource = (GLchar*) String::createWithContentsOfFile(
                                                            FileUtils::getInstance()->fullPathForFilename(_fragSourceFile).c_str())->getCString();
    
    vertSource = (GLchar*) String::createWithContentsOfFile(
                                                            FileUtils::getInstance()->fullPathForFilename(_vertSourceFile).c_str())->getCString();
    initShader(true);
}

void ShaderArmature::setstoneState(){
    _fragSourceFile ="StoneShader.fsh";
    _vertSourceFile ="StoneShader.vsh";
    fragSource = (GLchar*) String::createWithContentsOfFile(
                                                            FileUtils::getInstance()->fullPathForFilename(_fragSourceFile).c_str())->getCString();
    
    vertSource = (GLchar*) String::createWithContentsOfFile(
                                                            FileUtils::getInstance()->fullPathForFilename(_vertSourceFile).c_str())->getCString();
    initShader(true);
}


```


这个类写好之后在别的场景create出来即可,也可以自己写一个开关通过setXXstate来切换或者关闭shader。


其中initshader里面的写法是参考了创建armature的时候的写法,遍历里面的skin,每个skin都set一个shader,不知道这种办法对不对。下面的这些setXXXstate就是各种shader的状态,比如中毒效果,石化效果,.vsh和.fsh都是从刀塔传奇的shader里面直接拷过来的,也有一两个效果不好使,但是大部分都是有用的。
现在我想实现一些动态的效果,比如骨骼上面的光影会随参数变化而变化,之前在2.x上已经有一些效果,但是3.0就完全摸不到门路了。希望大牛们看到这个可以给出解决方案,也给一些想把简单shader用在骨骼动画上的人一些思路。

好顶!支持!

不需要 逐个 设置的。。。。。貌似他的 绘制 是一次完成 的 不是用的每个 骨骼的 绘制 而是 自己 一次 对所有骨骼进行 绘制

哎呀 是3.0啊 我看差了 无视我吧。。。

能不能吧你写的shader文件分享一下

我也是网上搜的,很常用的一些

vsh和fsh文件要怎么写,那个刀塔传奇的shader在哪可以下载??

谢谢分享!

我在给骨骼动画添加shader的时候遇到了一个问题,如果shader是携带uniform参数的,效率就会很低。
有没有好一点的解决方法?

用真机调试,模拟器就是效率很低

话说骨骼动画携带动态参数的你成功了?

该问题是游戏cocos2dx底层渲染判断机制的问题,只要是有uniform参数的shader,不管参数是否相同,他都会采用非batch来绘制,所以效率很低,你可以修改底层判断的机制来优化这个

我想问一下,大家都试验成功了么?都是在哪个版本下成功的?
我在3.0RC的版本下,如果加载了shader会造成贴图出现非常大的位移
具体表现为,比如我有个armature从右侧平移到左侧,为了看清楚,在armature下放个sprite做标记,可以看到标记向左平移到差不多1/3处,才出现armature,然后armature会快速的追赶标记,一直到屏幕最左边,才和标记重合

我也出现了你说的情况,我骨骼Armature设置shader 后 整个骨骼会变小,而且 位置坐标也不再指定的位置了

你解决没有?

那是因为3.0之后的vert不需要是用mvp的原因.

好高深,收藏了,支持一下

— Begin quote from ____

引用第14楼yefengsheji于2014-09-09 19:56发表的 回 13楼(yefengsheji) 的帖子 :
你解决没有? http://www.cocoachina.com/bbs/job.php?action=topost&tid=214515&pid=1049396

— End quote

15楼正解, 3.0RC以后 Armature的Skin在绘制命令发出时已经携带了MVP矩阵了,所以在顶点着色器里不能再使用MVP,传入的vert应该是NoMVP版本的:cocos2d::ccPositionTextureColor_noMVP_vert

好的谢谢,我看看

我的shader 是 不需要设置mvp的 :

shader:

attribute vec4 a_position;
attribute vec2 a_texCoord;
attribute vec4 a_color;

#ifdef GL_ES
varying lowp vec4 v_fragmentColor;
varying mediump vec2 v_texCoord;
#else
varying vec4 v_fragmentColor;
varying vec2 v_texCoord;
#endif

void main()
{
gl_Position = CC_MVPMatrix * a_position;
v_fragmentColor = a_color;
v_texCoord = a_texCoord;
}

这样是对的 不需要设置的