Search code examples
c#game-developmentangle

WorldToScreen Function without a View Matrix


I am trying to convert three dimensional world coordinates into coordinates on my screen. However, I do not have access to a view matrix, only to the camera's pitch and yaw angles, source coordinates (ground coordinates, not camera coordinates), target coordinates, window resolution, and field of view.

I have come up with this so far, but since I don't know how to incorporate view angles into the function, it yields incorrect results:

public static bool WorldToScreen(Vec3 source, Vec3 target, Vec2 viewAngles, uint fov, out Vec2 screenPos)
{
    screenPos = new Vec2();

    Vec2 deltaAngles;

    uint hWindowRes = 1920;
    uint vWindowRes = 1080;

    float hFov = GetFieldOfView(hWindowRes, vWindowRes, fov);
    float vFov = fov;

    CalcAngle(source, target, out deltaAngles);

    float hOffset = (float)(Math.Tan(deltaAngles.X * Math.Cos(hFov / 2) / Math.Sin(hFov / 2) * (hWindowRes / 2)));
    float hScreenPos = hWindowRes / 2 - hOffset;

    float vOffset = (float)(Math.Tan(deltaAngles.Y * Math.Cos(vFov / 2) / Math.Sin(vFov / 2) * (vWindowRes / 2)));
    float vScreenPos = vWindowRes / 2 - vOffset;

    screenPos.X = hScreenPos;
    screenPos.Y = vScreenPos;

    return true;
}

The used CalcAngle function looks like this:

private static bool CalcAngle(Vec3 source, Vec3 target, out Vec2 viewAngles)
{
    Vec2 angles;

    angles.X = (float)(((float)Math.Atan2(target.X - source.X, target.Y - source.Y)) / Math.PI * 180.0f);
    angles.Y = (float)(Math.Asin((target.Z - source.Z) / Vec3.Distance(source, target)) * 180.0f / Math.PI);

    viewAngles = angles;

    return true;
}

My question is: How would I create a method out of this to calculate screen coordinates using just the information I have (no view matrix, or three dimensional camera axes, I only have pitch and yaw)? If my current approach is already flawed, how would I go about creating a function that accomplishes that?


Solution

  • I successfully managed to fix this by subtracting the angles from CalcAngle from my view angles. Besides that, I also made sure to use the correct units for my corresponding trigonometric functions.

      public static bool WorldToScreen(Vec3 source, Vec3 target, Vec2 viewAngles, uint fov, out Vec2 screenPos)
            {
                screenPos = new Vec2();
    
                Vec2 aimAngles;
    
                uint hGameRes = 1920;
                uint vGameRes = 1080;
    
                float hFov = GetFieldOfView(hGameRes, vGameRes, fov);
                float vFov = fov;
                
                CalcAngle(source, target, out aimAngles);
    
    
                var deltaAngles = viewAngles - aimAngles;
    
    
                float hOffsetTan = (float)Math.Tan(deltaAngles.X.ToRadians());
                float hOffsetCos =  (float)Math.Cos((hFov / 2).ToRadians());
                float hOffsetSin = (float)Math.Sin((hFov / 2).ToRadians()); 
                
                float hOffset = (float)(hOffsetTan * hOffsetCos / hOffsetSin * (hGameRes / 2)); 
                float hScreenPos = hGameRes / 2 - hOffset;
    
                float vOffsetTan = (float)Math.Tan(deltaAngles.Y.ToRadians());
                float vOffsetCos = (float)Math.Cos((vFov / 2).ToRadians());
                float vOffsetSin = (float)Math.Sin((vFov / 2).ToRadians());
    
                float vOffset = (float)(vOffsetTan * vOffsetCos / vOffsetSin * (vGameRes / 2));
                float vScreenPos = vGameRes / 2 - vOffset;
    
                screenPos.X = hScreenPos;
                screenPos.Y = vScreenPos;
    
                return true;
            }