Flipped (Negative x scale) Images in UI not rendering in builds

I’ve created an issue with sample project for this (IN-65599).

Essentially our UI relies on creating symmetrical elements out of two Image components: one flipped, the other not. However the flipped side isn’t rendering in a build (tested on device and simulator, latest Unity, Polyspatial etc. etc.)

In the below screenshot I’ve duplicated the ‘expand’ icon and flipped it. Rendering is fine in editor, not on simulator.


Is there a workaround for this? Or a known fix incoming?

Thanks,
Joe

Is the flip a rotation or just setting scale to a - value?

It’s setting a negative scale (per the subject line).

The underlying issue is that using a negative scale flips the winding order and thus reverses back-face culling. We use RealityKit’s ShaderGraphMaterial for UI, and that material doesn’t support changing the backface culling setting (it always renders the “front face,” which is incorrect with negative scale).

In this particular case, a workaround might be to set a negative Z scale (which won’t affect the appearance, but will change the winding order), and then set the rotation to 180 degrees about Y (to flip the image around).

Sorry, I tried that and it didn’t work. It may be that the only option at the moment is to use a separate, flipped image. We’ll work on a better fix, though.

Thanks, I’ll give that a go - thought that could be the case with the winding order. Next problem is finding and dealing with all of these cases! A search on our project for m_LocalScale: {x: -1 yields 811 results :sweat_smile:

edit: just saw your edit - thanks for testing!

I’ve come up with a solution that uses a custom component that implements IMeshModifier which takes the opportunity to reverse the winding order of the mesh the sibling image component creates. That way we can keep the scaling as-is, and we don’t need duplicate flipped sprites. Plus the fix can be employed just for visionOS. Still means we need to find and add the component where needed, but without the ability to auto-detect image components being created, I think this is the cleanest solution

1 Like

hi, I am trying to solve the same problem with this code, but not work. Could you please share your custom component code? Thanks a lot!

[RequireComponent(typeof(RectTransform), typeof(Graphic)), DisallowMultipleComponent]
public class FlipImage : UIBehaviour, IMeshModifier
{
    [SerializeField]
    private bool _isFlipX = true;
 
    [SerializeField]
    private bool _isFlipY = true;
    
#if UNITY_EDITOR
    public new void OnValidate()
    {
        GetComponent<Graphic>().SetVerticesDirty();
    }
 
#endif
    
    public void ModifyMesh(Mesh mesh)
    {
        //do nothing
    }
 
    public void ModifyMesh(VertexHelper verts)
    {
        RectTransform rt = transform as RectTransform;
        
        for (int i = 0; i < verts.currentVertCount; ++i)
        {
            UIVertex uiVertex = new UIVertex();
            verts.PopulateUIVertex(ref uiVertex, i);

            // Modify positions
            float uvX = uiVertex.position.x;
            float uvY = uiVertex.position.y;
            if (_isFlipX)
            {
                uvX = 2 * rt.rect.center.x - uiVertex.position.x;
            }
            if (_isFlipY)
            {
                uvY = 2 * rt.rect.center.y - uiVertex.position.y;
            }
            uiVertex.position = new Vector3(uvX, uvY, uiVertex.position.z);

            // Apply
            verts.SetUIVertex(uiVertex, i);
        }
    }
}

I’m not the original poster, but it looks like the piece that you’re missing is reversing the winding order. When you negate the scale on an odd number of axes, you need to reverse the order in which triangle indices are listed to make backface culling work as expected. I haven’t tested it, but you could try adding something like this to the end of ModifyMesh(VertexHelper):

// Reverse winding order if x or y (but not both) flipped.
if (_isFlipX ^ _isFlipY)
{
    List<UIVertex> stream = new();
    verts.GetUIVertexStream(stream);

    for (var i = 0; i < stream.Count; i += 3)
    {
        var tmp = stream[i];
        stream[i] = stream[i + 1];
        stream[i + 1] = tmp;
    }

    verts.Clear();
    verts.AddUIVertexTriangleStream(stream);
}
1 Like

Yes, it works! Thanks a lot!

1 Like