Bad texture quality. Why?

magistr

24-11-2007 10:15:44

I have UI element:


I am paste this element into my game, and take low-qualyty result.
Result is blurred :-(
Why?


For compare, I am create collage in Photoshop:


As can be seen, quality is lost.

kungfoomasta

24-11-2007 21:59:05

What Widget is this? Is the widget size the same pixel dimensions as the Texture?

Outside of this, all I can tell you is that I am using Ogre Texture, and QuickGUI does nothing outside of Ogre's capabilities to render the image. It should look exactly the same if you make your own ManualObject and texture it with your picture.

magistr

24-11-2007 22:39:21

What Widget is this? Is the widget size the same pixel dimensions as the Texture?
Yes. Image has size 137x158, and widget has same dimension.

I am create my widget with following code:

QuickGUI::Image * userNamePanel = mSheet->createImage();
userNamePanel->setTexture("UserIcon.png");]
userNamePanel->setDimensions(QuickGUI::Rect(880, 5, 137, 158));


I am test application in OpenGL mode. This bug does not exists in OpenGL.

Why image widget is blurred in DirectX mode.

magistr

24-11-2007 23:27:02

I am download latest SVN version of QuickGUI, OGRE 1.4.5 and paste into
QuickGUI demo following code:
QuickGUI::Image * userNamePanel = mSheet->createImage();
userNamePanel->setTexture("UserIcon.png");
userNamePanel->setDimensions(QuickGUI::Rect(800, 0, 137, 158));




Also I am try to use all supported by OGRE image formats: jpg, png, psd, bmp, tga but my widget is blurred :-(

In OpenGL mode:


WHY?

kungfoomasta

25-11-2007 04:27:50

Interesting! Are you using the latest video card drivers? In Direct X, if you shrink the dimensions a small amount, does the Image get sharper?

Try:

userNamePanel->setDimensions(QuickGUI::Rect(800, 0, 136, 157));

magistr

25-11-2007 05:43:35

My videocard is NV550. And lates drivers is not for me :-)
But I am use latest driver where my card is updated.

