You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

X-Plane 12中XPLMProbeTerrainXYZ始终在相机原点命中且地形返回异常

X-Plane 12 地形射线投射问题排查

我尝试在X-Plane 12中使用XPLMProbeTerrainXYZ获取鼠标(或相机方向)下方的3D地形位置,编写了如下C++函数:

bool GetCameraSightIntersection(float* outX, float* outY, float* outZ, float* outNormalX, float* outNormalY, float* outNormalZ, bool* outIsWet) {
    if (g_terrain_probe == nullptr) {
        g_terrain_probe = XPLMCreateProbe(xplm_ProbeY);
        if (g_terrain_probe == nullptr) {
            return false;
        }
    }

    float cam_x = g_initial_camera_pos.x;
    float cam_y = g_initial_camera_pos.y;
    float cam_z = g_initial_camera_pos.z;
    float cam_heading = g_initial_camera_pos.heading;
    float cam_pitch = -g_initial_camera_pos.pitch;

    float heading_rad = cam_heading * M_PI / 180.0f;
    float pitch_rad = cam_pitch * M_PI / 180.0f;

    float forward_x = sin(heading_rad) * cos(pitch_rad);
    float forward_y = sin(pitch_rad);
    float forward_z = cos(heading_rad) * cos(pitch_rad);

    const int max_probes = 100;
    const float probe_distance = 100.0f;
    const float max_distance = 10000.0f;

    for (int i = 0; i < max_probes; i++) {
        float distance = i * probe_distance;
        if (distance > max_distance) break;

        float probe_x = cam_x + forward_x * distance;
        float probe_y = cam_y - forward_y * distance;
        float probe_z = cam_z - forward_z * distance;

        XPLMProbeInfo_t probe_info;
        probe_info.structSize = sizeof(XPLMProbeInfo_t);
        XPLMProbeResult result = XPLMProbeTerrainXYZ(g_terrain_probe, probe_x, probe_y, probe_z, &probe_info);

        if (result == xplm_ProbeHitTerrain) {
            if (outX) *outX = probe_info.locationX;
            if (outY) *outY = probe_info.locationY;
            if (outZ) *outZ = probe_info.locationZ;
            if (outNormalX) *outNormalX = probe_info.normalX;
            if (outNormalY) *outNormalY = probe_info.normalY;
            if (outNormalZ) *outNormalZ = probe_info.normalZ;
            if (outIsWet) *outIsWet = (probe_info.is_wet != 0);
            return true;
        }
    }
    return false;
}

遇到的问题

问题1:XPLMCreateProbe是否创建了无效探针?

预期XPLMCreateProbe(xplm_ProbeY)返回有效探针对象,但地形命中结果不符合预期,程序未崩溃。

问题2:探针总是在i == 0时返回命中

即使相机处于空中,循环第一步就检测到地形命中,但实际相机并未接触地形。

核心需求

从相机位置沿朝向投射射线,获取与地面的交点(比如鼠标或视野中心对应的地面3D点),怀疑相机方向计算有误。

疑问

  1. 有没有更高效的相机到地形射线投射方法?
  2. 为什么相机在空中时,XPLMProbeTerrainXYZ在相机位置就能返回命中?
  3. XPLMCreateProbe在X-Plane 12中会静默失败吗?

问题解答

1. XPLMCreateProbe的有效性问题

XPLMCreateProbe(xplm_ProbeY)在X-Plane 12中不会静默失败,只有返回nullptr时才代表创建失败。你的代码已经做了空指针检查,程序未崩溃说明探针大概率是有效的,问题出在后续逻辑或方向计算上。

2. 相机空中却在初始位置命中的原因

XPLMProbeTerrainXYZ的工作逻辑是:从你传入的坐标**沿Y轴负方向(向下)**探测地形,只要该点正下方存在地形,就会返回命中结果——不管相机是否在空中。你循环的第一步i=0时,探针坐标就是相机坐标,自然会直接探测相机正下方的地形,这就是为什么总是在第一步得到命中结果。

3. 更优的射线投射方法

当前的步进式探测效率低、精度差,X-Plane提供了直接的射线探测API XPLMProbeTerrainRay,可以沿任意射线方向直接获取地形交点,完全匹配你的需求。

修改后的核心逻辑示例:

// 替换原有的循环部分
XPLMProbeInfo_t probe_info;
probe_info.structSize = sizeof(XPLMProbeInfo_t);
// 传入相机起点、射线方向向量、最大探测距离
XPLMProbeResult result = XPLMProbeTerrainRay(
    g_terrain_probe,
    cam_x, cam_y, cam_z,  // 射线起点(相机位置)
    forward_x, forward_y, forward_z,  // 射线方向向量(需确保单位化)
    max_distance,  // 最大探测距离
    &probe_info
);

if (result == xplm_ProbeHitTerrain) {
    // 填充输出参数
    if (outX) *outX = probe_info.locationX;
    if (outY) *outY = probe_info.locationY;
    if (outZ) *outZ = probe_info.locationZ;
    if (outNormalX) *outNormalX = probe_info.normalX;
    if (outNormalY) *outNormalY = probe_info.normalY;
    if (outNormalZ) *outNormalZ = probe_info.normalZ;
    if (outIsWet) *outIsWet = (probe_info.is_wet != 0);
    return true;
}
return false;

另外需要修正方向向量的两个问题:

  • 符号检查:X-Plane坐标系为X轴东、Y轴上、Z轴北,俯仰角的符号需要确认。如果相机向上俯仰时pitch为正,cam_pitch = -g_initial_camera_pos.pitch可能需要调整,建议打印forward_y值,确保相机向下看时该值为负。
  • 单位化处理:方向向量必须是单位向量,否则XPLMProbeTerrainRay的距离计算会出错,可添加归一化代码:
float length = sqrt(forward_x*forward_x + forward_y*forward_y + forward_z*forward_z);
if (length > 1e-6) {
    forward_x /= length;
    forward_y /= length;
    forward_z /= length;
}

总结

  • 用XPLMProbeTerrainRay替代步进式探测,效率和精度更高
  • XPLMCreateProbe不会静默失败,返回nullptr才是创建失败,你的探针是有效的
  • 初始位置命中是因为XPLMProbeTerrainXYZ仅探测点正下方,而非沿你的射线方向
  • 检查方向向量的符号和单位化,确保射线方向正确

内容的提问来源于stack exchange,提问作者oKami

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.12 14:41:02