cocos2d / cocos2d-x

Cocos2d-x is a suite of open-source, cross-platform, game-development tools utilized by millions of developers across the globe. Its core has evolved to serve as the foundation for Cocos Creator 1.x & 2.x.
https://www.cocos.com/en/cocos2d-x
18.24k stars 7.06k forks source link

SpriteFrame rect offset changing on device, with textures exported from TexturePacker. #12847

Open UKDeveloper99 opened 9 years ago

UKDeveloper99 commented 9 years ago

I encountered this problem when I started cutting the level in my game in to smaller peices. I am using a sprite atlas exported from TexturePacker. If I move my Cocos2d::Camera (orthographic) in the Y axis the rect for each sprite frame appears to be changing its offset 1 by pixel. It doesn't happen consistently every frame, but maybe every 4-5 frames. There is a visible offset of the spriteframe and I know the rect is changing because I can see the first line of pixels from the frame above that particular frame in the sprite atlas is getting drawn. I've tried every option in TexturePacker and I'm convinced that's not the problem. I'm 99% it's something going wrong in the Cocos2D-x engine.

It should be fairly simple to reproduce, export a texture pack (sprite atlas) with a few images. Run it on a device, I used iPhone 4S. Then move the camera every frame on the y-axis, I moved it up (+0.1f). I have also tested it on an Android device (Samsung Galaxy Tab 4 10.1) and it still happens.

It's worth mentioning the problem appears to go away if running on the iOS simulator or in Win32, hopefully that may help to fix it.

It's also worth mentioning that the problem appears to happen rather sporadically, i.e. it has no set pattern. My first thought was floating point innacuracies in the shader or something.

UKDeveloper99 commented 9 years ago

Oh I forgot to mention, it's much easier to see visually if you set the background clear colour to red or white. It's also easier to see the problem if you disabled anti aliasing. sprite->getTexture()->setAliasTexParameters();

Don't misunderstand the cause however, the problem still exists with anti aliasing and no glclearcolour enabled.

UKDeveloper99 commented 9 years ago

Thought I'd add a few more comments on this problem. Here's a couple of things about reproducing the bug, make sure to move the camera up or down on the Y-axis, problem doesn't appear on the x-axis movement (this may give clues to fixing it). Make sure to move the camera by 0.05f every frame, this made the bug most noticeable for me, I think it's because this slow movement allows you to see exactly when the sprite gets redrawn at the next line of pixels. Make sure to disable anti aliasing on the sprite frames so the pixel edges are clean.

It's the top edge of pixels from the frame directly above in the sprite atlas that are drawn, so the sprite frame you test needs to have another frame above it. It's like the rect is offsetting by 1 pixel higher each frame then correcting itself.

Another thing, how does the anti aliasing work in cocos? Maybe when you apply anti alias to a sprite frame wouldn't it mix in pixels from other sprite frames outside the sprite frame. I know extrude in texture packing software can solve these problems to an extent.

Hope this helps.

pandamicro commented 9 years ago

Thank you for the detailed explanation, we will check this issue later

ricardoquesada commented 9 years ago

@pandamicro let me know if you need my help.

slackmoehrle commented 9 years ago

@pandamicro, @UKDeveloper99 is working on a very tight deadline for a large scale Cocos2d-x game. If we could see about resolving this as soon as makes sense, I think he would appreciate it very much :-)

fusijie commented 9 years ago

@UKDeveloper99 I I almost understand the problem. but could you please provide me some reproduce codes and resource to avoid my misunderstanding test case. Thank you.

UKDeveloper99 commented 9 years ago

@fusijie Sure I'll put something together.

UKDeveloper99 commented 9 years ago

Hi @pandamicro @fusijie @ricardoquesada

I put together a small test case using the hello world example project. I used Cocos2D-x v3.4 final from the cocos store, but I'll try later with the newer versions, I have a feeling the bug will still be there however.

Here is the test project, all the code is the in the helloworldscene class. https://drive.google.com/file/d/0BzImXAeK77t4Z2dCNEZ4X0d4SHM/view?usp=sharing I packed the sprite atlas separately as well as in the project resources folder for your convenience. https://drive.google.com/file/d/0BzImXAeK77t4T3FuNjJjWjA0ZVE/view?usp=sharing

Couple more notes: I had a bit of a problem recreating it first, it seems the camera positions and sprite positions make a difference.

fusijie commented 9 years ago

@UKDeveloper99 Thanks for your detailed description and code. I have reproduced this issue and found that the cause may be the texture coordinates. Cocos2d-x provide a precompiled macro CC_FIX_ARTIFACTS_BY_STRECHING_TEXEL to use 99% of the texture. You can enable it in the base/ccConfig.h.

