Skip to content

docs: document 2D coordinate spaces#9847

Open
poweifeng wants to merge 1 commit intomainfrom
docs-update-readpixels
Open

docs: document 2D coordinate spaces#9847
poweifeng wants to merge 1 commit intomainfrom
docs-update-readpixels

Conversation

@poweifeng
Copy link
Copy Markdown
Contributor

No description provided.

@poweifeng poweifeng added the internal Issue/PR does not affect clients label Mar 27, 2026

### OpenGL (The Bottom-Left API)

OpenGL is unique because its texture coordinate system origin \\((0, 0)\\) is at the **bottom-left**.
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

that's not "unique". That's actually typically how we learn math in school. Usually when you have a piece of paper, you naturally put the origin at the bottom left.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

removed and rephrased.


OpenGL is unique because its texture coordinate system origin \\((0, 0)\\) is at the **bottom-left**.

- **Texture Upload:** To ensure texture sampling works "normally" (where $v=0$ is the top), images are typically flipped during upload. This results in the texture being stored **upside-down** in GPU memory.
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No I think this is confusing.
Images are not technically "flipped", it's just that the opengl spec defines glTexImage2D's data pointer to be ordered with the bottom of the image at the low addresses.

This is contrary to how images are typically stored on a computer. So, yes, you have to "flip" your "Typical" bitmap if you use glTexImage2D and you want the bottom of the image to be at v=0.

We don't know how the image is stored in the GPU's memory (because in GL you can't read that buffer), so it's no correc to say that it's stored "upside down".

Copy link
Copy Markdown
Collaborator

@pixelflinger pixelflinger Mar 30, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note that from a user's point of view, GL, VK and Metal are consistent.

i.e. if you do a glTexImage2D without inverting the texture, in all cases you end-up with the origin at the top of the image. No changes needed in the shader. it's just that "conceptually* in GL the image is "upside down" (whatever that means) and the y axis goes "up" (whatever that means). Whereas is Metal/VK the image is right side up, and the y-axis goes down.

So... unintuitively, you do not have to do anything special to have your textures uploads and texture coords work on all platforms.

The problem only arises with render targets and is compounded when you upload data into a texture (as opposed to drawing into it) and use it as an attachment and readpixels from it.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I say "whatever that means" because the definition of "up" is the direction of the Y-axis.

OpenGL is unique because its texture coordinate system origin \\((0, 0)\\) is at the **bottom-left**.

- **Texture Upload:** To ensure texture sampling works "normally" (where $v=0$ is the top), images are typically flipped during upload. This results in the texture being stored **upside-down** in GPU memory.
- **Standard `glReadPixels`:** Because OpenGL reads from the bottom-left, a standard `glReadPixels` call effectively flips the data back, resulting in an **upright** image in the CPU buffer.
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another way to say it is the glReadPIxel is consistent with glTexImage2D.
Neither operation is flipping the image you give it.
glReadPixel just transfers line 0, 1, 2... (with 0 at the bottom of the screen) in order.

Note that typically you don't do a glTexImage2D, then bind the texture as an attachment and then do a readPixels().

This exact combination of operation is inconsistant in Filament (because filament actually flips the result of readPixels in GL). This was a compromise we had to do, to be compatible with other APIs.


- **Texture Upload:** To ensure texture sampling works "normally" (where $v=0$ is the top), images are typically flipped during upload. This results in the texture being stored **upside-down** in GPU memory.
- **Standard `glReadPixels`:** Because OpenGL reads from the bottom-left, a standard `glReadPixels` call effectively flips the data back, resulting in an **upright** image in the CPU buffer.
- **Filament's Handling:** To maintain consistency or specific internal alignment, the Filament OpenGL backend performs an additional **CPU flip** after the read. This results in a final buffer where the image is **upside-down** relative to the original source.
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The reason for this is so that given a framebuffer (e.g. you draw a triangle into it), and you do a readpixels, the result is the same on all APIs. (with the "Typical" bitmap layout, low address are the top of the image).

This however "breaks" the consistency of uploading data into a texture and then reading it back with readPixel with the texture as an attachment. This is actually the only situation in which filament doesn't work the same across all backends!

Metal, Vulkan, and WebGPU use a \\((0, 0)\\) **top-left** coordinate system, which aligns more closely with standard image memory layouts.

- **Texture Upload:** No flip is required. The image is stored in GPU memory in its **upright** orientation.
- **Filament `ReadPixels`:** The data is read directly. Since the GPU storage and the memory layout both favor top-left, the resulting CPU buffer is **upright**.
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, however all these APIs are inconsistent imho. Because their clip-space puts the origin at the bottom left! so in these APIs the clip space is "inverted".

And remove the pngs that documented the same thing.
@poweifeng poweifeng force-pushed the docs-update-readpixels branch from 0985c4c to bb73db2 Compare April 9, 2026 19:27
@poweifeng poweifeng changed the title docs: upload and read pixels vs. backend docs: document 2D coordinate spaces Apr 13, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

internal Issue/PR does not affect clients

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants