OSM buildings windows orientation problem with true origin enabled

Hello.

I am getting to know with OSM buildings. I am trying out levels from here: GitHub - CesiumGS/cesium-unreal-samples: Getting Started Sample Project for Cesium for Unreal.

When Georeference’s origin placement is set to ‘LongitudeLatitudeHeight’ the orientation of procedurally generated windows is ok

But when it is set to ‘TrueOrigin’ it gets messed up (see pic. 2)

As far as I understood the problem is that in both ML_ProceduralWindowsMetadata and ML_ProceduralWindowsMetadata in this section:

we are calling MF_TriplanarWindows which is used to generate window-grid.

But inside MF_TriplanarWindows:


we are retrieving current Absolute World Position in absolute UE coordinates, while we should (I guess) take current ENU (or ESU) position. I mean we should be geoawared to have a correct information about Z-axis.

I tried to somehow modify those Material Functions (MF_TriplanarWindows and ML_ProceduralWindowsMetadata), but I can’t think of way of getting current geographical-awared positions inside material function as we can’t get references to actors from MF.

How can I resolve this issue?
Maybe there are some other ways for correct orientation of windows with ‘True origin’ enabled?

p.s. Unreal Engine 5.4.4, cesium 2.10

Hi @ureadulose,

It’s a tough problem. Even if we had a material function to compute an ENU (or similar) coordinate system, we’d run into precision problems with it due to the limited floating-point precision available on GPUs (or at least in Unreal materials). But you might be able to get started by porting some GLSL from CesiumJS to HLSL and Unreal:

@Kevin_Ring thank you for the reply!

Here is what I came with now:
In MF_TriplanarWindows I inserted custom HLSL shader node:


the idea was:

  1. Define radii constants inside shader
  2. Get pixel in UE world space (AbsoluteWorldPosition node)
  3. Transform it’s coordinates from unreal space to ECEF
  4. Find rotation matrix that would “snap” current pixel position to normal in ECEF
  5. Apply transformation
    and after that send this back to the other nodes.

Here is HLSL code:

struct MyFunctions {

    // Unreal to ECEF
    float3 UnrealPositionToEcef(float3 unrealPos) {
        return float3(unrealPos.y, unrealPos.x, unrealPos.z) * 0.01; // Convert cm to meters
    }

    // WGS84
    float3 OneOverEllipsoidRadiiSquared() {
        return float3(
            1.0 / (6378137.0 * 6378137.0),
            1.0 / (6378137.0 * 6378137.0),
            1.0 / (6356752.314245 * 6356752.314245)
        );
    }

    // Computes the geodetic normal for an ECEF position
    // (based on https://github.com/CesiumGS/cesium/blob/main/packages/engine/Source/Shaders/Builtin/Functions/geodeticSurfaceNormal.glsl)
    float3 CalculateEllipsoidNormal(float3 ecefPos, float3 oneOverEllipsoidRadiiSquared) {
        return normalize(ecefPos * oneOverEllipsoidRadiiSquared);
    }

    // Helper function to create a rotation matrix from an axis and angle
    float3x3 RotationMatrixFromAxisAngle(float3 axis, float angle) {
        float c = cos(angle);
        float s = sin(angle);
        float t = 1.0 - c;

        float x = axis.x;
        float y = axis.y;
        float z = axis.z;

        return float3x3(
            t * x * x + c,      t * x * y - s * z,  t * x * z + s * y,
            t * x * y + s * z,  t * y * y + c,      t * y * z - s * x,
            t * x * z - s * y,  t * y * z + s * x,  t * z * z + c
        );
    }

    // Aligns Unreal world position with the ellipsoid normal
    float3 AlignWorldPositionToEllipsoid(float3 worldPos, float3 ecefNormal) {
        // Define the "up" vector (Z-axis in Unreal)
        float3 up = float3(0, 0, 1);

        // Compute the rotation axis (cross product of up and ellipsoid normal)
        float3 axis = cross(up, ecefNormal);
        // Compute the rotation angle (dot product of up and ellipsoid normal)
        float angle = acos(dot(up, ecefNormal));

        // Construct a rotation matrix from the axis and angle
        float3x3 rotationMatrix = RotationMatrixFromAxisAngle(axis, angle);
        // Transform the world position using the rotation matrix
        return mul(rotationMatrix, worldPos);
    }
};

MyFunctions f;

float3 ecefPos = f.UnrealPositionToEcef(unrealPos);
float3 oneOverRadiiSquared = f.OneOverEllipsoidRadiiSquared();
float3 ecefNormal = f.CalculateEllipsoidNormal(ecefPos, oneOverRadiiSquared);

return f.AlignWorldPositionToEllipsoid(unrealPos, ecefNormal);

So far by messing with different transformations I got this result (i will provide screenshot with unchanged-shader version of MF_TriplanarWindows too)


I guess that on the ‘left’ sides there are straight lines corresponding to the ‘new horizon’ line, but on the ‘right’ sides there are still problems.

Am I on the right way and what problems does the HLSL code have? Are my key points correct and there is some error in implementation of my steps, or did I misunderstood some key ideas?

I understood that there are some issues with precision and I guess that is why it looks so glitchy, but the outlines are visible and from the distance it’s kinda ok.

I think you have the right idea, but the transform is not quite right. I would suggest taking a look at this function and porting it to HLSL:

The inverse of the matrix returned by that function will take a position from Unreal world coordinates to a system centered at the given position where X points East, Y points South, and Z points Up. You could potentially even compute that on the CPU based on the camera position, and then pass it to the material using a Material Parameter Collection.