ricardoquesada commented 9 years ago

@fusijie Does the CC_FIX_ARTIFACTS_BY_STRECHING_TEXEL fix the issue?

IIRC, I added CC_FIX_ARTIFACTS_BY_STRECHING_TEXEL in cocos2d-iphone to prevent some bugs with textures. But actually it was fixing the bugs but it worked ok for some textures.

UKDeveloper99 commented 9 years ago

@fusijie @ricardoquesada I was aware of CC_FIX_ARTIFACTS_BY_STRECHING_TEXEL, however a lot of my research indicated that this option was essentially a hack fix. it's also worth mentionining that there is visual stretching and shrinking of the textures. It really makes the visual quality poor.

I don't think this can realistically be considered as a fix. I really think this issue needs revisiting and looking at some alternative options, judging by the amount of forum posts over the past 5 years it seems to have been a problem that has plagued developers for a while.

AFAIK there is another hacky solution, you can also avoid the problem occuring by rounding sub pixel coordinates, but this means all coordinates, including parent nodes etc. And I don't think this works when anything is scaled. So yeah, someone brave want to have a look in to a proper fix for this?

UKDeveloper99 commented 9 years ago

@ricardoquesada look familiar?

http://forum.spritebuilder.com/t/possible-bug-of-cocos2d-texture-packer/11429

https://code.google.com/p/cocos2d-iphone/issues/detail?id=1345

The first link has a post which I believe describes the exact cause of the issue.

UKDeveloper99 commented 9 years ago

That does mention about the float precision in the shader, I haven't tried that yet, but it seems like a more sensible fix. Is there any big performance implications to having high precision floats in the shader?

Edit: seems it does have a noticeable performance hit on some lower end devices.

UKDeveloper99 commented 9 years ago

http://blog.seasons.cc/entry/20101231/1293802694

ricardoquesada commented 9 years ago

@UKDeveloper99 absolutely... that's why I was surprised that CC_FIX_ARTIFACTS_BY_STRECHING_TEXEL fixed the issue.

@pandamicro @fusijie could you please check this issue again with the info posted by @UKDeveloper99 ? Thanks.

pandamicro commented 9 years ago

Ok, @fusijie is checking it

fusijie commented 9 years ago

@UKDeveloper99 @ricardoquesada I have refined the test case on my branch. There are some similar issues happened and people either enable CC_FIX_ARTIFACTS_BY_STRECHING_TEXEL or setAntiAliasTexParameters() (implemented by use GL_LINEAR Filtering mode internally) to cover it, but it seems as hack fix. I have not found the solution now, but I want to update my research by now.

I removed camera, and just moving the sprite per frame.

Contrast Tests

Other Tests

UKDeveloper99 commented 9 years ago

@fusijie A couple of things, I'm fairly sure you can't reproduce the issue if the single image occupies the entire texture pack. It's something to do with the texture coordinates that are < 1 and > 0 I think.

Also if this was the case then a regular sprite would also have the same issue.

I think CC_FIX_ARTIFACTS_BY_STRECHING_TEXEL just masks the issue, because when the flash line appears the texture is stretching to fill that area (by 1 pixel) and then it returns to it's original size. It does this over and over.

It's like the rect size (of the frame) is deviating when the flash line appears.

Just my thoughts.

Maybe @ricardoquesada has some ideas.

fusijie commented 9 years ago

@UKDeveloper99 yes, you are right, but Test2 and Test4 both have a 2 pixels padding, and Test3 just as a contrast test. No matter CC_FIX_ARTIFACTS_BY_STRECHING_TEXEL or setAntiAliasTexParameters only to cover the issue. Sorry for my poor English...

UKDeveloper99 commented 9 years ago

@fusijie Your English is fine :) Hmm unless I was sat trying to fix the issue myself I'm not sure what to suggest. Can you look at some more things in the debugger. See if the sprite frame rect is changing somewhere maybe?

Also I remember making a test with 0 padding and several frames and I was still experiencing the issue. I don't think the padding makes a difference.

ricardoquesada commented 9 years ago

@fusijie Please, create a test case that reproduces this bug, call it "Bug #12847" and send a Pull Request with the test case. If you cannot fix it, I'll take a look at it. thanks

ricardoquesada commented 9 years ago

@fusijie One more thing. Please, make sure to add the test in the "cpp-tests" -> "bugs" section. Thanks!

ricardoquesada commented 9 years ago

@fusijie thanks. I'm working on the bug now.

ricardoquesada commented 9 years ago

I haven't fixed the bug, and I'm not sure what is the best strategy to fix it yet.

