ps2dev / gsKit

gsKit is a C interface to the PS2 Graphics Synthesizer
Other
103 stars 27 forks source link

Example for pixel perfect texture mapping + fixes #11

Closed rickgaiser closed 7 years ago

rickgaiser commented 7 years ago

New example for testing how to perfectly map a texture onto the screen. It turns out: U 0.0, V 0.0 is the upper left corner of the upper left texel X 0.0, Y 0.0 is the center of the upper left pixel

Adding 0.5 to U and V only works when the magnification/minification = 1. Substracting 0.5 from X and Y always works.

Plus some other small fixes and improvements, most notably loading jpegs now works again.

sp193 commented 7 years ago

A long time ago, jbit wrote a short document on pixel-perfect texture mapping. It also describes that the texel coordinate system, but he mentioned something extra: simply adding/subtracting 0.5 doesn't totally make the texture sharp. So he continued to add/subtract (making the fractional part of some coordinates something like 0.625). Do you know what that might have been about?

rickgaiser commented 7 years ago

Yes, I've seen those fractional parts as well, like the 0.375 used in OPL before. I think it's used when magnifying the texture while using linear sampling. Instead of trying to align the upper-left corners of the pixel and the texel, you can also try to align the centers of the upper-left pixel and texel. Do you have this document or a link to it?

sp193 commented 7 years ago

Lukasz has the file mirrored.

rickgaiser commented 7 years ago

Thanks, that's a nice document. I tried those settings but in I can't decide what settings I like best. What do you think? vlcsnap-00011 vlcsnap-00012

The only difference I can see is in the enlarged version.

My settings also enlarge the green border (as is should, becouse it's enlarged), but that comes at the risk of texture bleeding when using linear filtering without clipping (like the font in OPL).

Jbits version tries to map from the center of texels to the center of the pixels. It looks like it could potentially be more accurate when using a 1:1 mapping (but I can't see the difference in my test), but when stretching the border texel will always be 1 pixel, and not stretched.

rickgaiser commented 7 years ago

Turns out Direct3D 9 is using the same coordinate system as the ps2 (with pixel centers at 0.5, 0.5). They have a nice document explaining how to map textures correctly: https://msdn.microsoft.com/en-us/library/windows/desktop/bb219690(v=vs.85).aspx

They too use an x/y offset of -0.5, -0.5.

(NOTE: OpenGL and Direct3D 10 use a different coordinate system)

sp193 commented 7 years ago

You're right... I can't tell which one is better/worse either. :|

You wrote that jbit's formula tries to map the center of texels to the centre of the pixels, but what is with the strange offset amounts? Given that the centre of the texels is 0.5,0.5, shouldn't subtracting 0.5 from both u and v be sufficient?

Thanks for sharing the link to the DirectX documentation. Yes, it gives a very clear explanation for why incorrect texel to pixel mapping can cause the texture's colours to be rendered differently from expected.

rickgaiser commented 7 years ago

Theoretically these are the values jbit uses:

Top-left: x/y = 0.0, 0.0 is the center of the top-left pixel u/v = 0.5, 0.5 is the center of the top-left texel

Bottom-right: x/y = width-1, height-1 is the center of the bottom-right pixel u/v = width-0.5, height-0.5 is the center of the bottom-right texel

But then it becomes a little more complicated, becouse the bottom right pixels are not drawn when they exacly intersect the center. To fix this add the smallest possible value to x/y (1/16 of a pixel), then the bottom right x/y becomes: x/y = width-0.9375, height-0.9375 is the center of the bottom-right pixel + 1/16 of a pixel

But this is not exactly the center of the bottom right pixel anymore, becouse we added 1/16th of a pixel. So the texture will not map perfectly anymore. So add a little something (1/8th a pixel? why?) to u/v as well: u/v = width-0.375, height-0.375 is the center of the bottom-right texel + 1/8 of a texel

The more I think about this method, the more I dislike it. Substracting 0.5 from the x/y coordinated is simple to understand. It is like a simple conversion from the Direct3D9 coordinate system to the OpenGL and Direct3D10 coordinate system.

sp193 commented 7 years ago

So it is not very clear why the centre of the bottom-right texel isn't exactly at 0.5,0.5, but just works that way? Thank you for your explanation on this matter!

rickgaiser commented 7 years ago

It's the top-left rasterization rule:

https://msdn.microsoft.com/en-us/library/windows/desktop/cc627092(v=vs.85).aspx "Any pixel center which falls inside a triangle is drawn; a pixel is assumed to be inside if it passes the top-left rule. The top-left rule is that a pixel center is defined to lie inside of a triangle if it lies on the top edge or the left edge of a triangle."

In short, we don't want to draw those pixels twice.

2017-07-21 10:29 GMT+02:00 Liu Woon Yung notifications@github.com:

So it is not very clear why the centre of the bottom-right texel isn't exactly at 0.5,0.5, but just works that way? Thank you for your explanation on this matter!

— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub https://github.com/ps2dev/gsKit/pull/11#issuecomment-316939449, or mute the thread https://github.com/notifications/unsubscribe-auth/AEfm37Pi1v6jgsHPZCnWKSd67aeMAU3Sks5sQGF1gaJpZM4OZHst .

sp193 commented 7 years ago

Thank you for the explanation. So if it can be explained, shouldn't going with jbit's explanation be best? It's not like it's more complicated (just that the amount to add/subtract is different). Even if we cannot see a visible difference, there should be some minute improvement.

rickgaiser commented 7 years ago

It looks like it could potentially be more accurate when using a 1:1 mapping

This was wrong, sorry. I didn't fully understand jbits version at the time, and him mentioning the DDA made me suspect there could be something special going on to make his version better somehow.

Even if we cannot see a visible difference, there should be some minute improvement.

When using a 1:1 pixel/texel mapping the solutions are both the same. Why not use the industry standard OpenGL/Direct3D10? The only difference of the ps2 in comparison to the what is now the standard is the center of the pixels. Just like microsoft changed the center of their pixels, I think we should go with that solution too.

Changing the texel coordinates as jbit does comes with other problems when magnifying. The border texels will be half the size. So when magnifying 4x, all texels will be shown on 4 pixels, but the border texels will only be shown on 2 pixels. Trying to solve this issue means understanding exactly how jbit's version work. I don't think there will be many developers willing to dive in that deep.

But all of this is a little besides the point, becouse gsKit does not do one or the other solution. gsKit just exposes the GS interface, that just so happens to be exactly like Direct3D9 (a standard at that time), and leaves it up to the application to choose a solution for pixel perfect texture mapping.

sp193 commented 7 years ago

Ah okay, that makes sense. Thank you for the detailed explanation.