cocos2dx - shader实现任意动画的残影效果

本节主要讲利用cocos2dx机制实现opengl es shader脚本的绘制

  这里先看下最终效果:

                    

这里分别实现了灰度效果及残影的效果。

一、绘制基类

  这里主要参考了cocos2dx源码中 RenderTexture 的实现,有兴趣的可以了解下。

绘制基类RenderShader主要实现以下方法:

//******************************************************************
// 文件名:    RenderShader.h
// 创建人:    稀饭lei
// 版  本:    1.0
// 描  述:    特效基类
//******************************************************************
#ifndef _RenderShader_H__
#define _RenderShader_H__
#include "cocos2d.h"
USING_NS_CC;

// GL纹理坐标组
static const  GLfloat ccRenderTextcord[8] ={
    0.0f, 1.0f,
    1.0f, 1.0f,
    0.0f, 0.0f,
    1.0f, 0.0f,
};
class RenderShader : public Node
{
public:
    static RenderShader* create();

    virtual bool LoadByteArray(const GLchar* vShaderByteArray, const GLchar* fShaderByteArray);    //加载 shader文件

    void    begin();                // 用于设置绘制开始要调用的onBegin

    void    end();                    // 用于设置绘制结束要调用的onEnd

    void    push2Draw(Node* node);  // 添加待绘制节点

    virtual void draw(Renderer *renderer, const Mat4 &transform, uint32_t flags) override;  // cocos2dx绘制回调

protected:
    virtual void onBegin();

    virtual void onEnd();

    //virtual void onClear();

    virtual void Render();                // 绘制实现

    virtual bool init();

    virtual void CreateFrameBuffer();    //创建FBO及texture

    RenderShader(void);

    virtual ~RenderShader(void);

    GLuint m_nFrameBuffer;            // 用于当前绘制的fbo

    GLuint m_nFrameBufferTexture;    // 用于绑定在fbo中的texture

    GLProgram* m_glprogram;            // GL绘制管理

    GLint m_nOldFBO;                // 原来的fbo

    CustomCommand _beginCommand;
    CustomCommand _endCommand;
    GroupCommand _groupCommand;

    std::set<Node*> sRenderChild;    // 需要绘制的节点

    Size    m_sRenderSize;
};

#endif

这里有几个主要实现:

1、我们用push2Draw(Node* node);  方法将待绘制的节点添加到sRenderChild中等待处理。

2、参考RenderTexture 的实现,我们也利用begin,end将需要绘制的节点利用visit访问添加到当前的RenderShader类所在的render层中进行绘制。

3、在实际绘制过程中调用的onBegin中缓存对应的坐标系,同时绑定我们创建的FBO使得visit进来的节点可以绘制到当前FBO中。然后在onEnd中调用render方法将我们的FBO内容绘制原来cocos2dx底层的FBO上。

4、在render()实现如何绘制当前FBO的内容到cocos2dx的FBO上。(实际shader作业的地方)

详细实现代码如下:

#include "RenderShader.h"
#define STRINGIFY(A)  #A
const char* ccPositionTextureColor_frag_test = STRINGIFY(
\n#ifdef GL_ES\n
  precision lowp float;
\n#endif\n

varying vec4 v_fragmentColor;
varying vec2 v_texCoord;

void main()
{
    vec4 color = v_fragmentColor * texture2D(CC_Texture0, v_texCoord);
    float gray = dot(color.rgb, vec3(0.299, 0.587, 0.114));
    gl_FragColor = vec4(vec3(gray), color.a);
}
);

RenderShader* RenderShader::create()
{
    auto node = new RenderShader();
    if (node  && node->init())
    {
        node->autorelease();
        return node;
    }
    delete node;
    return nullptr;
}

bool RenderShader::init()
{
    if (m_glprogram)
    {
        m_glprogram->release();
        m_glprogram = nullptr;
    }

    CreateFrameBuffer();

    m_glprogram = GLProgram::createWithByteArrays(ccPositionTextureColor_noMVP_vert, ccPositionTextureColor_frag_test);
    m_glprogram->retain();

    return true;
}

