three.js: GLTF Orthographic camera size seems to be handled incorrectly.

Describe the bug

Per spec, camera.orthographic.xmag and .ymag should be half the view window size, I think:

https://github.com/KhronosGroup/glTF/blob/main/specification/2.0/Specification.adoc#31034-orthographic-projection

==== Orthographic projection

Let

- `r` be half the orthographic width, set by `camera.orthographic.xmag`;
- `t` be half the orthographic height, set by `camera.orthographic.ymag`;
- `f` be the distance to the far clipping plane, set by `camera.orthographic.zfar`;
- `n` be the distance to the near clipping plane, set by `camera.orthographic.znear`.

The loader code looks right:

https://github.com/mrdoob/three.js/blob/46ccd684a05161dea72119b497057e417eb1ebb6/examples/js/loaders/GLTFLoader.js#L3360-L3364

https://github.com/mrdoob/three.js/blob/3d1c5b8aaad7f64f81322f778a6561d04efa5b77/docs/api/en/cameras/OrthographicCamera.html#L47-L55

But the results don’t:

Screenshots

image

image

Each square is one unit.

To Reproduce

Steps to reproduce the behavior:

  • Save and open the GLTF file below.

Note: The Khronos Group reference implementation looks like Three.JS, while Blender looks like Babylon.js (Okay. That’s actually not implemented for Blender, and the default just happened to be what I was expecting by hilarious coincidence.). So… Not 100% sure I’m interpreting the spec correctly, but either way something isn’t lining up here.

