ThreeJS doesnt properly render objects those have scale(-1,-1,-1)

ThreeJS render objects reverse lighting those have scale(-1,-1,-1).
Object A has scale(1,1,1)
Object B has scale(-1,-1,-1)
the Model was exported with Sketchup as GLB format.

I tried to manipulate objects those have scale(-1,-1,-1):

const Correct = (child,muls) => {
    if (child.isMesh && child.geometry) {
        child.geometry = child.geometry.clone()
        const normalAttr = child.geometry.attributes.normal
        for (var i = 0; i < normalAttr.count; i++) {
            normalAttr.setXYZ(
                i,
                normalAttr.getX(i) * muls[0],
                normalAttr.getY(i) * muls[0],
                normalAttr.getZ(i) * muls[0]
            )
        }
        normalAttr.needsUpdate = true
        const posAttr = child.geometry.attributes.position;
        for (var i = 0; i < posAttr.count; i++) {
            posAttr.setXYZ(
                i,
                posAttr.getX(i) * muls[0],
                posAttr.getY(i) * muls[1],
                posAttr.getZ(i) * muls[2]
            );
        }
        posAttr.needsUpdate = true;
        child.geometry.computeVertexNormals()
        const center = child.geometry.boundingBox.getCenter(new THREE.Vector3())
        child.geometry.translate(-center.x, -center.y, -center.z)
        child.position.add(center)
        child.updateMatrixWorld(true)
    }
    if (child.children)
        child.children.forEach( sub => Correct(sub,muls) )
}
scene.traverse((child) => {
    if (child.scale.x < 0 || child.scale.y < 0 || child.scale.z < 0) {
        const muls = [1,1,1]
        if (child.scale.x < 0) { child.scale.x *= -1; muls[0] = -1 }
        if (child.scale.y < 0) { child.scale.y *= -1; muls[1] = -1 }
        if (child.scale.z < 0) { child.scale.z *= -1; muls[2] = -1 }
        Correct(child,muls)
    }
})

Most objects looks correct, but some are not…

What am I missing? How to handle scale(-1,-1,-1) objects? How to make ThreeJS render them properly?

I am using ThreeJS v0.122.0 btw.

Thank you!

1 Like

r122 is a pretty old version of three.js — can you test your glb in a newer version, or perhaps in the latest three.js editor?

I tried with v0.147.0, but no lucky… For some reasons I cant use newers…

The images are too bloated and even with the help of the arrows it is unclear what exactly is wrong and how it should look like. Try with two boxes, one normal and one flipped. If you still get wrong light, then share it via CodePen.io (or similar tool) so that others could debug it live.

BTW I tried with two GLB models, one scaled (-1,-1,-1) and did not see any inconsistency. They look just as I’d expect. Of course, it is possible that I have misunderstood the issue.

https://codepen.io/boytchev/pen/GgpXvwz
image

3 Likes

In older versions of threejs - scale or 0 scale weren’t supported.

related: Support reflections in world transform by WestLangley · Pull Request #12787 · mrdoob/three.js · GitHub

1 Like

A negative scale would end up flipping the triangles correct?

2 Likes

I prepared a test suite. I tried on codepen: https://codepen.io/Akkara/pen/QwjVPEo
But Since codepen doesnt allowing to upload .glb (I couldnt figure out) I had to upload it to: https://zebraburada.com/3d/

Here you can see the problem clearly:

I only noticed scale property causes this; but there might be another properties to responsible this issue.

The problem is not based on older version. This test based on v0.179.1

You can download all from: https://zebraburada.com/3d/threejs-scale.zip

So, what does do this? Scale? @donmccurdy @PavelBoytchev

If you’re using a program such as blender, can you not select the negatively scaled meshes and in object mode use…

object → apply → scale (or all transforms)

before exporting to ensure you’re using standardised scale units?

Glb file became very large, because I had to explode every components/symbols in the sketchup file; which is not a good thing to do… And I will be have to do this again every model revision over Sketchup…

I think this problem belongs to threejs and glb.

Something into your mesh, maybe in up, scale, matrix, parents deep in childrens.
Because if apply negative scale to box it looks ok.
Your mesh have reacting to scale.z=-1.
No reactions to scale.x=-1, scale.x=1, scale.y=-1, scale.y=1, or its ok i dont know.
If change Y position, then mesh go forward insted up

This was very plain model exporting with Sketchup; There was no need to code hacks to make it view properly with Threejs..

It looks like the model data are wrongly defined. Even in Blender the model shows up wrongly. I made a quick patch that may or may not work in the general case. The patch flips the normals of all geometries, which are buried under odd number of negatively scaled objects, because even number of negative scales cancel each other. The first snapshot is without the patch, the second is with the patch.

Without:

With:

The patched file (see lines 58-79):

index.html (2.4 KB)

6 Likes

Thank you very much for your effort; I was doing very same hack in the first place -you can see it in question post, somes are ok but some objects never appeared in correct position.

It would be great to catch the cause of issue…

Maybe Sketchup uses some custom (or very new (or very rare)) GLTF/GLB feature not supported by others? Maybe it related to reversed winding order? Maybe you could try to export in different format?

The GLB model shows wrongly in Blender, so I’d assume it is not a Three.js issue.

How is the model created? If you have made it, could you remake the second object by rotating the first object, instead of by scaling it negatively?

Just to clarify, if the original is not GLB/GLTF, Sketchfab converts anything that’s not in the original format through its own pipeline, designers don’t upload multiple exported formats. That’s why I always do the glTF export myself, because a lot of things from the data structure to the materials end up incorrect otherwise.

Well it was an architectural model, and exported from Sketchup. The model created by architects and engineers for the construction. My sketchup skills was low, and not neccessary, I am on the Threejs side.

Well, when I think about the problem a little more, I realize this:

  • GLB keeps components as source objects
  • Threejs gets every objects those marked-as-component from “components library” in glb file
  • Threejs shows every component-based-objects as it is in the library
  • So every generated component-based-objects using same vertex, points etc…
  • While generation Threejs does not unlink objects connections and prepare/finalize properties independently…

So need to normalize every component-based-objects while generation…

Do I clearly explain the situation?

~Yeah, but if you export it yourself with the correct structure from the original format like USDZ or OBJ, you won’t see this problem as @PavelBoytchev fixed.~

Sorry, I’ve tried, it doesn’t always fix it. Even GLTF/GLB exports of custom meshes don’t work correctly, alpha and shadows look wrong as your issue…

There have been a number of fixes in three.js over the years intended to support lighting on negatively-scaled objects correctly. It looks to me like either (a) there has been a regression, or (b) this model requires some additional workarounds in three.js to handle. In either case — please feel free to open an issue with the .glb model on the github repository, perhaps that can be fixed.

That said, handling negative scaling in three.js is a bit tricky, and I’d (personally) be hesitant to rely heavily on that method in production applications, at least if you care about physically-based lighting. I suspect Sketchup has a specialized renderer for CAD models, and just handles this data very differently. If size of the model is the main concern, otherwise, there are many ways to reduce that. Exports directly from a DCC tool like Sketchup or Blender are not usually particularly optimized, so extra optimization steps with tools like gltfpack or glTF-Transform will help.

The glTF file itself contains nothing particularly unusual except the negatively-scaled nodes, and the KHR_materials_pbrSpecularGlossiness extension (which is now unsupported, but doesn’t seem to affect this issue).

2 Likes

If I am understanding correctly, it seems this is an existing issue: WebGPU: Negative scale inverts front-face direction · Issue #31764 · mrdoob/three.js · GitHub