RenderShader::RenderShader(void)
    :m_nFrameBuffer(0), m_nFrameBufferTexture(0), m_glprogram(nullptr)
{
    GLView* glView = Director::getInstance()->getOpenGLView();
    if (!glView)
    {
        return;
    }
    //屏幕大小
    m_sRenderSize = glView->getFrameSize();
}

RenderShader::~RenderShader(void)
{
    if (m_glprogram)
    {
        m_glprogram->release();
        m_glprogram = nullptr;
    }
    if (m_nFrameBuffer)
    {
        glDeleteFramebuffers(1, &m_nFrameBuffer);
        m_nFrameBuffer = 0;
    }
    if (m_nFrameBufferTexture)
    {
        glDeleteTextures(1, &m_nFrameBufferTexture);
        m_nFrameBufferTexture = 0;
    }
}

bool RenderShader::LoadByteArray(const GLchar* vShaderByteArray, const GLchar* fShaderByteArray)
{
    if (m_glprogram)
    {
        m_glprogram->release();
        m_glprogram = nullptr;
    }

    this->CreateFrameBuffer();

    m_glprogram = GLProgram::createWithByteArrays(vShaderByteArray, fShaderByteArray);
    m_glprogram->retain();

    return true;
}

void RenderShader::onBegin()
{
    if (!m_nFrameBuffer)
    {
        return;
    }
    Director* director = Director::getInstance();
    director->pushMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_PROJECTION);
    director->pushMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW);

    //save old fbo and bind own
    glGetIntegerv(GL_FRAMEBUFFER_BINDING, &m_nOldFBO);
    glBindFramebuffer(GL_FRAMEBUFFER, m_nFrameBuffer);
    glClearColor(0.0, 0.0, 0.0, 0.0);
    glClear(GL_COLOR_BUFFER_BIT);
}

void RenderShader::onEnd()
{
    if (!m_nFrameBuffer)
    {
        return;
    }

    // restore viewport for render to direct fbo
    Director *director = Director::getInstance();
    // restore viewport
    director->setViewport();
    this->Render();

    glBindFramebuffer(GL_FRAMEBUFFER, m_nOldFBO);
    director->popMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_PROJECTION);
    director->popMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW);
}

void RenderShader::Render()
{
    if (!m_glprogram || !m_nFrameBufferTexture)
    {
        return;
    }

    glBindFramebuffer(GL_FRAMEBUFFER, m_nOldFBO);
    m_glprogram->use();
    m_glprogram->setUniformsForBuiltins();

    //GLuint glTexture = m_glprogram->getUniformLocationForName("CC_Texture0");
    //glActiveTexture(GL_TEXTURE0);
    //glBindTexture(GL_TEXTURE_2D, m_nFrameBufferTexture);
    //glUniform1i(glTexture, 0);
    GL::bindTexture2D(m_nFrameBufferTexture);
    Size sSize = Director::getInstance()->getVisibleSize();
    float x = 0;
    float y = 0;
    float w = sSize.width;
    float h = sSize.height;
    GLfloat vertices[] = {
        x, y + h,
        x + w, y + h,
        x, y,
        x + w, y
    };

    glVertexAttribPointer(GLProgram::VERTEX_ATTRIB_POSITION, 2, GL_FLOAT, GL_FALSE, 0, vertices);
    glVertexAttribPointer(GLProgram::VERTEX_ATTRIB_TEX_COORD, 2, GL_FLOAT, GL_FALSE, 0, ccRenderTextcord);

    glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);

    /*GL::bindVAO(0);
    glBindBuffer(GL_ARRAY_BUFFER, 0);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);*/
}

