Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

i3dm renders incorrectly when model has non-identity matrix #11176

Open
bbbbx opened this issue Mar 21, 2023 · 10 comments
Open

i3dm renders incorrectly when model has non-identity matrix #11176

bbbbx opened this issue Mar 21, 2023 · 10 comments

Comments

@bbbbx
Copy link
Contributor

bbbbx commented Mar 21, 2023

When the transformation matrix of the glTF node of i3dm is not the identity matrix,
the calculation of the bounding sphere of the model(scene graph/primitive/draw command) is wrong,
causing the far and near planes of the viewing cone to be smaller than expected,
and finally the pixels outside the bounding sphere are discarded in the fragment shader(writeLogDepth.glsl).

Screenshot:

i3dm.mp4

Sandcastle example: url

Browser: Google Chrome 111.0.5563.65

Operating System: Windows 10

@bbbbx
Copy link
Contributor Author

bbbbx commented Mar 21, 2023

Model bounding sphere:

root node scale: 0.1 root node scale: 1.0
image image
@ggetz
Copy link
Contributor

ggetz commented Mar 23, 2023

Thanks for the report @bbbbx!

@LHolst
Copy link
Contributor

LHolst commented Jun 29, 2023

I have encountered a similar behavior with i3dm tiles, incorrectly clipping and warping vertices (looks like floatingpoint precision)

I can provide the tileset with some trees placed:
Symbole_Baum_test_tileset.zip

Peek.2023-06-29.15-04.webm

The same tileset is working just fine in 1.83.

@LHolst
Copy link
Contributor

LHolst commented Jun 30, 2023

i retried with a simpler example, cube 10 units.
The difference to the sandcastle InstancedOrientation tileset is the extra attribute SCALE_NON_UNIFORM

cube10m_instanced_tileset.zip

this leads to wobbly vertex displacements :

cube10_instanced_tileset.webm
@LHolst
Copy link
Contributor

LHolst commented Aug 10, 2023

I've found a workaround for cases when rtc_center property is defined in i3dm files.
If rtc_center is defined, the step for manual computation of a BoundingSphere and storing positions relative to the center is skipped. But if rtc_center is equal to [0,0,0] or Cartesian3.ZERO, the manual computation should be used as well. This seems to correct the rendering for the provided cases.

@ggetz
Copy link
Contributor

ggetz commented Nov 10, 2023

Also reported in #11617.

@javagl
Copy link
Contributor

javagl commented Jul 27, 2024

After this came up several times in the forum, most recently at https://community.cesium.com/t/disappearing-i3dms-on-map-navigation-again/34035 , I did a short debugging pass. Well. Not so short, actually. What's particularly humbling is that the statement that was made in the first comment, namely

the calculation of the bounding sphere of the model(scene graph/primitive/draw command) is wrong,

was spot on (and it took me a while to arrive at the same "insight").


Some details:

  • In the ModelSceneGraph.buildDrawCommands function, it is creating the PrimitiveRenderResources. The bounding sphere is computed from the min/max of the respective primitive, plus the 'instancing translation'. So far that sounds about right (with a caveat)
  • When creating the DrawCommand, it is taking the previously computed bounding sphere, and transforming it with the model matrix, which includes the transform of the glTF node
  • The resulting bounding sphere will later determine whether a command is executed or not. Details omitted here.

The caveat is that this node transform may, for example, involve scaling, and that scaling will therefore be indirectly applied to the instancing transforms when it comes to determining their effect on the bounding sphere.

Maybe it becomes clearer with some random debug log from some test model:

The case where it's not working, when a primitive is attached to a node with a uniform scale of 0.01:

primitive min/max is (-3558.85693359375, -2171.158447265625, -44.34097671508789) (3385.92578125, 3999.103271484375, 462.9575500488281)
instancing min/max is (-81.875, -975.484375, -156) (81.875, 975.484375, 156)
new primitive min/max is (-3640.73193359375, -3146.642822265625, -200.3409767150879) (3467.80078125, 4974.587646484375, 618.9575500488281)
Draw command bounding sphere is 
  center: Cartesian3 ...
  radius: 54.11951398537193
  • The primitive min/max and instancing translation min/max are added together
  • A bounding volume of radius ~5411 is computed from them
  • The scale of 0.01 is applied to that, resulting in a bounding sphere with radius of 54.11

This cannot be right. The instancing translations alone would require a radius of ~1000.

The case where it is working, because the scale of 0.01 is "baked" into the primitive, and the node transform is the identity:

