3.0 beta 2 RenderTexture stretching

Hi everyone,

I’m porting a project of mine from cocos2d-html5 to cocos2d-x, and have found some strange behaviour with the rendertexture class. Anything I draw to the rendertexture using visit() appears stretched, unless the rendertexture’s size is the same as the visible screen. The smaller I make the rendertexture, the larger the sprites drawn to it with visit() appear to be. Some example code:

// create renderTexture, and sprite to draw onto it
auto renderTexture = RenderTexture::create(512,512);
renderTexture->setPosition(0, 0);
renderTexture->beginWithClear(0, 0, 0, 255);
auto drawSprite = Sprite::create("draw.png");
drawSprite->setPosition(0, 0);
drawSprite->visit();
renderTexture->end();

// create sprite using rendertexture
auto rendTexSprite = Sprite::create();
rendTexSprite->setTexture(renderTexture->getSprite()->getTexture());
rendTexSprite->setTextureRect(Rect(0, 0, rendTexSprite->getTexture()->getContentSize().width, rendTexSprite->getTexture()->getContentSize().height));
this->addChild(rendTexSprite);

This causes the sprite drawn to the rendertexture (drawSprite) to appear stretched in the x axis. If the renderTexture is created with the same dimensions as the visible screen (e.g. 900x640), then the problem goes away, but I’m left with a huge texture.

Any ideas greatly appreciated :slight_smile:

that might be an issue of perspective.

Edit: actually more of aspect-ratio

Thanks, but I’m not sure what you mean?

Same thing here.

Please can someone check kingchimp’s description of the issue.
Currently it makes RenderTexture unusable.

The only way to get the content the right size is in the special case where the size of the RenderTexture is the same as the size of the visible screen. Typically a RenderTexture is smaller, which makes the content appear bigger (pixels are stretched accordingly).

The content size (size of pixels) of a RenderTexture should have nothing to do with its dimensions, the content should never be stretched if the RenderTexture scale is 1.

Any idea is welcome.

This fixes it.

In RenderTexture.cpp

void RenderTexture::onBegin()
{

Line 494:

const Size& texSize = _texture->getContentSizeInPixels();

// Calculate the adjustment ratios based on the old and new projections
Size size = director->getWinSizeInPixels();
float widthRatio = size.width / texSize.width;
float heightRatio = size.height / texSize.height;

// Adjust the orthographic projection and viewport
// original code:
// glViewport(0, 0, (GLsizei)size.width, (GLsizei)size.height);
// replaced with:
glViewport(0, 0, (GLsizei)texSize.width, (GLsizei)texSize.height);

It makes sense to me. And I get the behaviour I expected now.

I still think there’s a chance that it was not really a bug, and that somehow it was expected behaviour (however unlikely seeing that it differs from the older versions of cocos2d-x).
Please let us know.

Edit:
I was using cocos2d-x 3.0 beta 2.
The issue persists in RC0, however the class has changed.

It seems that _fullRect and _fullviewPort are switched in the .cpp.
According to the .h:

//fullRect: the total size of screen
//fullViewport: the total viewportSize

Then in the .cpp:
Line 197:

_fullRect = _rtTextureRect = Rect(0,0,w,h);
Size size = Director::getInstance()->getWinSizeInPixels();
_fullviewPort = Rect(0,0,size.width,size.height);

Should be:

_fullviewPort = _rtTextureRect = Rect(0,0,w,h);
Size size = Director::getInstance()->getWinSizeInPixels();
_fullRect = Rect(0,0,size.width,size.height);

Again, this fixes the issue.

@Romain thank you very much for this fix!
I spent two days trying to figure out where the problem is.

I ran into same problem and your solution fixed it for me too. I have created a pull request for this, hope it gets accepted for next release. Do tell me if you found any other details about this bug and I will add those changes also to my pull request.

https://github.com/cocos2d/cocos2d-x/pull/6298

Thanks guys.
We will review it soon.

Hi, Thank you for your report.
Please add code snap here
renderTexture->setKeepMatrix(true) to fix your bug.

For Grabbing part of texture, You can refer to Node:RenderTexture->RenderTexturePartTest in cpp-tests.
Thanks.

If there is questions, please let us know.

setKeepMatrix does not fix the problem, I also found another problem in my pull request.

Please note that the problem is not only how it looks (stretched), but also the “actual size” of the renderTexture and glViewport in pixels. RenderTexture’s texture size and viewport must be exactly same width and height to maintain full quality and to look correct.

There are two possible uses of renderTexture:

  1. Use renderTexture to save screenshot. Current code works fine.
  2. Use renderTexture to process a small or large texture using shaders, or create any size texture (and then use the generated texture in a sprite or as a sprite map etc)

Current code sets _fullviewPort (and glViewport) to screen size and that works only when saving screenshots. When creating a RenderTexture of different size, the code assumes that we want to render it on screen size, this is wrong. It makes the output 1) less pixels and less quality and, 2) also stretched if height/width ratio of texture is not same as screen height/width ratio).