void RenderShader::CreateFrameBuffer()
{
    glGetIntegerv(GL_FRAMEBUFFER_BINDING, &m_nOldFBO);
    if (m_nFrameBuffer == 0)
    {
        glGenFramebuffers(1, &m_nFrameBuffer);

        if (m_nFrameBuffer == 0)
        {
            CCLOG("m_FilterFrameBuffer == 0");
            return;
        }
    }
    glBindFramebuffer(GL_FRAMEBUFFER, m_nFrameBuffer);

    if (m_nFrameBufferTexture == 0)
    {
        glGenTextures(1, &m_nFrameBufferTexture);
        glActiveTexture(GL_TEXTURE0);
        glBindTexture(GL_TEXTURE_2D, m_nFrameBufferTexture);

        GLsizei nWidth = m_sRenderSize.width;
        GLsizei nHeight = m_sRenderSize.height;
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, nWidth, nHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
        GLenum err = glGetError();
        if (err != GL_NO_ERROR)
        {
            CCLOG("cocos2d: Texture2D: Error uploading compressed texture glError: 0x%04X", err);
            return;
        }

    }
    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_nFrameBufferTexture, 0);

    GLenum error;
    if ((error = glCheckFramebufferStatus(GL_FRAMEBUFFER)) != GL_FRAMEBUFFER_COMPLETE)
    {
        CCLOG("Failed to make complete framebuffer object 0x%X", error);
        return;
    }

    glBindFramebuffer(GL_FRAMEBUFFER, m_nOldFBO);
}

void RenderShader::begin()
{
    Director* director = Director::getInstance();
    CCASSERT(nullptr != director, "Director is null when seting matrix stack");
    director->pushMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_PROJECTION);
    director->pushMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW);

    _groupCommand.init(_globalZOrder);

    Renderer *renderer = Director::getInstance()->getRenderer();
    renderer->addCommand(&_groupCommand);
    renderer->pushGroup(_groupCommand.getRenderQueueID());

    _beginCommand.init(_globalZOrder);
    _beginCommand.func = CC_CALLBACK_0(RenderShader::onBegin, this);

    renderer->addCommand(&_beginCommand);
}

void RenderShader::end()
{
    _endCommand.init(_globalZOrder);
    _endCommand.func = CC_CALLBACK_0(RenderShader::onEnd, this);

    Director* director = Director::getInstance();
    CCASSERT(nullptr != director, "Director is null when seting matrix stack");

    Renderer *renderer = director->getRenderer();
    renderer->addCommand(&_endCommand);
    renderer->popGroup();

    director->popMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_PROJECTION);
    director->popMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW);

}

void RenderShader::draw(Renderer *renderer, const Mat4 &transform, uint32_t flags)
{
    //Begin will create a render group using new render target
    begin();

    ////clear screen
    //_clearCommand.init(_globalZOrder);
    //_clearCommand.func = CC_CALLBACK_0(RenderShader::onClear, this);
    //renderer->addCommand(&_clearCommand);

    //! make sure all children are drawn
    {
        auto it = sRenderChild.begin();
        while (it != sRenderChild.end())
        {
            if (Node* node = *it)
            {
                node->visit(renderer, transform, flags);
                node->release();
            }
            ++it;
        }
        sRenderChild.clear();
    }
    //End will pop the current render group
    end();
}

void    RenderShader::push2Draw(Node* node)
{
    if (node && sRenderChild.find(node) == sRenderChild.end())
    {
        node->retain();
        sRenderChild.insert(node);
    }
    else
    {
        CCLOG(" push same!!!");
    }
}

这里shader代码实现的是灰度图的效果,其他效果可以自己修改ccPositionTextureColor_frag_test内容。

二、实际使用

1、将RenderShader当成正常的Node使用,设置父节点及index坐标等。

        m_pShader = RenderShader::create();
        this->addChild(m_pShader);

2、因为RenderShader在draw中对需要绘制的子节点进行了清理,所以需要每次重新对子节点进行添加,添加动画节点如下:

void CPlayer::draw(Renderer *renderer, const Mat4 &transform, uint32_t flags)
{
    if (m_pShader)
    {
        m_pShader->push2Draw(m_pNode);
    }
}

