atteneder / KtxUnity

Load KTX and Basis Universal textures at runtime
Apache License 2.0
218 stars 42 forks source link

Basis Textures Load Flipped on the Y Axis #18

Closed sybixsus closed 3 years ago

sybixsus commented 3 years ago

All of my Basis textures come in flipped on the Y-axis. I've tested unpacking them with the BasisU executable and they are definitely being created correctly.

In order to test, I created a new project in Unity 2019. I created a screen space canvas and added a UIImage that fills the entire screen. Then I assign my test image (sprite) to the UI image. (See image 1 with the png texture correctly applied.)

BasisFlip1

Then I create a simple class and apply it to the GameObject with the Image component on it.

`public class LoadTexture : TextureFileLoader {

protected override void ApplyTexture(Texture2D T)
{
    GetComponent<Image>().sprite = Sprite.Create(T, new Rect(Vector2.zero, new Vector2(T.width, T.height)), Vector2.zero);
}

}`

I put the basis file in Streaming assets, of course, and I set the file path in the inspector. I hit play and I get the correct image, upside down. (See image 2 with the same texture upside down.)

BasisFlip2

I've tested it across various platforms, inside and outside of the editor and it's always the same. I've also tested loading the texture manually :

`/* private void Awake() { BasisUniversalTexture T = new BasisUniversalTexture(); T.LoadFromStreamingAssets("Shadow.basis", this); T.onTextureLoaded += TextureLoaded; }

protected void TextureLoaded(Texture2D T)
{
    Debug.LogError("Texture Loaded::" + T.width + "," + T.height + "," + T.format);
    GetComponent<Image>().sprite = Sprite.Create(T, new Rect(Vector2.zero, new Vector2(T.width, T.height)), Vector2.zero);
}
*/`

Again, in all instances, the texture is flipped on the Y-axis.

I've uploaded my test project folder (using Unity 2019.3.11f1) in case you find that useful.

https://drive.google.com/file/d/1Gp8Obz_x6ANkGnCWyNUirjI6amVU8X7w/view?usp=sharing

Note: I'm only using Basis textures so I can't say if this affects all texture or just Basis textures.

Let me know if there's anything else you need to reproduce and fix this problem.

DerrickBarra commented 3 years ago

@sybixsus I was planning on reporting the same bug today, good to see others can reproduce this issue.

@atteneder For my tests, I'm using .basis textures created with basisu.exe with default settings (although the same bug occurs on all .basis textures, including the examples used by this repo).

Here are some good test images that show the problem off. mario_transparent.jpg - The original file mario_transparent.basis - The file generated by basisu

If I open mario_transparent.basis in the WebGL testing page that's part of the BinomialLLC/basis_universal repo by downloading the repo, then hosting the WebGL folder using Servez, we can use their web viewer to see the mario_transparent.basis file as it's supposed to be.

image

And in Unity, if I load a .basis using KtxUnity onto a 2D Image renderer on a Canvas, this is what I see.

image

Note: My KTXDemo.cs component is just loading the .basis bytes via a WebRequest and then passing the NativeArray to your plugin, like in the examples and what @sybixsus mentioned earlier.

atteneder commented 3 years ago

Thanks a lot for reporting!

I could reproduce the issue! I'm not sure where the error is introduced (maybe Unity's Texture API), or how/if I can resolve it. I'll investigate more on it later this week.

A workaround would be to counter-act the orientation in the Sprite:

protected override void ApplyTexture(Texture2D T)
    {
        GetComponent<Image>().sprite = Sprite.Create(T, new Rect(new Vector2(0,1), new Vector2(T.width, -T.height)), Vector2.zero);
    }
atteneder commented 3 years ago

This issue seems to confirm the suspicion that Unity has a certain definition of the y-axis reference point and it's not changable at runtime. See:

https://issuetracker.unity3d.com/issues/loadrawtexturedata-loads-dds-textures-upside-down

Also quotes from a Blender issue:

OpenGL and DirectX/DDS have different conventions for UV mapping, putting 0,0 at the bottom and top left respectively. Blender uses the OpenGL convention and converts accordingly, as it does for many image file formats.

further:

According to our code, Blender does make the required vertical flip, while it seems Unity does not

So that means you'd have to encode your basis files with the -y_flip option for Unity.

That's a bummer, because I thought KTX/basis would be truly platform/gameengine/middleware independent.

Still needs more confirmation/investigation.

sybixsus commented 3 years ago

I knew that OpenGL and DX used different world/object coordinate systems (left/right handed) but I was not aware that the UV coordinates were also different between OpenGL and DX.

That does make a lot of sense though. And it would explain why the BasisU.exe comes with an option to flip on the y-axis. In point of fact, I'm already using the -y_flip option as a workaround for my development so it's no hardship for me if this is the ultimate answer.

Thanks for looking into it!

atteneder commented 3 years ago

No problem.

The remaining question is:

Can KtxUnity provide a consistent orientation (in-data), regardless of the -y_flip option or is it up to its user to handle that?

My current understanding is that it can't and KtxUnity has to expose the y_flip flag (or the KTXorientation for ktx files) so that the user can properly alter UVs/texture scale/Sprite coordinates to compensate for it.

Once I'm certain, I'll comment and close this issue.

Thanks!

atteneder commented 3 years ago

Further information on this topic:

PR about a transcode time y-flip in BasisU ETC1S issue about a transcode time y-flip in BasisU UASTC

Seems that it's possible, but not implemented atm.

atteneder commented 3 years ago

TLDR: With version 0.8.0 textures will still be flipped, but you get the orientation and can counter-act reliably now.

Read the updated README on the projects page for details.

Hope that helps