首页 > 解决方案 > 没有视图矩阵的 WorldToScreen 函数

问题描述

我正在尝试将三维世界坐标转换为屏幕上的坐标。但是,我无法访问视图矩阵,只能访问相机的俯仰角和偏航角、源坐标(地面坐标,而不是相机坐标)、目标坐标、窗口分辨率和视野。

到目前为止,我已经想出了这个,但由于我不知道如何将视角合并到函数中,它会产生不正确的结果:

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;
}

使用的CalcAngle函数如下所示:

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;
}

我的问题是:我将如何创建一种方法来仅使用我拥有的信息(没有视图矩阵或三维相机轴,我只有俯仰和偏航)来计算屏幕坐标?如果我目前的方法已经存在缺陷,我将如何创建一个实现这一目标的函数?

标签: c#game-developmentangle

解决方案


我通过从视角中减去 CalcAngle 的角度成功地解决了这个问题。除此之外,我还确保为相应的三角函数使用正确的单位。

  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;
        }

推荐阅读