By default BOTH _fullRect and _fullviewPort should be set to equal to renderTexture size. This will work fine when using renderTexture for processing or creating any size texture, and also work fine when taking a screenshot (because when taking screenshot user will create a renderTexture equal to screenSize).

I have updated my pull request for this.
https://github.com/cocos2d/cocos2d-x/pull/6298

1 Like

Thanks,

 2) Use renderTexture to process a small or large texture using shaders, or create any size texture (and then use the generated texture in a sprite or as a sprite map etc)

I am sorry i can not understand it, could you please give a more detailed description?

For RenderTexture, there are two usage for this.
1)same size as screenshot, the psuedo code like this:

auto size = Director::getInstance()->getWinSize();
auto renderTexture = RenderTexture::create(size.width,size.height);
renderTexture->begin();
drawSomething(...)
renderTexture->end();

Texture* texture = renderTexture->getSprite()->getTexture();
Dosomething(texture);

2)different size as screen, the psudo code like this:(only smaller size can work)

auto renderTexture = RenderTexture::create(x,y);
renderTexture->setKeepMatrix(true);
//rtBegin, lowerLeft point for grabbing
//fullRect, usually it is the size of screen in points
//fullViewport, usually it is the size of screen in pixels
renderTexture->setVirtualViewport(rtBegin,fullRect, fullViewport);
renderTexture->begin();
drawSomething(...)
renderTexture->end();

Texture* texture = renderTexture->getSprite()->getTexture();
Dosomething(texture);

There is one more use of RenderTexture, to take an input texture and process it using a shader into a RenderTexture. For example, when using PingPong RenderTexture.

  1. Create big InputTexture (2048x2048, or 1024x1024) etc… from a PNG or other resource.
  2. Create RenderTexture of same size (2048x2048).
  3. renderTexture->begin();
  4. Draw big InputTexture into RenderTexture using a shader.
  5. renderTexture->end();
  6. Use renderTexture->getSprite()->getTexture();

In this case the rendering and viewport etc should all be at full RenderTexture size 2048x2048. If we do it at screen size then we get less pixels in output and less quality.

I have created a new pull request for this bug. Instead of changing behavior of current init functions, it adds a new init function which takes height/width parameters in pixels instead of points.

i had a similar problem using render texture.
my problem is :
i have two renderTextures, A and B,both the same size of the screen .
i alternatively use its visit method to draw one onto another.
then found a strange issue that the image would stretch all sides but all right in middle
the step is as follows:
1)draw a sprite to renderTexture A
(like A->beginWithClear(0, 0, 0, 0);sprite->visit();A->end() )
2)draw A to renderTexture B
(like B->beginWithClear(0, 0, 0, 0);A->visit();B->end() ) )
3)in the next update ,draw B back to renderTexture A
(like A->beginWithClear(0, 0, 0, 0);B->visit();A->end() )

recyle step 2 and 3 ,and you would find the sprite show in renderTexture stretching in a rect
It’s hard to describe the phenomenon clearly ,
to make my description clear, I will show you some codes and screenshots

some codes:
renderTexture1 = RenderTexture::create(s.width, s.height, Texture2D::PixelFormat::RGBA8888);
renderTexture1->retain();
renderTexture1->setPosition(Point(s.width / 2, s.height / 2));
this->addChild(renderTexture1);