主要事项:RenderShader在Render方法中将内容绘制到原来的FBO中,而cocos2dx对每个需要绘制的同一index层级会在开始的时候单独创建一个FBO来处理。因而如果RenderShader单独一个层级可能导致此时的FBO没有创建,导致没有绘制效果。

到这里我们就实现灰色角色的效果了,使用也依照cocos2dx的用法,相对简单。

三、残影效果扩展

主要实现代码如下:

//******************************************************************
// 文件名:    RemindShader.h
// 创建人:    稀饭lei
// 版  本:    1.0
// 描  述:    残影特效
//******************************************************************
#ifndef _RemindShader_H__
#define _RemindShader_H__
#include "RenderShader.h"
USING_NS_CC;
#define REMIND_RENDER_COUNT 5  // 残影个数
class RemindShader :public RenderShader
{
public:
    static RemindShader* create();

    virtual void draw(Renderer *renderer, const Mat4 &transform, uint32_t flags) override;
private:
    virtual void onBegin() override;

    //virtual void onClear();

    virtual void Render() override;

    virtual bool init() override;

    virtual void CreateFrameBuffer() override;

    RemindShader(void);

    virtual ~RemindShader(void);

    GLuint m_pTextureArr[REMIND_RENDER_COUNT]; 

    int   m_nRenderFrq;            // 渲染间隔帧率

    int      m_nRenderCount;        // 渲染间隔计数

    int   m_nCurTexutreIndex;    // 当前渲染的纹理ID
};

#endif

#include "RemindShader.h"

#define STRINGIFY(A)  #A
const char* ccRemind_fsh = STRINGIFY(
    \n#ifdef GL_ES\n
    precision lowp float;
    \n#endif\n

    varying vec2 v_texCoord;
    uniform float u_alpha;

    void main()
    {
        vec4 pcolor = texture2D(CC_Texture0, v_texCoord) * u_alpha;
        gl_FragColor = pcolor;
    }
    );

RemindShader* RemindShader::create()
{
    auto node = new RemindShader();
    if (node  && node->init())
    {
        node->autorelease();
        return node;
    }
    delete node;
    return nullptr;
}

bool RemindShader::init()
{
    if (m_glprogram)
    {
        m_glprogram->release();
        m_glprogram = nullptr;
    }

    CreateFrameBuffer();
    m_glprogram = GLProgram::createWithByteArrays(ccPositionTextureColor_noMVP_vert, ccRemind_fsh);
    m_glprogram->retain();
    return true;
}

RemindShader::RemindShader(void)
: m_nCurTexutreIndex(0), m_nRenderFrq(10), m_nRenderCount(0)
{
    memset(m_pTextureArr, 0, REMIND_RENDER_COUNT);
}

RemindShader::~RemindShader(void)
{
    if (m_pTextureArr[0])
    {
        glDeleteTextures(REMIND_RENDER_COUNT, m_pTextureArr);
        memset(m_pTextureArr, 0, REMIND_RENDER_COUNT);
    }
}

void RemindShader::onBegin()
{
    if (!m_nFrameBuffer)
    {
        return;
    }
    Director* director = Director::getInstance();
    director->pushMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_PROJECTION);
    director->pushMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW);
//    director->setProjection(director->getProjection());
//    GLView* glView = director->getOpenGLView();
//    if (!glView)
//    {
//        return;
//    }
//    Size frameSize =glView->getFrameSize();
//    {
//        // Calculate the adjustment ratios based on the old and new projections
//        Size size = director->getWinSizeInPixels();
//        float widthRatio = size.width / frameSize.width;
//        float heightRatio = size.height / frameSize.height;
//        //caculate the projections of size change
//        Mat4 orthoMatrix;
//        Mat4::createOrthographicOffCenter((float)-1.0 / widthRatio, (float)1.0 / widthRatio, (float)-1.0 / heightRatio, (float)1.0 / heightRatio, -1, 1, &orthoMatrix);
//        director->multiplyMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_PROJECTION, orthoMatrix);
//    }
//
//    //calculate viewport
//    {
//        glViewport(0, 0, (GLsizei)(frameSize.width), (GLsizei)(frameSize.height));
//    }
    glGetIntegerv(GL_FRAMEBUFFER_BINDING, &m_nOldFBO);
    // 仅在计数为0才更新新的渲染
    if (m_nRenderCount == 0)
    {
        glBindFramebuffer(GL_FRAMEBUFFER, m_nFrameBuffer);
        glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_pTextureArr[m_nCurTexutreIndex], 0);
        m_nCurTexutreIndex = m_nCurTexutreIndex + 1 < REMIND_RENDER_COUNT ? m_nCurTexutreIndex + 1 : 0;

         glClearColor(0.0, 0.0, 0.0, 0.0);
         glClear(GL_COLOR_BUFFER_BIT);
    }
}