I continued with @fusijie 's test and replaced the top line with Pink and the bottom line with Yellow. The example from the left is using simple sprites. The example from the right is using SpriteFrame. As you can see the example from the left doesn't show the "gap", the background red line, because it is drawing the top line (Pink) twice. And the example from the left has the gap because it is not drawing the top line twice.

img_3436

So, my suggestions for the moment are:

More info about this bug:

ricardoquesada commented 9 years ago

Pull Request that improves that test case: https://github.com/cocos2d/cocos2d-x/pull/12964/files

UKDeveloper99 commented 9 years ago

@ricardoquesada interesting, I have seen some posts on forums about setting the projection to 2D. It's definitely not a straightforward bug. Keep up the good work, I'm sure you'll find the fix :) !

ricardoquesada commented 9 years ago

@UKDeveloper99 have you tried with "extrude + 2 pixels of padding/border"? did it work?

UKDeveloper99 commented 9 years ago

@ricardoquesada I believe I tried it before and it didn't make a difference, but I'll try it again now and see if it makes a difference, so for clarity I will use. Extrude: 1, Padding: 2, Border: 2 and Projection::_2d

ricardoquesada commented 9 years ago

yes, please. thanks.

UKDeveloper99 commented 9 years ago

@ricardoquesada Yes I can confirm this appears to look correct to me. It's a little difficult to tell with the extrude option as obviously the outer edge of pixels looks identical so without a pink or yellow line like you have used you it's a little difficult to see. But it definitely doesn't look anywhere near as bad as the CC_FIX_ARTIFACTS_BY_STRECHING_TEXEL fix, with the visual stretching and shrinking. I don't think the top line of extruded pixels is flashing in and that's the most important thing.

I still think the underlying cause needs to be resolved and I hope you can get it in to v3.8 as planned (e.g. other developers may run in to this problem and not be able to find this workaround). However I can work with this solution for now. Thanks.

UKDeveloper99 commented 9 years ago

@ricardoquesada @fusijie out of interest is there any performance implications or features that may stop working with 2D projection?

ricardoquesada commented 9 years ago

2d projection is not really needed. But if you are not going to use 3d features, I suggest using it. No performance implications in using one or the other.

Creating spritesheets with extrude and 2 pixels of padding/border is pretty much what I have been recommending from day one in cocos2d-iphone and cocos2d-x. All spritesheets must be created like that.

You are not triggering the bug you mentioned before... that one could be fixed by using highp. You are triggering another one, which could be fixed by using extrude + border, or by changing some shaders to round up/down the floats... I think this bug is low priority right now since the workaround is trivial to implement... and perhaps the "real fix" will have performance implications.

UKDeveloper99 commented 9 years ago

@ricardoquesada I know they are recommended, when you select the cocos2d preset in TexturePacker in turns them on for you. I was experimenting with them turned off because when I was trying to fix the original bug they weren't making a difference. Of course they help with the various OpenGL artifacts/issues that can crop up, as can pre multiplied alpha.

I don't think using highp is an option, I tested that last week and it really makes a difference to the fps. And obviously the more stuff you have in the scene the more noticeable that difference is.

I did experiment with rounding, initially I thought it might help if all the positions were on rounded coordinates. Something like.... floor(position.x + 0.5f) as an example. That didn't help. I wouldn't know which parts to round in the shader.

Hmm I agree the workaround is trivial, but the fact is unless it is put in to the documentation/ programmers guide. The average user isn't gona know what they need to do to fix it. It's trivial for you and me but it's not something that's enabled by default.

I highly agree that if the "real fix" has performance implications then it can't be considered at all, it would be interesting to know if you had some idea of what the "real fix" might be, just out of curiosity.

ricardoquesada commented 9 years ago

@UKDeveloper99 good point. We should put it in the Programmers Guide!

The "real fix" has to do with subpixeling... at least that is my understanding of this problem. And the fix is to prevent subpixeling.

So, rouding the pixels, as you said before, should fix this. But I wouldn't know right now what is the best way to do it.

UKDeveloper99 commented 9 years ago

@slackmoehrle Programmers Guide^

slackmoehrle commented 9 years ago

Yes, we should definitely add. If you want to take a pass at the content to add I can then put it in the correct format, make sure it makes sense, etc, etc.

On Fri, Jul 24, 2015 at 11:49 AM, Mark notifications@github.com wrote:

@slackmoehrle https://github.com/slackmoehrle Programmers Guide^

— Reply to this email directly or view it on GitHub https://github.com/cocos2d/cocos2d-x/issues/12847#issuecomment-124563880 .

UKDeveloper99 commented 9 years ago

@slackmoehrle sure, if I get a spare moment. This project is really intense at the moment.