Skip to content

Commit 365a13f

Browse files
committed
Merge branch 'main-mistake' into vendor-extensions
2 parents a0879a5 + 049c8c4 commit 365a13f

4 files changed

Lines changed: 177 additions & 0 deletions

File tree

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
<!--
2+
Copyright 2025 Bentley Systems, Incorporated
3+
SPDX-License-Identifier: CC-BY-4.0
4+
-->
5+
6+
# EXT_textureInfo_constant_lod
7+
8+
## Contributors
9+
10+
* Paul Connelly, Bentley Systems, [@pmconne](https://github.com/pmconne)
11+
* Erin Ingram, Bentley Systems, [@eringram](https://github.com/eringram)
12+
* Mark Schlosser, Bentley Systems, [@markschlosseratbentley](https://github.com/markschlosseratbentley)
13+
* Daniel Zhong, Bentley Systems, [@danielzhong](https://github.com/danielzhong)
14+
15+
## Status
16+
17+
Complete
18+
19+
## Dependencies
20+
21+
Written against the glTF 2.0 spec.
22+
23+
## Overview
24+
25+
Constant level-of-detail ("LOD") is a technique of texture coordinate generation which dynamically calculates texture coordinates to maintain a consistent texel-to-pixel ratio on screen, regardless of camera distance. As the camera zooms in or out, the texture coordinates are recalculated so that the texture pattern remains at approximately the same visual scale. The transition between scale levels is smoothly blended to avoid abrupt changes.
26+
27+
The `EXT_textureInfo_constant_lod` extension defines properties needed to calculate these dynamic texture coordinates: the number of times the texture is repeated per meter, an offset to shift the texture, and the minimum and maximum distance for which to clamp the texture. The minimum and maximum clamp distance control at which point the texture sizes shall be blended. These images illustrate the textures blending to create the constant LOD effect:
28+
29+
![Constant LOD gif](./figures/constantlod.gif "Constant LOD gif")
30+
31+
![Constant LOD image](./figures/constantlod.jpg "Constant LOD image")
32+
33+
The extension specifies an alternative way of computing the texture coordinates, so if it is supported by the client then the `textureInfo`'s `texCoord` is not used. For maximum compatibility however, encoders still must provide `texCoord` UV coordinates so the texture renders even if the extension is not supported.
34+
35+
## Specifying Constant LOD Texture Mapping
36+
37+
The `EXT_textureInfo_constant_lod` extension is defined on `textureInfo` structures. When that `textureInfo` is used by a material, this extension applies the constant LOD technique to the specified texture.
38+
39+
Constant LOD uses the following properties:
40+
41+
* `repetitions` - specifies the number of times the texture is repeated per meter in both the X and Y dimensions. Increasing this will make the texture pattern appear smaller; decreasing it will make it appear larger.
42+
* `offset` - used to shift the texture, specified as a pair of numbers in meters in the format [X, Y].
43+
* `minClampDistance` - specifies the minimum distance in meters from the camera to the surface at which to clamp the texture. This value must not be greater than `maxClampDistance`.
44+
* `maxClampDistance` - specifies the maximum distance in meters from the camera to the surface at which to clamp the texture. This value must not be less than `minClampDistance`.
45+
46+
For example, the following JSON defines a material with a texture at index 0 that is modified by the `EXT_textureInfo_constant_lod` extension. The extension has a `repetitions` value of 2 causing it to be repeated twice per meter, making its pattern appear half the size. It is shifted by 1 meter in the X direction and 0 meters in the Y direction. It also has a minimum clamp distance of 0.5 meters, meaning the constant LOD effect stops being present when the camera is 0.5m away from the surface or closer. This is a closer distance than the default value of 1m; therefore, this material requires a closer zoom before the texture stops adapting its level of detail (and for it to appear magnified). The absence of the `maxClampDistance` property means it has a default value of $2^{32}$, so the constant LOD effect will occur until the camera is $2^{32}$ away from the surface.
47+
48+
```json
49+
"materials": [
50+
{
51+
"pbrMetallicRoughness": {
52+
"baseColorTexture": {
53+
"index": 0,
54+
"texCoord": 0,
55+
"extensions": {
56+
"EXT_textureInfo_constant_lod": {
57+
"repetitions": 2,
58+
"offset": [1, 0],
59+
"minClampDistance": 0.5
60+
}
61+
}
62+
},
63+
"baseColorFactor": [1.0, 1.0, 1.0, 1.0],
64+
"metallicFactor": 0.0,
65+
"roughnessFactor": 1.0
66+
}
67+
}
68+
],
69+
```
70+
71+
## Implementation Notes
72+
73+
The `EXT_textureInfo_constant_lod` extension may be present on any `textureInfo` object, including those that extend `textureInfo` such as `normalTextureInfo`. Encoders and clients may decide to implement the extension such that the normal texture has identical constant LOD properties to the base color texture to produce a synchronized blending of levels-of-detail, although this is not required.
74+
75+
### Formulas
76+
77+
This section outlines the formulas that shall be used by implementations to calculate the dynamic texture coordinates for constant LOD.
78+
79+
In the vertex shader:
80+
81+
```math
82+
\begin{aligned}
83+
customUvCoords.xy &= worldPosition.xy + offset\\
84+
customUvCoords.z &= \begin{cases}
85+
-eyeSpace & \text{if } isPerspectiveProjection \\
86+
frustrumWidth & \text{if } isOrthographicProjection \\
87+
\end{cases}
88+
\end{aligned}
89+
```
90+
91+
Where $worldPosition$ is the vertex position in world coordinates, $eyeSpace$ is the vertex position in camera coordinates, and $frustumWidth$ is the frustum width in meters.
92+
93+
The resulting $customUvCoords.xy$ contain the vertex's X and Y position in world coordinates. $customUvCoords.z$ is the negative eye-space depth (z-coordinate) from the camera to the fragment when using perspective projection. Since all points are equidistant to the camera when using orthographic projection, this value shall be set to the frustum width as a proxy for zoom level. $customUvCoords$ should then be passed into the fragment shader as a `varying`.
94+
95+
In the fragment shader:
96+
97+
```math
98+
\begin{aligned}
99+
(x, y, z) &= customUvCoords\\
100+
minDistance &= min(clampDistance) \\
101+
maxDistance &= max(clampDistance) \\
102+
logDepth &= \log_{2}{z}\\
103+
textureCoordinates_1 &= \frac{xy}{clamp(2^{\lfloor logDepth \rfloor}, minDistance, maxDistance)} \cdot repetitions\\
104+
textureCoordinates_2 &= \frac{xy}{clamp(2^{\lfloor logDepth \rfloor + 1}, minDistance, maxDistance)} \cdot repetitions\\
105+
result &= mix(textureCoordinates_1, textureCoordinates_2, fract(logDepth))
106+
\end{aligned}
107+
```
108+
109+
*Non-normative note: the addition of $`\lfloor logDepth \rfloor + 1`$ when calculating $`textureCoordinates_2`$ allows the result of the $`mix`$ function to blend between two adjacent mipmap levels.*
110+
111+
Where $clamp(x, minVal, maxVal)$ constrains the value of $x$ to the range of $minVal$ to $maxVal$, defined by the [GLSL definition](https://registry.khronos.org/OpenGL-Refpages/gl4/html/clamp.xhtml) of:
112+
113+
```math
114+
\min(\max(x, minVal), maxVal)
115+
```
116+
117+
$fract(x)$ returns the fractional part of $x$, defined by the [GLSL definition](https://registry.khronos.org/OpenGL-Refpages/gl4/html/fract.xhtml) of:
118+
119+
```math
120+
x - \lfloor x \rfloor
121+
```
122+
123+
and $mix(x, y, a)$ performs a linear interpolation between $x$ and $y$ using $a$ to weight between them, using the [GLSL definition](https://registry.khronos.org/OpenGL-Refpages/gl4/html/mix.xhtml) of:
124+
125+
```math
126+
x \cdot (1 − a) + y \cdot a
127+
```
128+
129+
## JSON Schema
130+
131+
* [textureInfo.EXT_textureInfo_constant_lod.schema.json](schema/textureInfo.EXT_textureInfo_constant_lod.schema.json)
132+
133+
## Known Implementations
134+
135+
* [iTwin.js](https://github.com/iTwin/itwinjs-core/pull/8882)
136+
* [CesiumJS](https://github.com/CesiumGS/cesium/pull/13121)
981 KB
Loading
47.2 KB
Loading
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
{
2+
"$schema": "https://json-schema.org/draft/2020-12/schema",
3+
"title": "EXT_textureInfo_constant_lod TextureInfo Extension",
4+
"type": "object",
5+
"description": "Enables dynamically calculated UV coordinates to keep the texture near a constant level of detail",
6+
"allOf": [
7+
{ "$ref": "glTFProperty.schema.json" }
8+
],
9+
"properties": {
10+
"repetitions": {
11+
"type": "number",
12+
"description": "The number of times the texture is repeated per meter.",
13+
"exclusiveMinimum": 0.0,
14+
"default": 1.0
15+
},
16+
"offset": {
17+
"type": "array",
18+
"description": "The offset in meters used to shift the texture.",
19+
"items": {
20+
"type": "number"
21+
},
22+
"minItems": 2,
23+
"maxItems": 2,
24+
"default": [ 0.0, 0.0 ]
25+
},
26+
"minClampDistance": {
27+
"type": "number",
28+
"description": "The minimum distance in meters from the eye to the surface at which to clamp the texture.",
29+
"minimum": 0.0,
30+
"default": 1.0
31+
},
32+
"maxClampDistance": {
33+
"type": "number",
34+
"description": "The maximum distance in meters from the eye to the surface at which to clamp the texture.",
35+
"exclusiveMinimum": 0.0,
36+
"default": 4294967296.0
37+
},
38+
"extensions": { },
39+
"extras": { }
40+
}
41+
}

0 commit comments

Comments
 (0)