void RemindShader::Render()
{
    if (!m_glprogram )
    {
        return;
    }

    glBindFramebuffer(GL_FRAMEBUFFER, m_nOldFBO);
    m_glprogram->use();
    m_glprogram->setUniformsForBuiltins();

    Size sSize = Director::getInstance()->getVisibleSize();
    float x = 0;
    float y = 0;
    float w = sSize.width;
    float h = sSize.height;

    // 除当前纹理外的纹理存储的都是拖影
    int nBeginIndex = m_nCurTexutreIndex;
    int nCurIndex = nBeginIndex;
    GLfloat falpha = 0.3f;
    float addf = 0.05f;

    // 渲染存在m_pTextureArr队列里的拖影纹理到主场景
    do
    {
        Point delta = _position;// this->convertToWorldSpace(_position);
        GLfloat vertices[] = {
            x   - delta.x,y+ h - delta.y,
            x+w - delta.x,y+ h - delta.y,
            x - delta.x  , y - delta.y,
            x+w - delta.x, y - delta.y
        };
        //GLuint glTexture = m_glprogram->getUniformLocationForName("CC_Texture0");
        //glActiveTexture(GL_TEXTURE0);
        //glBindTexture(GL_TEXTURE_2D, m_pTextureArr[nCurIndex]);
        //glUniform1i(glTexture, 0);
        GL::bindTexture2D(m_pTextureArr[nCurIndex]);

        GLuint glAlpha = m_glprogram->getUniformLocationForName("u_alpha");
        glUniform1f(glAlpha, falpha);

        glVertexAttribPointer(GLProgram::VERTEX_ATTRIB_POSITION, 2, GL_FLOAT, GL_FALSE, 0, vertices);
        glVertexAttribPointer(GLProgram::VERTEX_ATTRIB_TEX_COORD, 2, GL_FLOAT, GL_FALSE, 0, ccRenderTextcord);
        glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);

        falpha += addf;
        addf += 0.025f;
        nCurIndex = ++nCurIndex<REMIND_RENDER_COUNT ? nCurIndex : 0;
    } while (nCurIndex != nBeginIndex);
//
//    GL::bindVAO(0);
    //glBindBuffer(GL_ARRAY_BUFFER, 0);
    //glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
}
void RemindShader::CreateFrameBuffer()
{

    glGetIntegerv(GL_FRAMEBUFFER_BINDING, &m_nOldFBO);
    if (m_nFrameBuffer == 0)
    {
        glGenFramebuffers(1, &m_nFrameBuffer);

        if (m_nFrameBuffer == 0)
        {
            CCLOG("m_FilterFrameBuffer == 0");
            return;
        }
    }
    GLsizei nWidth = m_sRenderSize.width;
    GLsizei nHeight = m_sRenderSize.height;
    glBindFramebuffer(GL_FRAMEBUFFER, m_nFrameBuffer);
    if (m_pTextureArr[0] == 0)
    {
        glGenTextures(REMIND_RENDER_COUNT, m_pTextureArr);
        for (int i = 0; i < REMIND_RENDER_COUNT; i++)
        {
            glActiveTexture(GL_TEXTURE0);
            glBindTexture(GL_TEXTURE_2D, m_pTextureArr[i]);
            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
            glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, nWidth, nHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
            GLenum err = glGetError();
            if (err != GL_NO_ERROR)
            {
                CCLOG("cocos2d: Texture2D: Error uploading compressed texture glError: 0x%04X", err);
                return;
            }
            glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_pTextureArr[i], 0);
            glClearColor(0.0, 0.0, 0.0, 0.0);
            glClear(GL_COLOR_BUFFER_BIT);
        }
    }
    glBindFramebuffer(GL_FRAMEBUFFER, m_nOldFBO);
}