OrthoScale.gltf
{
    "asset" : {
        "generator" : "Khronos glTF Blender I/O v3.3.32",
        "version" : "2.0"
    },
    "scene" : 0,
    "scenes" : [
        {
            "name" : "Scene",
            "nodes" : [
                0,
                36
            ]
        }
    ],
    "nodes" : [
        {
            "camera" : 0,
            "name" : "Camera",
            "rotation" : [
                -0.7071068286895752,
                0,
                0,
                0.7071068286895752
            ],
            "scale" : [
                2,
                1.9999998807907104,
                1
            ],
            "translation" : [
                6,
                2,
                -4
            ]
        },
        {
            "mesh" : 0,
            "name" : "Plane"
        },
        {
            "mesh" : 0,
            "name" : "Plane",
            "translation" : [
                2,
                0,
                0
            ]
        },
        {
            "mesh" : 0,
            "name" : "Plane",
            "translation" : [
                4,
                0,
                0
            ]
        },
        {
            "mesh" : 0,
            "name" : "Plane",
            "translation" : [
                6,
                0,
                0
            ]
        },
        {
            "mesh" : 0,
            "name" : "Plane",
            "translation" : [
                8,
                0,
                0
            ]
        },
        {
            "mesh" : 0,
            "name" : "Plane",
            "translation" : [
                10,
                0,
                0
            ]
        },
        {
            "mesh" : 0,
            "name" : "Plane",
            "translation" : [
                12,
                0,
                0
            ]
        },
        {
            "mesh" : 0,
            "name" : "Plane",
            "translation" : [
                0,
                0,
                -2
            ]
        },
        {
            "mesh" : 0,
            "name" : "Plane",
            "translation" : [
                2,
                0,
                -2
            ]
        },
        {
            "mesh" : 0,
            "name" : "Plane",
            "translation" : [
                4,
                0,
                -2
            ]
        },
        {
            "mesh" : 0,
            "name" : "Plane",
            "translation" : [
                6,
                0,
                -2
            ]
        },
        {
            "mesh" : 0,
            "name" : "Plane",
            "translation" : [
                8,
                0,
                -2
            ]
        },
        {
            "mesh" : 0,
            "name" : "Plane",
            "translation" : [
                10,
                0,
                -2
            ]
        },
        {
            "mesh" : 0,
            "name" : "Plane",
            "translation" : [
                12,
                0,
                -2
            ]
        },
        {
            "mesh" : 0,
            "name" : "Plane",
            "translation" : [
                0,
                0,
                -4
            ]
        },
        {
            "mesh" : 0,
            "name" : "Plane",
            "translation" : [
                2,
                0,
                -4
            ]
        },
        {
            "mesh" : 0,
            "name" : "Plane",
            "translation" : [
                4,
                0,
                -4
            ]
        },
        {
            "mesh" : 0,
            "name" : "Plane",
            "translation" : [
                6,
                0,
                -4
            ]
        },
        {
            "mesh" : 0,
            "name" : "Plane",
            "translation" : [
                8,
                0,
                -4
            ]
        },
        {
            "mesh" : 0,
            "name" : "Plane",
            "translation" : [
                10,
                0,
                -4
            ]
        },
        {
            "mesh" : 0,
            "name" : "Plane",
            "translation" : [
                12,
                0,
                -4
            ]
        },
        {
            "mesh" : 0,
            "name" : "Plane",
            "translation" : [
                0,
                0,
                -6
            ]
        },
        {
            "mesh" : 0,
            "name" : "Plane",
            "translation" : [
                2,
                0,
                -6
            ]
        },
        {
            "mesh" : 0,
            "name" : "Plane",
            "translation" : [
                4,
                0,
                -6
            ]
        },
        {
            "mesh" : 0,
            "name" : "Plane",
            "translation" : [
                6,
                0,
                -6
            ]
        },
        {
            "mesh" : 0,
            "name" : "Plane",
            "translation" : [
                8,
                0,
                -6
            ]
        },
        {
            "mesh" : 0,
            "name" : "Plane",
            "translation" : [
                10,
                0,
                -6
            ]
        },
        {
            "mesh" : 0,
            "name" : "Plane",
            "translation" : [
                12,
                0,
                -6
            ]
        },
        {
            "mesh" : 0,
            "name" : "Plane",
            "translation" : [
                0,
                0,
                -8
            ]
        },
        {
            "mesh" : 0,
            "name" : "Plane",
            "translation" : [
                2,
                0,
                -8
            ]
        },
        {
            "mesh" : 0,
            "name" : "Plane",
            "translation" : [
                4,
                0,
                -8
            ]
        },
        {
            "mesh" : 0,
            "name" : "Plane",
            "translation" : [
                6,
                0,
                -8
            ]
        },
        {
            "mesh" : 0,
            "name" : "Plane",
            "translation" : [
                8,
                0,
                -8
            ]
        },
        {
            "mesh" : 0,
            "name" : "Plane",
            "translation" : [
                10,
                0,
                -8
            ]
        },
        {
            "mesh" : 0,
            "name" : "Plane",
            "translation" : [
                12,
                0,
                -8
            ]
        },
        {
            "children" : [
                1,
                2,
                3,
                4,
                5,
                6,
                7,
                8,
                9,
                10,
                11,
                12,
                13,
                14,
                15,
                16,
                17,
                18,
                19,
                20,
                21,
                22,
                23,
                24,
                25,
                26,
                27,
                28,
                29,
                30,
                31,
                32,
                33,
                34,
                35
            ],
            "name" : "Plane.001"
        }
    ],
    "cameras" : [
        {
            "name" : "Camera",
            "orthographic" : {
                "xmag" : 3,
                "ymag" : 4,
                "zfar" : 100,
                "znear" : 0.10000000149011612
            },
            "type" : "orthographic"
        }
    ],
    "materials" : [
        {
            "doubleSided" : true,
            "name" : "White",
            "pbrMetallicRoughness" : {
                "baseColorFactor" : [
                    0.800000011920929,
                    0.800000011920929,
                    0.800000011920929,
                    1
                ],
                "metallicFactor" : 0,
                "roughnessFactor" : 0.5
            }
        },
        {
            "doubleSided" : true,
            "name" : "Black",
            "pbrMetallicRoughness" : {
                "baseColorFactor" : [
                    0.05000000074505806,
                    0.05000000074505806,
                    0.05000000074505806,
                    1
                ],
                "metallicFactor" : 0,
                "roughnessFactor" : 0.5
            }
        }
    ],
    "meshes" : [
        {
            "name" : "Plane",
            "primitives" : [
                {
                    "attributes" : {
                        "POSITION" : 0,
                        "NORMAL" : 1,
                        "TEXCOORD_0" : 2
                    },
                    "indices" : 3,
                    "material" : 0
                },
                {
                    "attributes" : {
                        "POSITION" : 4,
                        "NORMAL" : 5,
                        "TEXCOORD_0" : 6
                    },
                    "indices" : 7,
                    "material" : 1
                }
            ]
        }
    ],
    "accessors" : [
        {
            "bufferView" : 0,
            "componentType" : 5126,
            "count" : 7,
            "max" : [
                1.5,
                0,
                0.5
            ],
            "min" : [
                -0.5,
                0,
                -1.5
            ],
            "type" : "VEC3"
        },
        {
            "bufferView" : 1,
            "componentType" : 5126,
            "count" : 7,
            "type" : "VEC3"
        },
        {
            "bufferView" : 2,
            "componentType" : 5126,
            "count" : 7,
            "type" : "VEC2"
        },
        {
            "bufferView" : 3,
            "componentType" : 5123,
            "count" : 12,
            "type" : "SCALAR"
        },
        {
            "bufferView" : 4,
            "componentType" : 5126,
            "count" : 7,
            "max" : [
                1.5,
                0,
                0.5
            ],
            "min" : [
                -0.5,
                0,
                -1.5
            ],
            "type" : "VEC3"
        },
        {
            "bufferView" : 5,
            "componentType" : 5126,
            "count" : 7,
            "type" : "VEC3"
        },
        {
            "bufferView" : 6,
            "componentType" : 5126,
            "count" : 7,
            "type" : "VEC2"
        },
        {
            "bufferView" : 7,
            "componentType" : 5123,
            "count" : 12,
            "type" : "SCALAR"
        }
    ],
    "bufferViews" : [
        {
            "buffer" : 0,
            "byteLength" : 84,
            "byteOffset" : 0,
            "target" : 34962
        },
        {
            "buffer" : 0,
            "byteLength" : 84,
            "byteOffset" : 84,
            "target" : 34962
        },
        {
            "buffer" : 0,
            "byteLength" : 56,
            "byteOffset" : 168,
            "target" : 34962
        },
        {
            "buffer" : 0,
            "byteLength" : 24,
            "byteOffset" : 224,
            "target" : 34963
        },
        {
            "buffer" : 0,
            "byteLength" : 84,
            "byteOffset" : 248,
            "target" : 34962
        },
        {
            "buffer" : 0,
            "byteLength" : 84,
            "byteOffset" : 332,
            "target" : 34962
        },
        {
            "buffer" : 0,
            "byteLength" : 56,
            "byteOffset" : 416,
            "target" : 34962
        },
        {
            "buffer" : 0,
            "byteLength" : 24,
            "byteOffset" : 472,
            "target" : 34963
        }
    ],
    "buffers" : [
        {
            "byteLength" : 496,
            "uri" : "data:application/octet-stream;base64,AAAAPwAAAAAAAAA/AAAAvwAAAAAAAAC/AAAAPwAAAAAAAAC/AADAPwAAAAAAAAA/AADAPwAAAAAAAAC/AAAAvwAAAAAAAMC/AAAAPwAAAAAAAMC/AAAAAAAAgD8AAACAAAAAAAAAgD8AAACAAAAAAAAAgD8AAACAAAAAAAAAgD8AAACAAAAAAAAAgD8AAACAAAAAAAAAgD8AAACAAAAAAAAAgD8AAACAAACAPwAAgD8AAAAAAAAAAAAAgD8AAAAAAACAPwAAgD8AAIA/AAAAAAAAAAAAAAAAAACAPwAAAAACAAAAAwACAAMABAABAAIABgABAAYABQAAAAC/AAAAAAAAAD8AAAA/AAAAAAAAAD8AAAC/AAAAAAAAAL8AAAA/AAAAAAAAAL8AAMA/AAAAAAAAAL8AAAA/AAAAAAAAwL8AAMA/AAAAAAAAwL8AAAAAAACAPwAAAIAAAAAAAACAPwAAAIAAAAAAAACAPwAAAIAAAAAAAACAPwAAAIAAAAAAAACAPwAAAIAAAAAAAACAPwAAAIAAAAAAAACAPwAAAIAAAAAAAACAPwAAgD8AAIA/AAAAAAAAAAAAAIA/AAAAAAAAgD8AAAAAAACAPwAAAAAAAIA/AAAAAAAAAQADAAAAAwACAAMABAAGAAMABgAFAA=="
        }
    ]
}

