On Mon, Oct 5, 2009 at 4:29 PM, Richard Jones
<r1char...@gmail.com> wrote:
Sub-pixel rendering requires a lot of manual effort to ensure there's
no strange visual artefacts on the borders. Hence the standard Sprite
implementation avoids it.
Richard, can you elaborate a bit on what you mean? I'm assuming you are mean when the edges of a sprite either blackening or whitening as the sprite moves on subpixel coordinates?
...If you are, then I think what you are talking about is what happens when pixels are bilinearly interpolated from an opaque to a transparent pixel in the common RGBA color and blending model (dest = src*alpha + dest*(1-alpha)). It's not just a problem for subpixel - it's also a problem for scaling and rotation, so avoiding subpixel rendering is a bad way to "solve" the problem. Also, that border problem, is solved extremely easily, without a lot of "manual" effort, simply by using the (poorly named) "premultiplied alpha" for your textures and colors passed to gl and using the appropriate blending model (dest = src + dest*(1-alpha))
The basic problem is that in the type of RGBA photoshop uses, a color like (0,0,0,0) has a bizarre meaning that can't be mapped to the real world - the pixel is both fully transparent and completely black. But when people use that color, they usually just intend to mean fully transparent, and most of the time they happily live with the delusion of (0,0,0,0) meaning a transparent pixel cause the truth doesn't matter (like when they use integer sprite positions)
... but then in comes bilinear filter - which says interpolate linearly from one color to the other on RGBA independently... so when (0,0,0,0) meets, say, a fully yellow pixel (255,255,0,255), and they get interpolated evenly, you get (128,128,0,128), which is a half transparent, brown pixel. suddenly, that nice sharp yellow border of your sprite becomes a brown muddy mess as you sprite slides half a pixel over... all of a sudden, the fact that what you thought was transparent, was actually transparent black, comes into play, and "strange visual artefacts" occur.
Contrast that with "premultiplied alpha" (I like to call it light-opacity RGBA). In this model, the source color gets added to the dest without being multiplied by alpha - in this way, the RGB channels mean simply what to add when you are drawing. The Alpha channel on the other hand, gets multiplied by the dest to take away it's RGB. In this way, the alpha means simply how much of the dest to block, or what the "opacity" of what you are drawing is. So now when you say (0,0,0,0), you are saying to add no color (no light, really), and to take none of the original value away. That actually means transparent, which is what most people really want when they give that value - there's no color to the transparency to confuse things.
So now what happens when bilinear filter comes in, and blends your full yellow opaque pixel (255,255,0,255) halfway to your transparent pixel (0,0,0,0) in the light-opacity (premultiplied alpha) model? well bilinear filter didn't change, so you get the same numbers: (128,128,0,128). But the new model makes it mean a very different thing - now that value means take half of the dest away, and add in half a yellow. expressed in the common RGBA model, it's be the same as (255,255,0,128), or a half transparent yellow. No "strange visual artefacts" on your sharp yellow edge this time...
note that this model is nothing new - it's very common in 3d multipass rendering (even if people using it aren't fully aware why), it's the normal format 3d studio max always wrote to tga's in, and it's been in papers and such. Lots of hardware accelerated games on all kinds of platforms use it specifically because in light-opacity/premultiplied model, bilinear filtering actualy works right - it does what they actually wanted it to do with transparent and opaque pixels.
...Also, the problem with common RGBA blending going wrong is also an old problem lots of people have struggled with. If the art uses (255,255,255,0) edges whiten instead of blacken, and the fact the difference between (255,255,255,0) and (0,0,0,0) is not visible, has led to a lot of confusion. Quite often, artists have worked around the problem by extended their colors from the edges out to color the transparent pixels so they will blend right, just assuming it's what needs to be done - but it's sad so much effort was applied to something that should have been fixed with using better math. It's actually a very simple thing to do to convert existing stuff over to it (just convert pixels before you upload them to the card, convert your colors before passing them to the card, and change the gl blending model), and I've done it in a few games shortly before they ship (games in beta, for ones using openGL, directX and wii hardware rendering), without messing up the schedule at all.
...if on the other hand, you didn't mean the edges blackening/whitening as sprites move subpixel, I'm really curious to hear (or see) what it is you were thinking of :)