Compare commits
1 Commits
pf/vk-bett
...
docs-updat
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f0a3b10681 |
Binary file not shown.
|
Before Width: | Height: | Size: 83 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 35 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 38 KiB |
4
docs_src/.gitignore
vendored
4
docs_src/.gitignore
vendored
@@ -3,3 +3,7 @@ src_mdbook/book
|
||||
src_mdbook/src/dup/*.md
|
||||
src_mdbook/src/main/filament.md
|
||||
src_mdbook/src/main/materials.md
|
||||
src_mdbook/src/samples/web/*.md
|
||||
src_mdbook/src/web
|
||||
src_mdbook/src/release/cocoapods.md
|
||||
src_mdbook/src/remote
|
||||
@@ -23,23 +23,18 @@
|
||||
- [skinning](./samples/web/skinning.md)
|
||||
- [sky](./samples/web/sky.md)
|
||||
- [Technical Notes](./notes/README.md)
|
||||
- [Material Properties](./notes/material_properties.md)
|
||||
- [Release](./release/README.md)
|
||||
- [CocoaPods](./release/cocoapods.md)
|
||||
- [Maven](./release/maven.md)
|
||||
- [Github](./release/guide.md)
|
||||
- [Versioning](./release/versioning.md)
|
||||
- [Branching](./release/branching.md)
|
||||
- [Documentation](./dup/docs.md)
|
||||
- [Debugging](./notes/debugging.md)
|
||||
- [Metal](./notes/metal_debugging.md)
|
||||
- [Vulkan](./notes/vulkan_debugging.md)
|
||||
- [SPIR-V](./notes/spirv_debugging.md)
|
||||
- [Running with ASAN and UBSAN](./notes/asan_ubsan.md)
|
||||
- [Using Instruments on macOS](./notes/instruments.md)
|
||||
- [Code coverage analysis](./notes/coverage.md)
|
||||
- [Performance analysis](./notes/performance_analysis.md)
|
||||
- [ASAN and UBSAN](./notes/asan_ubsan.md)
|
||||
- [Instruments on macOS](./notes/instruments.md)
|
||||
- [Code coverage](./notes/coverage.md)
|
||||
- [Performance](./notes/performance_analysis.md)
|
||||
- [Documentation](./dup/docs.md)
|
||||
- [Material Properties](./notes/material_properties.md)
|
||||
- [Framegraph](./notes/framegraph.md)
|
||||
- [2D Coordinate Spaces](./notes/2d_coordinate_spaces.md)
|
||||
- [Tests](./notes/tests.md)
|
||||
- [backend](./dup/backend_test.md)
|
||||
- [CI: backend](./dup/test_ci_backend.md)
|
||||
@@ -66,3 +61,9 @@
|
||||
- [specgen](./dup/specgen.md)
|
||||
- [specular-color](./dup/specular_color.md)
|
||||
- [zbloat](./dup/zbloat.md)
|
||||
- [Release](./release/README.md)
|
||||
- [CocoaPods](./release/cocoapods.md)
|
||||
- [Maven](./release/maven.md)
|
||||
- [Github](./release/guide.md)
|
||||
- [Versioning](./release/versioning.md)
|
||||
- [Branching](./release/branching.md)
|
||||
|
||||
221
docs_src/src_mdbook/src/notes/2d_coordinate_spaces.md
Normal file
221
docs_src/src_mdbook/src/notes/2d_coordinate_spaces.md
Normal file
@@ -0,0 +1,221 @@
|
||||
# 2D Coordinate Spaces
|
||||
|
||||
This page describes Filament's interpretation of 2D coordinate spaces. We address the differences
|
||||
between graphics APIs and provide explanations for custom logic and client APIs.
|
||||
|
||||
## 1. Texture coordinate space
|
||||
|
||||
To begin our discussion, we imagine looking at a rectangular image, like a photograph. To be able to
|
||||
describe to another person where things are on the photograph (say, a **tree**), we need to first
|
||||
define a consistent coordinate system that both parties understand. A natural choice is to pick one
|
||||
of the corners as the origin of the coordinate system (that is, corners are easy to identify on a
|
||||
rectangle, and do not require tools - like a ruler - for positioning). We can further refine this
|
||||
coordinate system by assigning 2D numbers to the four corners so that we can accurately
|
||||
describe the positions on the photograph.
|
||||
|
||||
So for example, we assign the corners in clockwise order, starting from the top-left corner:
|
||||
\\((0, 0)\\), \\((1, 0)\\), \\((1, 1)\\), \\((0, 1)\\).
|
||||
|
||||
Assuming we are sampling from a texture in a shader, we sample by using a 2D coordinate (UV)
|
||||
to identify a point on the rectangular space of the texture. We can make a statement such as, "the
|
||||
**tree** is near the point of \\(((0.65, 0.35)\\))", to communicate the position of a tree in
|
||||
the picture to another person.
|
||||
|
||||
Given the description above, we conclude this section with the following statements
|
||||
- Programmers communicate with computers using coordinate systems like the above to draw 2D images
|
||||
onto a display.
|
||||
- In 3D graphics, similar coordinate systems are used to texturize 3D models with 2D images. The
|
||||
2D images are called *textures*.
|
||||
- Filament defines its coordinate system using the *top-left* of the image as \\((0, 0)\\) and
|
||||
the *bottom-right* is \\((1, 1)\\).
|
||||
|
||||
### 1.1. OpenGL (The Bottom-Left API)
|
||||
|
||||
OpenGL has its texture coordinate origin \\((0, 0)\\) at the **bottom-left**, and
|
||||
\\((1, 1)\\) is at the top-right. Note that this differs from Filament's assumed texture
|
||||
coordinate system.
|
||||
|
||||
The GL function for uploading texture is `glTexImage2D`. Even though GL interprets \\((0, 0)\\) as
|
||||
the bottom-left of a 2D image, `glTexImage2D` uploads the lower-bits as the *bottom* of the image
|
||||
as opposed to the *top*. (This behavior is opposite of the other backends' texture upload
|
||||
APIs). The two differences cancel each other, meaning there is no difference between the different
|
||||
backends in upload and sampling of textures.
|
||||
|
||||
We can restate the above as
|
||||
|
||||
| |
|
||||
|---|
|
||||
| *Filament's internal texture coordinate space defines the **top-left** corner of the image as the origin.* |
|
||||
|
||||
### 1.2. Readback of rendertarget
|
||||
|
||||
While we've established that the APIs are not any different in the *uploading* and *sampling* of
|
||||
textures for Filament's backends, there are, unfortunately, cases where special handling of
|
||||
texture coordinate is necessary.
|
||||
|
||||
Reading the result of rendering into CPU memory can be very useful; for example, one might want to
|
||||
take snapshots of the rendering and output to image formats such as PNG or JPEG. Another use is to
|
||||
perform image analysis or processing over a rendered image. In Filament, the readback path
|
||||
corresponds to the `readPixels`. In the OpenGL backend, this is implemented using the `glReadPixels`
|
||||
function. GL's interpretation of texture coordinate space holds true for `glReadPixels`; this means
|
||||
that it treats the bottom-left of the image as the origin and the readback output *starts* from the
|
||||
origin. Hence, the first row of the image in CPU memory is the bottom-most row of the image (when
|
||||
displayed).
|
||||
|
||||
This creates two related inconsistencies:
|
||||
- First, (not a real use case), if one were to upload a texture, attach it as an attachment, and
|
||||
read the attachment, the GL backend would place the bottom of the image as the first row of
|
||||
pixels in CPU memory.
|
||||
- Second, the other backends consider the top-left corner as the origin, which means their
|
||||
readbacks would place the top of the image as the first row of the image in CPU memory.
|
||||
|
||||
To account for these inconsistencies, *the GL backend vertically flips the rows of the readback
|
||||
image*.
|
||||
|
||||
### 1.3. `flipUV` in Materials
|
||||
|
||||
In order to work with textures and associated primitives from different sources, Filament provides
|
||||
a simple, material-level client-set flag to flip the v-direction.
|
||||
|
||||
For example, if a 3D modeler
|
||||
exported a mesh with textures such that the bottom-left corner of the texture is \\(u=0, v=0\\) in
|
||||
texture space, then this is not consistent with Filament's assumption. Luckily, modern modelers will
|
||||
only choose either the top-left corner or the bottom-right corner as the origin. To address this
|
||||
inconsistency, Filament provides `flipUV` in the material definition. Setting this flag to true will
|
||||
flip the UV coordinate in the vertex attributes so that \\(v' = 1 - v\\), and the texture
|
||||
coordinate interpretation is then consistent with Filament's internal coordinates.
|
||||
|
||||
## 2. Clip-space
|
||||
|
||||
The clip-space is the bounded 3D space that a primitive vertex is transformed into at the end of a
|
||||
vertex shader. Abstractly, the XY-plane of the clip-space is parallel to the display, and its
|
||||
bounds correspond to the bounds of the window, surface, or buffer that we are rendering into. (This
|
||||
API-specific bounded space is also known as Normalized Device Coordinates or NDC).
|
||||
In different graphics API, the bounds and orientation of the NDC is also inconsistent. For all of
|
||||
the backends, the ranges of the XY-plane \\([-1, 1]\\) and the origin (\\((0, 0)\\)) are the
|
||||
same. However, the depth range and the Y-axis directions are not the same.
|
||||
|
||||
| Graphics API | Depth Range (Z) | Y-Axis Direction | Frame Buffer Origin |
|
||||
| -------------- | --------------- | ---------------- | ------------- |
|
||||
| OpenGL / WebGL | \\([−1, 1]\\) | Up | bottom-left |
|
||||
| Metal | \\([0, 1]\\) | Up | top-left |
|
||||
| WebGPU | \\([0, 1]\\) | Up | top-left |
|
||||
| Vulkan | \\([0, 1]\\) | Down | top-left |
|
||||
|
||||
(We will defer the discussion of GL's depth range difference to section 2.3.)
|
||||
|
||||
### 2.1. Vulkan y-axis correction
|
||||
|
||||
The XY ranges for all backends are the same, but Vulkan interprets the Y-Axis direction as
|
||||
"Down", meaning that \\(y=1\\) is at the bottom of the image. To account for this difference,
|
||||
Filament flips the projected \\(y\\) coordinate in the vertex shader for Vulkan
|
||||
|
||||
```c++
|
||||
#if defined(TARGET_VULKAN_ENVIRONMENT)
|
||||
// In Vulkan, clip space is Y-down. In OpenGL and Metal, clip space is Y-up.
|
||||
position.y = -position.y;
|
||||
#endif
|
||||
```
|
||||
|
||||
### 2.2 Fix Frame buffer origin difference with `uvToRenderTargetUV`
|
||||
|
||||
After the workaround in the above section, we can rewrite the table thus
|
||||
|
||||
| Graphics API | Y-Axis Direction | Frame Buffer Origin |
|
||||
| -------------- | ---------------- | ------------------- |
|
||||
| OpenGL / WebGL | Up | bottom-left |
|
||||
| Metal | Up | top-left |
|
||||
| WebGPU | Up | top-left |
|
||||
| Vulkan | Up (corrected) | top-left |
|
||||
|
||||
We can rephrase the Y-Axis Direction column to say that
|
||||
|
||||
| |
|
||||
|---|
|
||||
| *Filament's internal clip-space defines the Y-axis direction as **UP** (i.e. the physical world up direction pointing to \\(+1\\)).* |
|
||||
|
||||
Now consider the "Frame Buffer Origin" column. This column identifies the origin of the rendered
|
||||
image in the context of sampling by subsequent passes.
|
||||
|
||||
Assume
|
||||
- We have two passes \\(A\\) and \\(B\\)
|
||||
- \\(A\\) renders to a render target that attaches texture \\(T\\)
|
||||
- \\(B\\) renders a full-screen quad
|
||||
- In \\(B\\), top-left and bottom-right corners of the quad are assigned texturing UV coordinates
|
||||
\\((0, 0)\\) and \\((1, 1)\\), respectively.
|
||||
- The quad in \\(B\\) samples texture \\(T\\).
|
||||
|
||||
For GL, having the *bottom-left* corner as the origin means that rendering a pixel to clip-space
|
||||
\\(x=-1, y=-1\\) in \\(A\\) will output to pixel at \\(u=0, v=0\\) of \\(T\\). Note that GL
|
||||
clip-space's y-axis direction is consistent with the texture space v-axis direction. That is, in
|
||||
pass \\(B\\), sampling from \\(v=0\\) to \\(v=1\\) (moving in the positive direction) maps to going
|
||||
from \\(y=-1\\) to \\(y=1\\) in clip-space (also moving in positive direction).
|
||||
|
||||
This behavior is opposite for all other backends; sampling from \\(v=0\\) to \\(v=1\\) will map to
|
||||
clip-space movement from \\(y=1\\) to \\(y=-1\\) (going in the negative direction).
|
||||
|
||||
To account for this, Filament provides the following shader method that clients can call to
|
||||
correctly sample from a texture that was the rendertarget of a previous pass.
|
||||
|
||||
```c++
|
||||
highp vec2 uvToRenderTargetUV(const highp vec2 uv) {
|
||||
#if defined(TARGET_METAL_ENVIRONMENT) || defined(TARGET_VULKAN_ENVIRONMENT) || defined(TARGET_WEBGPU_ENVIRONMENT)
|
||||
return vec2(uv.x, 1.0 - uv.y);
|
||||
#else
|
||||
return uv;
|
||||
#endif
|
||||
}
|
||||
```
|
||||
|
||||
It's meant to be used like so
|
||||
|
||||
```c++
|
||||
vec2 uv0 = uvToRenderTargetUV(getUV0());
|
||||
material.baseColor = texture(materialParams_baseColor, uv0);
|
||||
```
|
||||
|
||||
### 2.3. Clip-space depth adjustments for OpenGL
|
||||
|
||||
While the clip-space depth (or Z) dimension does not pertain to the 2D coordinate spaces that we have
|
||||
been discussing, the concept in this section parallels the space adjustment logic in the other
|
||||
sections. Hence, we have included it here.
|
||||
|
||||
Consider again this table
|
||||
|
||||
| Graphics API | Depth Range (Z) |
|
||||
| -------------- | --------------- |
|
||||
| OpenGL / WebGL | \\([−1, 1]\\) |
|
||||
| Metal | \\([0, 1]\\) |
|
||||
| WebGPU | \\([0, 1]\\) |
|
||||
| Vulkan | \\([0, 1]\\) |
|
||||
|
||||
For OpenGL, the NDC depth dimension ranges from \\(-1\\) to \\(1\\). Filament implements two
|
||||
paths to adjust this so that the internal clip-space definition remains consistent:
|
||||
|
||||
1. Use the `EXT_clip_control` extension to change the depth range to \\([0, 1]\\).
|
||||
|
||||
```c++
|
||||
#if !defined(__EMSCRIPTEN__)
|
||||
if (ext.EXT_clip_control) {
|
||||
#if defined(BACKEND_OPENGL_VERSION_GL)
|
||||
glClipControl(GL_LOWER_LEFT, GL_ZERO_TO_ONE);
|
||||
#elif defined(GL_EXT_clip_control)
|
||||
glClipControlEXT(GL_LOWER_LEFT_EXT, GL_ZERO_TO_ONE_EXT);
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
```
|
||||
|
||||
2. For cases where the extension is not available, the following shader code will convert the
|
||||
internal (or noted as virtual in the code) coordinates to GL's expected depth coordinates.
|
||||
|
||||
```c++
|
||||
#if !defined(TARGET_VULKAN_ENVIRONMENT) && !defined(TARGET_METAL_ENVIRONMENT) && !defined(TARGET_WEBGPU_ENVIRONMENT)
|
||||
// This is not needed in Vulkan, Metal or WebGPU because clipControl is always (1, 0)
|
||||
position.z = position.z * frameUniforms.clipControl.x
|
||||
+ position.w * frameUniforms.clipControl.y;
|
||||
#endif
|
||||
```
|
||||
|
||||
This code uses a vector in the uniform buffer to scale and translate the resulting depth. This
|
||||
vector is written in runtime (so that we'd know whether the extension is present or not).
|
||||
Reference in New Issue
Block a user