Sign in to follow this  
sqpat

Sprite border artifacts with linear filtering

Recommended Posts

sqpat    122
I understand that when I linearly filter my sprites, the border pixels will get mixed with the color keyed transparent sprites. There doesn't seem to be any way to avoid this problem when using linear filtering, so what I did was add an alpha stencil and block off these partially transparent pixels from getting rendered. Looks good enough... except now there's a very dark border around my sprite, and it's very visible with lighter backgrounds. I don't know why it's there or what to do about it. And it's really quite ugly and if I can't fix it its enough to make me switch to point filtering which I'd rather not have to do, cause linear filtered sprites look pretty. Anyway, here is an example of what I mean: http://sqpat.com/project/mysample37-uglyborders.PNG At first I thought it was cause the borders of the sprite were a darker color, but then I edited the hair in the second sprite so the border was the same color, and you can still see that dark border (perhaps even more clearly). Then i figured my alpha stencil was maybe too lenient so i made it more strict, with a higher cutoff: http://sqpat.com/project/mysample38-noBordersEvenWorse.PNG Yeah... the really pixellated edges dont mesh with the filtered style for one, and then the fact that it looks like garbage after a zoom out (due to the same reason) Any ideas on how to make this still look right with linear filtering? Maybe something with texture stages, like a custom alpha channel or something crazy? I really don't know, haha. I get the feeling I'll have to switch to point filtering unless someone has a very clever idea... I may as well ask, though! Don't all help me out at once, now ;)

Share this post


Link to post
Share on other sites
Drilian    1067
Premultiplied alpha is likely what you want.

Basically, you take normal alpha blending:

SrcBlend = source.a (multiply the source color by the source alpha)
DestBlend = 1-source.a (multiply the destination color by the inverse of the source alpha)

Basically, you (in a preprocess) multiply the color in the pixel by the alpha. What that does is make all pixels with 0 alpha be transparent black (and fades out any others). If, as it appears, your sprites are only ever alpha=1 or alpha=0, just make sure that the clear color is transparent black.

You then change the blend mode to:

SrcBlend = 1 (this is okay, the source has already been multiplied by the alpha)
DestBlend = 1-source.a (same as before)

What this does is, if you want to look at this way, darken the destination color to match the alpha and then add the entirety of the source to it.

It's very effective for eliminating unwanted borders on the edges of sprites.

Give it a go :)

Share this post


Link to post
Share on other sites
sqpat    122
[EDIT] long post and mostly nonsense, i answered these questions on my own [/EDIT]

[Edited by - sqpat on August 20, 2008 3:56:05 AM]

Share this post


Link to post
Share on other sites
sqpat    122
Quote:
Original post by Matt Aufderheide
1) dont use color keying
2) use an alpha channel
3) extend the rgb pixels on the sprite a bit around all edges (can be done simply using the photoshop filter "maximum"


you'd still get partly transparent pixels on the border due to filtering which would mess with the zbuffer and look weird, i'm pretty sure.

Share this post


Link to post
Share on other sites
sqpat    122
ok i racked my brain on this all day and here's what i came up with

a. i realize i can have gimp or photoshop or something do pre-multiplied alpha for me ahead of time. but im not sure how to write the dx code to make it work to render these premultiplied alpha images

b. i tried to do the premultiplied alpha stuff in the code itself. this is what i ended up with:

(note: alpha blending is enabled)

p_dx_Device->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_ONE);
p_dx_Device->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);

[EDIT] okay i had a bunch of texturestagestates here that dont do anything. [/EDIT]

i tried both the old green XRGB color key and the transparent black ARGB color key with a bit of editing of the .png sprite sheet and either way i get these black lines:

http://sqpat.com/premultalpha.bmp

by the way i've also seen some stuff about the compressed textures... DXT4 mentioned alpha blending. but i wasn't sure if they were lossless or not (kind of important when talking about sprite accuracy). Should i be looking at something like that or is it lossy?

[Edited by - sqpat on August 20, 2008 3:08:05 AM]

Share this post


Link to post
Share on other sites
Hodgman    51324
How are you loading your color-keyed sprites - is D3DX doing this for you or something?

Modern cards don't support color-keys - so what the loader is doing is just converting the color key into an alpha channel for you and then filling in the color-keyed pixels black.
That's the problem, you need your loader (after the alpha-channel creation process) to fill in color-keyed pixels using the color from a non-color-keyed bordering pixel so that this transparent border is the correct color.


If you can do this in your art tool and save them out with an alpha channel (instead of using color keys) it will be much easier.