primitive min/max is (-3.376162052154541, -1.1918214559555054, -13.400678634643555) (3.376161575317383, 103.71656036376953, 4.557344913482666)
instancing min/max is (-81.875, -975.484375, -156) (81.875, 975.484375, 156)
new primitive min/max is (-85.25116205215454, -976.6761964559555, -169.40067863464355) (85.25116157531738, 1079.2009353637695, 160.55734491348267)
Draw command bounding sphere is  
  center: Cartesian3 ...
  radius:  1044.5781589720143

The bounding sphere here is properly computed from the primitive min/max plus the instancing min/max. No scaling is applied afterwards.


Now, the question is still: How to solve this?

One could be tempted to just omit some scaling here or there, to compute the proper bounding sphere.

But it's not that simple.

There could be instancing information (like from EXT_mesh_gpu_instancing) that actually is attached to a node that involves a scaling. And in this case, the instancing translations should be affected by the node transform.

In contrast to that, the I3dmLoader.createInstances function assigns the instancing information that was created from the I3DM data to all nodes of the glTF, regardless of whether they contain a scaling factor or not. This means that the instancing translations should not be affected by the node transform.

(I'm talking about "affecting" here - I should be more precise, and say whether this affects the rendering or some bounding volume computation in some draw command. But .. it's complicated. One thing that I want to try out is whether the rendering even works properly when there is an I3DM for a glTF that contains two nodes with different scaling factors. It's not yet clear whether the "error" here is really only affecting the draw command bounding volume part, or whether it also affects the rendered instances. I hope that it's only the draw command. But this has to be investigated further)

@javagl
Copy link
Contributor

javagl commented Jul 31, 2024

@bertt already provided very helpful data in the forum thread...s, that helped to narrow down the issue.

I created another test ("CC0", so to speak).

It is an I3DM with positions

        new float[] {
            0.0f, 0.0f, 0.0f,
            10.0f, 0.0f, 0.0f,
            0.0f, 10.0f, 0.0f,
            10.0f, 10.0f, 0.0f,
        }

This is used to create four instances of a GLB that contains some data that aims at

  • visually checking whether the output is "right"
  • identifying the reason why the output is "not right"

For the latter, there are a bazillion possible reasons. (I mean, starting with the fact that the I3DM only contains positions, but no scaling/up-vectors etc). But the glTF consists of 4 unit cubes:

  • The gray one is a "real" unit cube, with a size of (1,1,1) attached to a node with a scaling of (1,1,1)
  • The red one is a cube of size (0.1, 0.1, 0.1), attached to a node with scale (10.0, 10.0, 10.0) (thus, the cube should have unit size), and a translation of (2.0, 0.0, 0.0)
  • The green one is a cube of size (10.0, 10.0, 10.0), attached to a node with scale (0.1, 0.1, 0.1) (thus, also yielding unit size), and a translation of (0.0, 2.0, 0.0)
  • The yellow one is a real unit cube, attached to a node with scale (10.0, 10.0, 10.0) and a translation of (20.0, 20.0, 20.0), but attached to a parent node with a scale of (0.1, 0.1, 0.1) - thus, boiling down to a unit cube at (2, 2, 0)

(I obviously ~"tried to cover several cases" here. Doing this thoroughly and systematically, sneaking in scaling and translation (and maybe rotation etc) everywhere, and/or using transforms that "cancel out" each other, would yield a combinatorial explosion. The cases have been selected based on the insights from the debugging pass, just to have a few "hot candidates", and a first, basic test...)

The good news is: The bug does not affect the rendering of the instances themself. Regardless of the scaling and whatnot, they are all unit cubes at the proper locations:

Cesium Issue 11176 disappearing in I3DM

The ... also good... ? or bad...? ... the other news is that this already shows the problem:

Cesium 11176 disappearing in I3DM

Only the green cube seems to ever disappear. It is the only one that is attached to a node with a global scaling less than 1.0. Based on the previous debugging pass, this is not toooo surprising: For the other cubes, it will just overestimate the size of the object, roughly meaning ~"a reduced culling-efficiency" - it will still 'render' these nodes even if it wouldn't have to.

I'll have a short look at possible solutions for all this, and maybe create a draft PR.


EDIT: Nearly forgot: Here's the test data, with a tileset JSON and sandcastle:

CesiumJS issue 11176 test.zip

@javagl
Copy link
Contributor

javagl commented Aug 11, 2024

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment