Cocos2dx:Sprite3D不会渲染到纹理

时间:2016-01-04 16:34:19

标签: cocos2d-x shader cocos2d-x-3.0 fragment-shader vertex-shader

我使用RenderTexture将包含其所有节点的图层渲染到纹理,然后在该纹理上应用OpenGL着色器以创建后期处理效果。除Sprite3D和Billboard节点外,它的工作正常。它已在他们的论坛上被问过几次而没有任何回应。我想知道是否有人让这个工作。

以下是一个例子:

Layer* gameLayer = Layer::create();
this->addChild(gameLayer, 0);

auto dir = Director::getInstance()->getWinSize();
Camera *camera = Camera::createPerspective(60, (GLfloat)dir.width / dir.height, 1, 1000);
camera->setPosition3D(Vec3(0, 100, 100));
camera->lookAt(Vec3(0, 0, 0), Vec3(0, 1, 0));
gameLayer->addChild(camera); //add camera to the scene

// You'll get a NULL camera inside BillBoard::calculateBillbaordTransform() function
// if you call visit()
/*auto billboard = BillBoard::create("cocos2d-x.png", BillBoard::Mode::VIEW_POINT_ORIENTED);
billboard->setPosition(Vec2(VisibleRect::center().x, VisibleRect::center().y));
gameLayer->addChild(billboard, 100);*/

// This one won't render into the texture
Sprite3D* sprite3D = Sprite3D::create("blend_test/character_3_animations_test.c3b");
sprite3D->setScale(5.0f); //sets the object scale in float
sprite3D->setRotation3D(Vec3(0.0f, 0.0f, 0.0f));
//sprite3D->setPosition3D(Vec3(VisibleRect::center().x, VisibleRect::center().y, 0.0f)); //sets sprite position
sprite3D->setPosition(Vec2(VisibleRect::center().x, VisibleRect::center().y));
gameLayer->addChild(sprite3D, 1); //adds sprite to scene, z-index: 1

// This one works just fine and appears black and white as expected
// in the resulting texture
Sprite* sprite2D = Sprite::create("cocos2d-x.png");
sprite2D->setPosition(Vec2(VisibleRect::center().x, VisibleRect::center().y));
gameLayer->addChild(sprite2D);

// Black and white OpenGL shader
GLProgram* glProgram = GLProgram::createWithFilenames("shaders/gray.vert", "shaders/gray.frag");
glProgram->bindAttribLocation(GLProgram::ATTRIBUTE_NAME_COLOR, GLProgram::VERTEX_ATTRIB_POSITION);
glProgram->bindAttribLocation(GLProgram::ATTRIBUTE_NAME_POSITION, GLProgram::VERTEX_ATTRIB_COLOR);
glProgram->bindAttribLocation(GLProgram::ATTRIBUTE_NAME_TEX_COORD, GLProgram::VERTEX_ATTRIB_TEX_COORD);
glProgram->bindAttribLocation(GLProgram::ATTRIBUTE_NAME_TEX_COORD1, GLProgram::VERTEX_ATTRIB_TEX_COORD1);
glProgram->bindAttribLocation(GLProgram::ATTRIBUTE_NAME_TEX_COORD2, GLProgram::VERTEX_ATTRIB_TEX_COORD2);
glProgram->bindAttribLocation(GLProgram::ATTRIBUTE_NAME_TEX_COORD3, GLProgram::VERTEX_ATTRIB_TEX_COORD3);
glProgram->bindAttribLocation(GLProgram::ATTRIBUTE_NAME_NORMAL, GLProgram::VERTEX_ATTRIB_NORMAL);
glProgram->bindAttribLocation(GLProgram::ATTRIBUTE_NAME_BLEND_WEIGHT, GLProgram::VERTEX_ATTRIB_BLEND_WEIGHT);
glProgram->bindAttribLocation(GLProgram::ATTRIBUTE_NAME_BLEND_INDEX, GLProgram::VERTEX_ATTRIB_BLEND_INDEX);
glProgram->link();
glProgram->updateUniforms();

RenderTexture* renderTexture = RenderTexture::create(VisibleRect::width(), VisibleRect::height());
renderTexture->retain();

Sprite* ppSprite = Sprite::createWithTexture(renderTexture->getSprite()->getTexture());
ppSprite->setTextureRect(Rect(0, 0, ppSprite->getTexture()->getContentSize().width,
ppSprite->getTexture()->getContentSize().height));
ppSprite->setAnchorPoint(Point::ZERO);
ppSprite->setPosition(Point::ZERO);
ppSprite->setFlippedY(true);
ppSprite->setGLProgram(glProgram);
this->addChild(ppSprite, 100);

renderTexture->beginWithClear(0.0f, 0.0f, 0.0f, 0.0f);
auto renderer = _director->getRenderer();
auto& parentTransform = _director->getMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW);
gameLayer->visit(renderer, parentTransform, true);
//gameLayer->visit();
renderTexture->end();

ppSprite->setTexture(renderTexture->getSprite()->getTexture());

2 个答案:

答案 0 :(得分:1)

Cocos2d-x v3.11.1(本文截至本文)及以下内容并未正确支持使用Sprite3D的RenderTextures,因为有明显的深度缓冲错误。

该错误有一个GitHub issue。但现在存在一种解决方法:

...
sprite3D->setForce2DQueue(true); // puts your Sprite3D on same render queue as the RenderTexture. More info below.
...
auto rt = RenderTexture::create(1280, 720, Texture2D::PixelFormat::RGBA8888, GL_DEPTH24_STENCIL8); // By default a depth buffer isn't created
rt->setKeepMatrix(true); // required
...
...
rt->beginWithClear(0, 0, 0, 0, 1); // required, clears the depth buffer

此外,需要对RenderTexture.cpp进行更改。这修复了Cocos2d-x中的清除深度缓冲区错误。

void RenderTexture::onClear()
{
    // save clear color
    GLfloat oldClearColor[4] = {0.0f};
    GLfloat oldDepthClearValue = 0.0f;
    GLint oldStencilClearValue = 0;
    GLboolean oldDepthWrite = GL_FALSE;

    // backup and set
    if (_clearFlags & GL_COLOR_BUFFER_BIT)
    {
        glGetFloatv(GL_COLOR_CLEAR_VALUE, oldClearColor);
        glClearColor(_clearColor.r, _clearColor.g, _clearColor.b, _clearColor.a);
    }

    if (_clearFlags & GL_DEPTH_BUFFER_BIT)
    {
        glGetFloatv(GL_DEPTH_CLEAR_VALUE, &oldDepthClearValue);
        glClearDepth(_clearDepth);

        glGetBooleanv(GL_DEPTH_WRITEMASK, &oldDepthWrite);
        glDepthMask(true);
    }

    if (_clearFlags & GL_STENCIL_BUFFER_BIT)
    {
        glGetIntegerv(GL_STENCIL_CLEAR_VALUE, &oldStencilClearValue);
        glClearStencil(_clearStencil);
    }

    // clear
    glClear(_clearFlags);

    // restore
    if (_clearFlags & GL_COLOR_BUFFER_BIT)
    {
        glClearColor(oldClearColor[0], oldClearColor[1], oldClearColor[2], oldClearColor[3]);
    }
    if (_clearFlags & GL_DEPTH_BUFFER_BIT)
    {
        glClearDepth(oldDepthClearValue);
        glDepthMask(oldDepthWrite);
    }
    if (_clearFlags & GL_STENCIL_BUFFER_BIT)
    {
        glClearStencil(oldStencilClearValue);
    }
}

有关详细信息,请参阅the issue。我还提出了example gist解决方法。截图如下。

我不确定广告牌,但这种解决方法也可能会解决它。

Example gist screenshot

有关Cocos2d-x渲染队列的信息:

Sprite3D需要与RenderTexture位于同一渲染队列中。 Cocos2d-x(截至v3.7左右)现在有5个渲染队列:

  • 全球Z订单< 0
  • 3D Opaque
  • 3D透明
  • 全球Z订单== 0(2D默认)
  • 全球Z订单> 0

您可以使用setGlobalZOrder(1)将Sprite3D和RenderTexture放在最后一个队列中,或者将Sprite3D放在带有sprite3D->setForce2DQueue(true)的2D队列中。

答案 1 :(得分:0)

与cocos2d RenderTexture不同,以下适用于3D屏幕捕捉(或我想象的任何东西)!

Sprite *  CcGlobal::getScreenAsSprite(void) {
    Size screenSize = Director::getInstance()->getWinSize();
    int width = screenSize.width;
    int height = screenSize.height;

    std::shared_ptr<GLubyte> buffer(new GLubyte[width * height * 4], [](GLubyte* p) {      CC_SAFE_DELETE_ARRAY(p); });
    glPixelStorei(GL_PACK_ALIGNMENT, 1);
    glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, buffer.get());

    Image* image = new (std::nothrow) Image;
    image->initWithRawData(buffer.get(), width * height * 4, width, height, 8);

    Texture2D *texture = new (std::nothrow) Texture2D();
    texture->initWithImage(image);
    SpriteFrame *spriteFrame = SpriteFrame::createWithTexture(texture, Rect(Vec2(0, 0), screenSize));
    Sprite *sprite = Sprite::createWithSpriteFrame(spriteFrame);
    sprite->setFlippedY(true);
    delete image;
    return sprite;
}

=============================================== ====