if I set height dimension to 157, image is improved, but not much :-(

hotdot

25-11-2007 06:26:32

Hi,

just my two cents, if you are testing your UI in game with different resolutions than the one your desktop use with photoshop, result will be blurred because of downgrade, this is a LOD thing with resolution. for same result do both in same resolution, if this does not fix the problem check setting of OGRE for textures. Same thing happened with other engines like quake3 when modding. below 1024 resolution there is always a quality loss and frame rate gains.

hope this helps.

magistr

25-11-2007 15:12:09

Hm. interesting...
But this is does not resolve my problem :-(

I am set desctop resolution 1024x768 and create full screen application with same size 1024x768. But images is blurred.

if this does not fix the problem check setting of OGRE for textures.
hm. for example please. I am not found any settings.

mr.Zog

25-11-2007 16:02:01

it's just a guess, but could this result from texture filtering or mipmapping?

magistr

25-11-2007 16:08:19

I am dont know.

I am edit code to disable mipmap for texture, but image is blurred.

This code create image in QuickGUI demo:

QuickGUI::Image * userNamePanel = mSheet->createImage();
userNamePanel->setTexture("UserIcon.png");
userNamePanel->setDimensions(QuickGUI::Rect(800, 0, 68, 78));
userNamePanel->getQuad()->setColor(ColourValue::White);
userNamePanel->getQuad()->setOpacity(1);
ResourcePtr pt = Ogre::TextureManager::getSingletonPtr()->getByName("UserIcon.png");
Texture* tex = (Texture*)pt.getPointer();
tex->setNumMipmaps(0);


Source image:
http://www.switlle.net/art/UserIcon.png

magistr

25-11-2007 16:10:36

How to reset texture filtering?

magistr

25-11-2007 17:58:36

For test I am create equal code with OGRE overlays. And image is NOT blured!

Code based on overlays:
int width = 137;
int height = 158;
MaterialPtr pmat = MaterialManager::getSingleton().create("UserIcon.png",
ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME);
pmat->getTechnique(0)->getPass(0)->createTextureUnitState("UserIcon.png");
pmat->getTechnique(0)->getPass(0)->setSceneBlending(SBF_SOURCE_ALPHA, SBF_ONE_MINUS_SOURCE_ALPHA);
OverlayManager& overlayManager = OverlayManager::getSingleton();
Overlay* myOverlay = overlayManager.create("Overlay");
OverlayContainer* panel = static_cast<OverlayContainer*>
(overlayManager.createOverlayElement("Panel", "myPanel"));
panel->setMaterialName("UserIcon.png");
panel->setMetricsMode(Ogre::GMM_PIXELS);
panel->setPosition(0, 0);
panel->setDimensions(width, height);
myOverlay->add2D(panel);
myOverlay->show();


consequently it is QuickGUI bug???

ajs15822

25-11-2007 22:17:01

Magistr, what you're looking to do requires two things:
  1. perfect pixel alignment of the texture in screen-space (Manual Quad or UV's are properly scaled to the pixel dimensions of the screen)[/*:m]
  2. all texture-filtering for that texture unit state in the material is disabled (FO_NONE)[/*:m][/list:u]

    I haven't used QuickGUI extensively but I assume it has constructs that make the first requirement possible.

    As for the second requirement, you can disable texture filtering for a material programmatically like so:
    someMaterial->getTechnique(0)->getPass(0)->getTextureUnitState(0)->setTextureFiltering(FO_NONE, FO_NONE, FO_NONE);

    This is how Navi stays super-sharp. ;)

kungfoomasta

25-11-2007 22:37:31

Thanks ajs for the information! I was starting to pull out my hair figuring out how Ogre actually renders stuff, its kind of complex...

When setting the filtering properties, I followed the Ogre CEGUI renderer, by using the same line of code:

d_render_sys->_setTextureUnitFiltering(0, FO_LINEAR, FO_LINEAR, FO_POINT);

I will try setting these all to FO_NONE and see what happens. Its odd to have these yield differing results between directX and openGL.

Also, another thing I haven't yet figured out is regarding texels. What are they? Should I be using them? I think they are related to positioning of quads, although I believe the quads are positioned correctly, so I think I don't need this.

Also, regarding Overlays, this is the only obvious properties set by overlay materials I could find:


mpMaterial->setLightingEnabled(false);
mpMaterial->setDepthCheckEnabled(false);


I already disable lighting, and for depth check, I'm not sure. When I look at the DX vs GL images, it seems to be blurry because DX image is lighter in color, the dark areas need to be darker.

kungfoomasta

25-11-2007 22:53:25

Unfortunately setting everything to FO_NONE didn't produce any visible differences. Somewhere in Ogre material processing is done to remove this affect. I've tried looking through how materials are rendered, but its confusing with the RenderGroup, RenderQueueGroup, RenderQueuePriorityGroup, RenderInvocationGroup, etc. etc.

Maybe I can try something like making a material, inserting the texture into it, and rendering the material, and hope it fixes the problem. (using a material for every widget texture would be really bad for performance)

ajs15822

26-11-2007 00:31:34

A 'texel' is really just another name for 'texture pixel', the smallest unit of a texture; textures are really just 2-dimensional arrays of colored squares (texels).

Let's go through this with examples:

Here is a texture that is 300 texels wide and 300 texel tall:



Assuming you are using your monitor's native resolution and your browser is not scaling images, you will view this image in a 1:1 texel to screen pixel ratio. Thus the text appears crisp and the edges of the yellow circle appear quite sharp.

Notice what happens when we apply this texture to a 3D plane that is rotated a little:




Some of the texels are bigger than or smaller than the pixels of the screen, the ratio of texels to screen pixels is not 1:1! The videocard tries to fill in the misaligned data using Nearest neighbor interpolation, thus you see ugly blocks and artifacts.


This is where texture filtering comes in; a bilinear filter will try to average the color of the four nearest texels to a pixel:




Game GUI's generally try to preserve a perfect 1:1 texel to screen pixel ratio by scaling the UV coordinates and/or the dimensions of an identity-projected quad. Thus they save performance and ugly blurring of textures by removing the need for any texture filtering.

I've never used RenderSystem::_setTextureUnitFiltering, so I can't be of any help there. :?

kungfoomasta

26-11-2007 01:05:29

Thanks again! When I see texels being used, its normally to slightly adjust the position of a texture. So either way its probably not something really big. I'm taking a break from the image investigation.. I'll probably try the material idea and see how that works. (first I have to figure out how to render materials, Tuan's code might be a good starting point)

magistr

26-11-2007 06:37:15

As for the second requirement, you can disable texture filtering for a material programmatically like so:
someMaterial->getTechnique(0)->getPass(0)->getTextureUnitState(0)->setTextureFiltering(FO_NONE, FO_NONE, FO_NONE);

This is how Navi stays super-sharp. ;)


But QuickGUI does not have materials. How to set this for texture?

kungfoomasta

26-11-2007 08:40:06

Direct X:



I have figured out how to get the image to display unfiltered, so that it looks the same in direct X as in Open GL. The problem right now is that the unfiltering applies to everything, including rendering of the Text, which looks very blocky and choppy. I will have to think about how to deal with this, I have to change the design of the render system, and its not a simple procedure.

For now, develop in OpenGL. I will add the Render System update to the list of items required for next release.

magistr

26-11-2007 21:49:35

My project fully suport only DirectX :-(
Please tell me how to manualy remove filter for image.
If this have difficulties, I can watch next release. But this is very important for me.

Please, tell me: how long I must wait?

Best regards!

kungfoomasta

26-11-2007 21:55:50

If you remove filtering, you will see the text as shown above, it will look really bad! Can you give me until the weekend to implement this, it requires a lot of work. I wasn't aware that you only support DirectX.

If you want to see the results, you will need to modify QuickGUI::VertexBuffer::_render() function:

p->getTextureUnitState(0)->setTextureFiltering(FO_NONE, FO_NONE, FO_NONE);

If you can confirm this sharpens the image that would be helpful, but the new render system design I had in mind has other benefits as well, and would be worthwhile to implement.

magistr

27-11-2007 12:58:43

Hm.
Ok. I can watch next release.

Please fix this bug.

Best regards!

magistr

02-12-2007 12:27:08

This bug is not fixed in latest SVN version?

kungfoomasta

03-12-2007 17:35:26

Sorry, my schedule did not go as planned, I have way too many things going at once!

Like I've said its not a simple drop in fix. I started on the weekend, and I'm implementing the required changes piece by piece. I shouldn't put down a deadline for this, just know that I'm working on it on a daily basis, and would like it to be completed soon.

This issue isn't a show stopper for you is it? You should be able to work with the somewhat blurred images for now, and later on adjust it to the new changes. :)

kungfoomasta

13-12-2007 04:25:41

Getting close to finished with this! Sorry for the long delay. Need 1-3 days to polish this up. :D

magistr

13-12-2007 10:36:01

O! Very nice. I am test this tonight.
Big Thanks!

SpaceDude

13-12-2007 12:08:29

I seem to remember sinbad mentioning on the main ogre forum that UV coordinates are interpreted slightly differently between OpenGL and DirectX. I'm a bit fuzzy on the details but it was something like one API interprets the UV coordinates as the centre of a pixel while the other interprets it as the top left corner. So that means the texture is offset by half a pixel in one of the APIs. You could test that theory by offsetting the UV coordinates by half a pixel and see if the result is clearer or not.

I think what could be happening is that the texture pixels are offset by half a pixel relative to the screen. So essentially every pixel drawn on the screen is the average of 4 pixels in your texture. But that would only happen if you switched filtering on. By switching filtering off you are forcing it to take only one pixel from the texture.

kungfoomasta

13-12-2007 17:46:57

@magistr, I said I need 1-3 days!! I was just letting you know I have finished the majority of the work, the rest is cleaning it up and preparing to explain to you how you will need to make use of the changes.

@SpaceDude

Very interesting. The OpenGL rendering looks fine, its in DX where it looks blurry. So maybe I should try to modify the UV textures when rendered in DX. If we say the U and V need to be shifted right/down by half a screen pixel, that means I need to add (.5/800) to the U and (.5/600) to the V, in a viewport that is 800 x 600 in size, right?

SpaceDude

13-12-2007 18:44:27

So maybe I should try to modify the UV textures when rendered in DX. If we say the U and V need to be shifted right/down by half a screen pixel, that means I need to add (.5/800) to the U and (.5/600) to the V, in a viewport that is 800 x 600 in size, right?

Yeah, that's what I had in mind... It should be a pretty quick thing to try isn't it?

kungfoomasta

13-12-2007 18:53:32

Yah, just a few lines to add, one at the SkinSet class which provides UV coords, and one for getting UV coords of Glyphs within an Ogre::Font texture. I'm at work so I can't really test this out until the evening.

kungfoomasta

14-12-2007 05:01:30

Actually SpaceDude, wouldn't shifting the uv coords just shift the entire picture by a half pixel amount, instead of increasing the sharpness?

Anyway, I ran out of time today. But I was able to finish the render project! :D

@magistr:

Its done! And here is what you need to do:


QuickGUI::Image* logoImage = mSheet->createImage();
Ogre::MaterialPtr mp = Ogre::MaterialManager::getSingleton().create("temp",Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME);
Ogre::Pass* p = mp->getTechnique(0)->getPass(0);
p->setSceneBlending(Ogre::SBT_TRANSPARENT_ALPHA);
p->setLightingEnabled(false);
p->setCullingMode(Ogre::CULL_CLOCKWISE);
Ogre::TextureUnitState* tus = p->createTextureUnitState("usericon.png");
tus->setTextureFiltering(Ogre::TFO_NONE);
mp->load();
logoImage->setMaterial("temp");


It would be more preferable if you made this material in a *.material file and used it, but if you wanted to do it via code, this is how you would do it.

You can also modify the SkinSet material, if you wanted to remove any texture filtering for that.

magistr

14-12-2007 09:02:06

I have a question.
I am must use this code for All other widget's which use any image?

If I not use this code widget is blurred? But blur is not good effect for gui ^-)

kungfoomasta

14-12-2007 18:56:19

Ogre::String matName = SkinSetManager::getSingleton().getSkin("qgui")->getMaterialName();

Ogre::MaterialPtr mp = Ogre::MaterialManager::getSingleton().getMaterial(matName);

Then just modify the material. If this still doesn't solve your needs, you're welcome to submit a patch.

SpaceDude

15-12-2007 11:26:13

Actually SpaceDude, wouldn't shifting the uv coords just shift the entire picture by a half pixel amount, instead of increasing the sharpness?

It should do both. You'll need to experiment a bit to check that the bounds of the image are still good.

kungfoomasta

15-12-2007 22:41:20

Here is some basic testing I've done, but the image is still blurry.


void Image::setMaterial(const Ogre::String& materialName)
{
if(mTextureLocked)
return;

mMaterialName = materialName;
mQuad->setMaterial(mMaterialName);

Ogre::Real testX = 0.5 / mGUIManager->getViewportWidth();
Ogre::Real testY = 0.5 / mGUIManager->getViewportHeight();

//mQuad->setTextureCoordinates(Ogre::Vector4(testX,testY,1.0 - testX,1.0 - testY));
mQuad->setTextureCoordinates(Ogre::Vector4(0,0,1,1));


I've treid modifying the textureCoordinates with the testX/testY values, but it doesn't help. Anything obvious I'm doing wrong here?

SpaceDude

15-12-2007 23:48:46

Here is some basic testing I've done, but the image is still blurry.


void Image::setMaterial(const Ogre::String& materialName)
{
if(mTextureLocked)
return;

mMaterialName = materialName;
mQuad->setMaterial(mMaterialName);

Ogre::Real testX = 0.5 / mGUIManager->getViewportWidth();
Ogre::Real testY = 0.5 / mGUIManager->getViewportHeight();

//mQuad->setTextureCoordinates(Ogre::Vector4(testX,testY,1.0 - testX,1.0 - testY));
mQuad->setTextureCoordinates(Ogre::Vector4(0,0,1,1));


I've treid modifying the textureCoordinates with the testX/testY values, but it doesn't help. Anything obvious I'm doing wrong here?


I'd try:


mQuad->setTextureCoordinates(Ogre::Vector4(testX,testY,1.0 + testX,1.0 + testY));


If that doesn't work then I'm probably wrong.

kungfoomasta

16-12-2007 06:26:32

Yah, I tried that and didn't see any noticeable affect. :( I guess the best solution is to just disable the texture filtering, for the entire skin, or for the image itself.

SpaceDude

16-12-2007 23:32:26

Ok fair enough, sorry to waste your time.

But I'm not sure disabling filtering entirely is a good thing. Because lets say for example you have a background texture that is 800 x 600 for use as a splash screen and the user is in 1024 x 768 resolution, the texture will look nasty.

PS: Just one last thing, is the texture you where trying it on fullscreen or a small widget? If it was a small widget then you should be getting the width and height of the widget:

Ogre::Real testX = 0.5 / mQuad->getWidth();
Ogre::Real testY = 0.5 / mQuad->getHeight();

kungfoomasta

17-12-2007 16:58:15

SpaceDude, I'll give it a shot when I get a chance, busy weekend. I'm trying to get PhysX working with ETM via my Component Based Design. Are you using ETM in your project? :twisted:

SpaceDude

17-12-2007 17:20:45

SpaceDude, I'll give it a shot when I get a chance, busy weekend. I'm trying to get PhysX working with ETM via my Component Based Design. Are you using ETM in your project? :twisted:

Yeah sure no rush, I'm just interested if it helps or not.

I'm guessing I'm not using ETM in my project because I don't know what you mean by ETM. ? ? ?

kungfoomasta

17-12-2007 17:39:42

Editable Terrain Manager made by CABAListic! I'm using some reference code from JohnJ and have peeked at some nxOgre code, but I can't figure out how to make the terrain into an actor. :( (I am aware of nxOgre obviously, but I want to use PhysX directly)

SpaceDude

17-12-2007 20:19:57

Ah right, well you can create actors out of polygons so that's one way to go. So you can basically write some code that will get the polygons from an Ogre entity and turn that into an actor, it requires quite a bit of code though.

I'm not sure if PhysX has some special classes for dealing with terrain as opposite to a general polygon soup.

kungfoomasta

24-12-2007 21:22:40

SpaceDude,

I finally tried out your suggestion, using the quad dimensions to alter the UV coords. I think it worked! OpenGL is blurry with the altered UV, but DirectX looks good. I guess I'll have to modify my Quad class to alter UV's if DirectX is being used.

I'll apply this now, thanks for the suggestion.

kungfoomasta

24-12-2007 21:43:47

Not sure where I should add this change in. I tried it out and got some really odd results..

First, in the Quad constructor, I check if DirectX render system is used:


// Determine if dirext X renderer used.
if(mRenderSystem->getName().find("Direct") != Ogre::String::npos)
mDirectXUsed = true;


And then in the Quad::setTextureCoordinates method:


if(mDirectXUsed)
{
Ogre::Real xVal = 0.5 / mPixelDimensions.width;
Ogre::Real yVal = 0.5 / mPixelDimensions.height;

mTextureCoordinates = Ogre::Vector4(
textureCoordinates.x + xVal,
textureCoordinates.y + yVal,
textureCoordinates.z + xVal,
textureCoordinates.w + yVal);
}
else
mTextureCoordinates = textureCoordinates;


The only thing that looks normal is the Image widget. The rest have bad UV coords. Any obvious errors here? I have to run now, but I'll try out a few things and see if I can get this working.

SpaceDude

03-01-2008 13:52:20

Oh right, good to see I wasn't completely crazy after all.

I've since come across this document about texture atlases which I found quite informative:

http://download.nvidia.com/developer/SD ... tlases.pdf

There is some information relevant to this offsetting by half a pixel in the section "Using Coordinates in the Zero to One Range". It's worth a read.

Also, rather than adjusting the UV coordinates you could probably achieve the same thing by offsetting the quad on the screen by half a pixel. I don't know which is most convenient and least likely to mess something else up.

SpaceDude

05-01-2008 18:23:12

Ah, I came across this post a while ago and just found it again. Have a look at this:

http://www.ogre3d.org/phpBB2/viewtopic.php?t=35213

You don't need to check whether or not its DX or OpenGL. Just use the functions below:

RenderSystem::getVerticalTexelOffset
RenderSystem::getHorizontalTexelOffset

kungfoomasta

05-01-2008 21:47:55

Thanks! I haven't had time to read the last link you gave me, but I quickly tried messing with the texel offsets, and I'm getting some strange results in DirectX. In OpenGL the texel offsets are zero, so no change there.


void Quad::setTextureCoordinates(const Ogre::Vector4& textureCoordinates)
{
// DirectX interprets UV coords from a different location than OpenGL, so
// we have to modify the coordinates to get the same results.
/* if(mDirectXUsed)
{
Ogre::Real xVal = 0.5 / mPixelDimensions.width;
Ogre::Real yVal = 0.5 / mPixelDimensions.height;

mTextureCoordinates = Ogre::Vector4(
textureCoordinates.x + xVal,
textureCoordinates.y + yVal,
textureCoordinates.z + xVal,
textureCoordinates.w + yVal);
}
else */
// mTextureCoordinates = textureCoordinates;

mTextureCoordinates.x = textureCoordinates.x - mRenderSystem->getHorizontalTexelOffset();
mTextureCoordinates.y = textureCoordinates.y;// + mRenderSystem->getVerticalTexelOffset();
mTextureCoordinates.z = textureCoordinates.z;// + mRenderSystem->getHorizontalTexelOffset();
mTextureCoordinates.w = textureCoordinates.w;// + mRenderSystem->getVerticalTexelOffset();

mTextureCoordsChanged = true;

_clip();
}


I'm in the process of updating the Quad class, I will probably play with this again. Does it seem like simply adding the offset to everything should work? The DX offset is hard coded to -0.5, as you read in the post you linked to me.

SpaceDude

06-01-2008 01:29:43

Yeah I think you still need to adjust for the width of the texture... so something like this may work better:


mTextureCoordinates.x = textureCoordinates.x + mRenderSystem->getHorizontalTexelOffset()/mPixelDimensions.width;
...etc...


Of course I don't know if this will actually work since I don't know what mPixelDimensions represents but it should be something like that I think.

kungfoomasta

09-01-2008 06:00:47

Those dimensions are the dimensions of the Quad being rendered. They didn't work, but this seems to work..


mTextureCoordinates.x = textureCoordinates.x;// + (mRenderSystem->getHorizontalTexelOffset() / 1024);
mTextureCoordinates.y = textureCoordinates.y;// + (mRenderSystem->getVerticalTexelOffset() / 1024);
mTextureCoordinates.z = textureCoordinates.z;// + (mRenderSystem->getHorizontalTexelOffset() / 1024);
mTextureCoordinates.w = textureCoordinates.w;// + (mRenderSystem->getVerticalTexelOffset() / 1024);


You might have guessed, but the SkinSet png file is 1024 x 1024 pixels..

I'll have to see how I can add this in, I'm currently working on updating how quads work, so hopefully this can be added in then.

kungfoomasta

11-01-2008 20:02:12

Ok I thought up a much better solution. The SkinSet class will obtain the texel offset in its constructor, and record the UV coords with texel offsets. 8)

I'll add this in over the weekend, since its such a quick fix. Thanks for helping me out with this, SpaceDude.