void RemindShader::draw(Renderer *renderer, const Mat4 &transform, uint32_t flags)
{
    if (sRenderChild.size() <= 0 )
    {
        if (m_nFrameBuffer)
        {
            // 清理所有缓存的纹理
            glGetIntegerv(GL_FRAMEBUFFER_BINDING, &m_nOldFBO);
            glBindFramebuffer(GL_FRAMEBUFFER, m_nFrameBuffer);
            for (int i = 0; i < REMIND_RENDER_COUNT; i++)
            {
                glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_pTextureArr[i], 0);
                glClearColor(0.0, 0.0, 0.0, 0.0);
                glClear(GL_COLOR_BUFFER_BIT);
            }
            glBindFramebuffer(GL_FRAMEBUFFER, m_nOldFBO);
        }
        m_nRenderCount = m_nRenderFrq - 1;
        return;
    }
    //Begin will create a render group using new render target
    begin();

    if (++m_nRenderCount == m_nRenderFrq)
    {
        //! make sure all children are drawn
        auto it = sRenderChild.begin();
        while (it != sRenderChild.end())
        {
            if (Node* node = *it)
            {
                node->visit(renderer, transform, flags);
                node->release();
            }
            ++it;
        }
        m_nRenderCount = 0;    // 0 提示需要渲染进去
    }
    sRenderChild.clear();
    //End will pop the current render group
    end();

}

几个关键点:

1、利用REMIND_RENDER_COUNT控制需要创建的Texture个数,通过每次绘制到不同的Texture上来保存原来的效果。

2、shader中利用u_alpha参数控制残影的透明度,在render中绘制到原来的FBO上时调用不同的透明度来实现残影渐隐的效果。

3、通过m_nRenderFrq来控制间隔的绘制次数,来实现残影的残留效果远近。

这样就实现了残影的效果了,使用的方法还是跟基类一样。

时间: 2024-08-10 08:23:41

cocos2dx - shader实现任意动画的残影效果的相关文章

Unity3D手游开发日记(8) - 运动残影效果

2D游戏的残影很简单,美术做序列帧图片就行了,那么3D游戏的残影美术做不了,得靠程序员动态创建模型来处理. 实现原理也很简单: 1.间隔一定时间创建一个残影模型 [csharp] view plain copy GameObject go = GameObject.Instantiate(origin, pos, dir) as GameObject; 2.对残影模型采用特殊的shader,要简单高效 [csharp] view plain copy public class MotionGho

as3如何做出残影效果

在页游中,时不时能看到人物做一些快速移动动作如冲刺时,有残影效果,强化了画面表现.实际人肉眼之所以能看到残影的效果,是因为观察到的物体会在人视线中残留几十毫秒时间,当运动物体运动太快时,人肉眼所见未能跟上物体运动速度,旧的物体影像未消失,物体已经移动到别的位置,就会产生残影效果. 实际游戏里物体的移动速度当然不可能这么快,但我们可以根据原理做出残影效果.用一张Bitmap专门draw运动物体,当物体移动时,并不把上一帧的bitmapData进行dispose,而是对bitmapData加上一层透

Spine动画在Cocos2d-x中,多动画切换播放出现残影的解决方法