Code

Relevant excerpt from file:

    "cameras" : [
        {
            "name" : "Camera",
            "orthographic" : {
                "xmag" : 3,
                "ymag" : 4,
                "zfar" : 100,
                "znear" : 0.10000000149011612
            },
            "type" : "orthographic"
        }
    ],

See also

https://github.com/KhronosGroup/glTF/issues/1663

https://github.com/KhronosGroup/glTF/pull/2053

https://github.com/mrdoob/three.js/pull/19221

About this issue

  • Original URL
  • State: closed
  • Created 2 years ago
  • Comments: 16 (2 by maintainers)

Most upvoted comments

is there any reason we’d want (or not want) that change in the core three.js library?

I would not make it a priority unless there is demand for doing so. Cameras can have children, and ignoring part of the camera’s world transform raises questions about how to handle the children.

Cameras in three.js should not have a scale applied.

the camera’s global transform/world matrix could still have scaling that is not only != 1.0, but even non-uniform

Yes, as in your example.

The view matrix is derived from the global transform of the node containing the camera with the scaling ignored.

How is that currently handled?

three.js does not ignore the scaling in a camera transform.

three.js assumes the rotation component of the view matrix is orthonormal.

(There are side-effects if it is not. See, for example, #9002, and the discussion in #7028.)

And somewhere along the way, it looks like the numbers are being doubled.

"camera" : 0,
"name" : "Camera",
"rotation" : [
    -0.7071068286895752,
    0,
    0,
    0.7071068286895752
],
"scale" : [
    2,
    1.9999998807907104,
    1
],
"translation" : [
    6,
    2,
    -4
]

I did not mention this because it’s not explicitly clear what should be done instead.

See https://github.com/KhronosGroup/glTF/pull/2053#issuecomment-933017285.