Quote:
Original post by sqpat
Quote:
Original post by Matt Aufderheide
1) dont use color keying
2) use an alpha channel
3) extend the rgb pixels on the sprite a bit around all edges (can be done simply using the photoshop filter "maximum"


you'd still get partly transparent pixels on the border due to filtering which would mess with the zbuffer and look weird, i'm pretty sure.
Any kind of alpha blending will cause zbuffer artefacts unless you sort the sprites and draw them in furtherst-to-nearest order.

Share this post


Link to post
Share on other sites
sqpat    122
Yup - using d3dxcreatetexturefromfileexa (whew that's a long one)

Hodgman, your solution seems to be the exact same one as Matt's, which won't work. It still has the z buffer artifact problem, and I don't want to order sprites and primitives to both be rendered back to front cause that'd get rid of all my batching and slow down the pipeline, quite likely.

You're also incorrect on your statement because pre-multiplied alpha blending, when shooting for opaque only sprites, seems quite feasible to get away with without z-buffer artifacts, unless I'm misinterpreting drillan's explanation.

Share this post


Link to post
Share on other sites
Hodgman    51324
Quote:
Original post by sqpat
don't want to order sprites and primitives to both be rendered back to front cause that'd get rid of all my batching and slow down the pipeline, quite likely.

You're also incorrect on your statement because pre-multiplied alpha blending, when shooting for opaque only sprites, seems quite feasible to get away with without z-buffer artifacts, unless I'm misinterpreting drillan's explanation.
If you have alpha blending enabled, then you will get z-buffer artefacts unless you sort your geometry (explanatory diagram below).

If you don't want to sort, and don't want artefacts, then you have to disable alpha blending and use alpha-testing instead (which either draws a fragment or discards it - no blending occurs).


Why blending causes artefacts:

Background
| Back Sprite
| Front Sprite
| |w
|z |y |x <---- camera
| |v |

Pretend that each "|" represents a pixel on the screen. Pixels on the left are deeper in the screen and ones on the right are closer to the camera. Pixels on the same row are overlapping on the screen.

Lets say we draw background, then front sprite, then back sprite (z then x then y), also x is the edge of the front-sprite, so is 50% transparent.
After drawing z, the frame buffer contains the background color.
After drawing x, the frame buffer is half background color and half front-sprite color.
Drawing y has no effect, because the depth buffer discards it as x was already drawn with a closer depth value.

However, the pixels to either side (w, v) are the color of the back sprite -- but the pixel in between these two (x/y/z) does contain any color at all from the back sprite!

Share this post


Link to post
Share on other sites
I dont understand.. you cant have alpha blending AND use the Z-buffer.. it doesnt work that way (unless..you use alpha-to-coverage).

Normally with transparent sprites you just sort back to front..this can be easily batched by using D3DXSPRITE..

Why do people insist on making things harder than they should be?

EDIT:

There are some tricks to get what you want and use the Z-buffer so you dont have to sort if for some reason you dont want to.

option 1: a two-pass method... just render each sprite twice, the first with alpha bledning and soft edges, the second time with z-buffer and pixel perfect hard edges..this give you the look of anti aliased sprites, with mostly correct z-sorting.

option 2: render all sprites to a second render target twice the size of your main window, with z-buffer enabled and hard edges. Then after all else is rendered, just render this texture over the background on a screen-aligned quad with filtering, and you have perfect anti-aliased sprites.

option 3: i mentioned alpha-to-coverage.. this render state allows you fake alpha blending by using antialiasing and screendoor transparency.. this is not ideal in my opinion and not all cards support it.

Share this post


Link to post
Share on other sites
sqpat    122
EDIT:

Okay everyone! I think i finally got it. Here is what i did:

i set the color key to the ARGB (0, 0, 0, 0) and edited my PNG's color key accordingly

p_dx_Device->SetRenderState(D3DRS_ALPHABLENDENABLE, true);
p_dx_Device->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_ONE);
p_dx_Device->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);

Whoops, there was a small mistake there earlier... the pre-multiplied blending was correct, but since I wanted my sprites to be opaque, I forgot to turn on alpha blending in the first place!


Secondly, because I'm rendering the character sprites at two different intervals (once before and once after certain transparent objects are rendered, because of a certain effect i wanted) i was able to render it in a first pass with a heavy alpha stencil and in a second pass without the heavy alpha stencil. This got me this effect:

http://sqpat.com/project/mysample39-premult.PNG

(obviously the second char's spritesheet has yet to be edited. But it shows a nice bright backdrop to make it easy to see that the first character is properly drawn.)

thanks for the help everyone. i finally understand how these blend operations work, rather than just copying what people say to do, now.



lastly, sorry if i got anyone angry with that mini-rant earlier. that wasnt my intention, it just felt like no one was listening to what i was trying to say.

[Edited by - sqpat on August 20, 2008 4:25:30 AM]

Share this post


Link to post
Share on other sites
Drilian    1067
Looks great!

One thing to note: when you're working with alpha blending, the only current reliable way to not have weird effects when alpha blended objects overlap on-screen is to render the object in back to front order (That is, draw the characters farthest from the screen before the ones nearer the screen. That'll ensure that everything gets drawn properly.

Glad it works, good luck with the rest of the project!

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this