renderTexture2 =RenderTexture::create(s.width, s.height, Texture2D::PixelFormat::RGBA8888);
renderTexture2->setPosition(s.width/2, s.height/2);
renderTexture2->retain();
this->addChild(renderTexture2);

renderTexture1->beginWithClear(0, 0, 0, 0);
auto sprite = Sprite::create("testBg.png");
sprite->retain();
sprite->setPosition(s.width/2, s.height/2);
sprite->visit();
renderTexture1->end();

this->schedule(schedule_selector(RenderTextureSave::doNextSchedule),0.1);

int countn=0;

void RenderTextureSave::doNextSchedule(float dt){
if(countn++==100) this->unschedule(schedule_selector(RenderTextureSave::doNextSchedule));

if(countn%2==1){
    renderTexture2->beginWithClear(0, 0, 0, 0);
    renderTexture1->cocos2d::Node::visit();
    renderTexture2->end();
}else{
    renderTexture1->beginWithClear(0, 0, 0, 0);
    renderTexture2->cocos2d::Node::visit();
    renderTexture1->end();
}

}

1.png is the origin sprite,2.png is the show after 100 recycles
It seems that as a new user , I cannot upload images !!

you guys can try a test easily.
or see the screenshot in http://www.cocoachina.com/bbs/read.php?tid=207893

Any help appreciated

I still have a problem to capture a sprite texture. Here is my code:

const Size visibleSize = Director::getInstance()->getVisibleSize();

auto originPoint = Vec2::ZERO + Vec2(visibleSize.width * 0.5f, 400);

Sprite* normal = Sprite::createWithSpriteFrameName("worbits_header.png");
normal->setAnchorPoint(Vec2::ANCHOR_BOTTOM_LEFT);
normal->setPosition(originPoint);
addChild(normal);


RenderTexture* renderTexture = RenderTexture::create(normalSize.width, normalSize.height, Texture2D::PixelFormat::BGRA8888);

Size size = Director::getInstance()->getWinSize();
Size pixelSize = Director::getInstance()->getWinSizeInPixels();
renderTexture->setVirtualViewport(originPoint, Rect(0, 0, size.width, size.height), Rect(0, 0, pixelSize.width, pixelSize.height));


renderTexture->setKeepMatrix(true);
renderTexture->retain();
renderTexture->begin();
normal->visit();
renderTexture->end();


Sprite* normal2 = Sprite::createWithTexture(renderTexture->getSprite()->getTexture());
normal2->setAnchorPoint(Vec2::ANCHOR_MIDDLE);
normal2->setPosition(Vec2(visibleSize.width * 0.5f, visibleSize.width * 0.6f));
normal2->setFlippedY(true);
addChild(normal2);

And here is the original sprite (on a yellow background):

and the render texture result:

You see the letters how they become distorted?
@dabingnn could you please help me? I use Cocos-2d-x 3.5 and my content scale factor (if it matters) is 1.0f.

In addition I should say that distorted image I get even I I capture the whole screen:

Here is the code:

    const Size visibleSize = Director::getInstance()->getVisibleSize();
    Size screenSize = Director::getInstance()->getWinSize();

    auto rt = RenderTexture::create(screenSize.width, screenSize.height, Texture2D::PixelFormat::RGBA8888);
    rt->retain();
    rt->begin();
    m_ingameLayer->visit();
    rt->end();




    Sprite* normal2 = Sprite::createWithTexture(rt->getSprite()->getTexture());
    normal2->setAnchorPoint(Vec2::ANCHOR_MIDDLE);
    normal2->setPosition(Vec2(visibleSize.width * 0.5f, visibleSize.height* 0.45f));
    normal2->setFlippedY(true);
    removeAllChildren();
    addChild(normal2);

I get this on iOS and Windows.

Hello,

I still have this problem also. The center area is rendered fine but the margins are stretched.

screenshot

Is there anyone looking at this issue?

Thanks,
Marius

I come with more details after further investigations. Please find the screenshot at the following address, with the result of multiple rendering actions:

It seems that the rendering is made in tiles and maybe the buffer sizes for those tiles are not correctly computed?
Any help would be appreciated!
Thank you!

1 Like

I met similar problem and found a simple solution.

Add following line.

rt->getSprite()->getTexture()->setAntialiasTexParameter();