最近在做的一个项目,NPC的动画是用Spine来做的.游戏中随着不同的状态变化,会导致NPC在"待机"."准备进攻"."进攻"."眩晕"."死亡"等等多种状态之间来回切换,NPC每切换一种状态,随之就要立刻播放该状态对应的动画. 游戏中播放spine动画是用SkeletonAnimation对象调用setAnimation方法,我遇到的问题是,在多种动画这样切换播放时,有的动画被切换后仍有残影存留,直到再一

Unity Shader : Ghost(残影) v1

前阵子组长给我提了个需求,要实现角色人物的残影.我百度google了一下,发现可以用两种方式实现这个效果:1.记录前几帧的人物位置,将其传入shader中,对每个位置进行一个pass渲染.2. 通过相机的targetRender,记录前几帧的人物的影像,然后通过后处理混合上去. 这里先介绍方法1,先看效果: 残影用了alpha混合的方法,将它们变得透明. 先列出shader代码: Shader "Custom/Ghost" { Properties { _MainTex ("

关于unity中spine动画切换时有残影或者动画播放不正确的解决方法

问题描述: 最近在用spine动画时发现,有时候角色在切换动画的时候会有残影,或者动画播放不正确,例如会丢失一部分节点,例如切换动画后角色虽然动画播放正常,但丢失了武器. 解决办法: 在unity编辑器下找到spine动画的SkeletonData文件,在Animationgs下面有一个"Setup Pos",这个按钮可以将骨骼(bones).资源槽(slots)或两者重置为装配动作(setup pose).它与调用`setBonesToSetupPose` 或 `setSlotsTo

Unity运动残影技能

残影实现: 1.List<DrawMesh> list,此list中包含某一帧动画模型网格.材质 2.每过一段时间就将运动物体的模型add到list中(优化:未实现,网格合并) 3.LateUpdate里将list中所有模型材质alpha减少,为0,则remove:其余的模型 DrawMesh 技能实现: 技能为两部分,前一部分是运动残影,后一部分是落地爆炸,协程处理即可. ps 此例简单实现了这个技能,不过应该能够做的更好一些,就是给运动物体添加状态:空中.即将落地,需要维护这两个状态,并且

PS制作gif动图以及背景透明与消除残影

摘要: 用Photoshop制作gif动画的要点:在窗口菜单中找到“时间轴”选中打开时间轴,单击一帧,设置该帧显示持续时间在图层里将该帧要显示的图层显示,并将不该显示的层隐藏,新建一帧,接下来就是重复劳动了.最后在文件菜单里找到存储为Web所用的格式,设置一下存储选项即可. 透明背景制作方法有二:一是删除每层的背景,这种方法应该算是通用的方法.二是在存储时在颜色表中选择设为透明色的颜色,在颜色表下方有个将选中的颜色映射为透明色的按钮,点一下就行了,这个方法应该是适合背景色是纯色,并且是主体没有的

仙人球的残影

Problem Description 在美丽的HDU,有一名大三的同学,他的速度是众所周知的,跑100米仅仅用了2秒47,在他跑步过程中会留下残影的哎,大家很想知道他是谁了吧,他叫仙人球,既然名字这样了,于是他的思想是单一的,他总是喜欢从一点出发,经过3次转折(每次向右转90°),回到出发点,而且呢,他每次转折前总是跑相同长度的路程,所以很多人都想知道如果用‘1’算他跑步出发的第一个残影的话,那么回到起点的时候,他的残影是怎么样的呢? Input 测试数据有多行,每一行为一个数N(1<=N<

[Cocos2d-x v3.x]序列帧动画

简介 Cocos2d-x中,动画的具体内容是依靠精灵显示出来的,为了显示动态图片,我们需要不停切换精灵显示的内容,通过把静态的精灵变为动画播放器从而实现动画效果.动画由帧组成,每一帧都是一个纹理,我们可以使用一个纹理序列来创建动画. 我们使用Animation类描述一个动画,而精灵显示动画的动作则是一个Animate对象.动画动作Animate是精灵显示动画的动作,它由一个动画对象创建,并由精灵执行. 创建方法 - 手动添加序列帧到Animation类 - 使用文件初始化Animation类 手