I’m seeing some artifacts when I draw my Batch object to an FBO, and then drawing that FBO’s texture to screen (see attached screenshot).
I’m setting up the FBO like this:
gl::Fbo::Format designFboFormat; designFboFormat.setSamples(4); mDesignFbo = gl::Fbo::create(getWindowWidth(), getWindowWidth(), gl::Fbo::Format());
The BatchRef is created with a VboMesh made from triangulating an SVG document. I’m clearing the FBO with a transparent color before drawing the Batch object to it. Is there a different format that would result in me seeing the same thing whether I’m drawing to the FBO or not?
This screenshot is a result of drawing the Batch object the exact same way, once to the FBO and once directly to the screen. You can see the top version isn’t quite the same color and it has almost a slight border around the edges (not intentional).
Thanks!
I think you need to use premultiplied blending. Here’s a similar question in the old forum.
Super, that did the trick. Thanks a bunch!
Hey thanks again, that definitely helped my on-window drawing issues.
In my project, I’m drawing some things to this FBO and showing it on screen, then eventually saving the current state of the FBO to disk as an image like this:
ImageTarget::Options opts; opts.quality(1.0f); writeImage(imagePath, mDesignFbo->getColorTexture()->createSource(), opts, "png");
The image is saving to disk where I want it to, but the resulting file has some of the same artifacts as the FBO did before applying the premultiplied blending. Is there something similar I can do before saving the file?
Thanks again, appreciate the help.
I think it is normal if you see artifacts when saving the premultiplied Fbo. I assume you open the saved image later in an image viewer, which does not use premultiplied blending when displaying the image. I’m not sure what your use case is, but you can save the contents of the screen instead.
I’m sending the finalized PNG file with transparency to a web service, which in turns saves a record of it and then forwards it on to a printing company for printing on some swag. I won’t be touching the image again on the machine the Cinder app is running on (ie, i’m not opening it in Photoshop and resaving or anything like that). Best case scenario is I can save the contents of the FBO to disk, with the included transparency and without the artifacts.
I’ve been trying to wrap my head around the concept of premultiplied alpha, and having a bit of a tough time. I see there are some methods in the ip
namespace for premultiply()
and unpremultiply()
, would those be of any help to me when saving the image?
Thanks again, everything else is looking pretty good!
Ok, I just tried putting that ip::unpremultiply()
method before calling writeImage()
and it looks like it did work. From the earlier questions, I ended using premultiplied alpha when drawing to my FBO, so un-premultiply sounded like a rational choice to use before saving the image. Here’s what that snippet looks like now:
fs::path imagePath = getHomeDirectory() / LOCAL_DESIGN_DIRECTORY / submission.designId; ImageTarget::Options opts; opts.quality(1.0f); Surface8u *sur = new Surface8u(mDesignFbo->getColorTexture()->createSource()); ip::unpremultiply(sur); writeImage(imagePath, *sur, opts, "png");
Does this look reasonable? I am getting the results I want, but I still don’t quite understand the premultiplication idea…
The article that opened my eyes to pre-multiplied alpha is this one by Shawn Hargreaves. Hopefully this explains the difference. Your solution is a good one, as the compositing software probably can only handle normal alpha blending, not pre-multiplied.
One suggestion: try to avoid using raw pointers (or at the very least make sure to delete
the sur
variable after use). Here’s how you create a shared pointer that takes care of memory management for you:
auto sur = Surface8u::create(mDesignFbo->getColorTexture()->createSource()); ip::unpremultiply(sur.get()); writeImage(imagePath, *sur, opts, "png");
Thanks for the resource Paul!
Also, yeah, I usually use the shared pointer ::create()
methods, but I couldn’t figure out how to pass that to the unpremultiply()
method. Did not know about .get()
method on shared pointers, thanks for the tip!
The service you are using probably assumes your PNG files conform to the w3 specification for alpha representation in png. PNGs with alpha are officially non-premultiplied, though Apple premultiplies all PNGs bundled into an (iOS) application through XCode. So un-premultiplying for export seems like a good bet.