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

求实现类Minecraft游戏中射线与立方体相交面的检测函数

类Minecraft游戏立方体射线相交函数实现

我正在开发一款类Minecraft游戏,使用的立方体顶点缓冲区如下:

float positions[] = {
    // front face       
    0.0f, 0.0f, 1.0f,
    1.0f, 0.0f, 1.0f,
    1.0f, 1.0f, 1.0f,
    0.0f, 1.0f, 1.0f,
    // back face
    0.0f, 0.0f, 0.0f,
    0.0f, 1.0f, 0.0f,
    1.0f, 1.0f, 0.0f,
    1.0f, 0.0f, 0.0f,
    // top face         
    0.0f, 1.0f, 0.0f,
    0.0f, 1.0f, 1.0f,
    1.0f, 1.0f, 1.0f,
    1.0f, 1.0f, 0.0f,
    // bottom face      
    0.0f, 0.0f, 0.0f,
    1.0f, 0.0f, 0.0f,
    1.0f, 0.0f, 1.0f,
    0.0f, 0.0f, 1.0f,
    // right face       
    1.0f, 0.0f, 0.0f,
    1.0f, 1.0f, 0.0f,
    1.0f, 1.0f, 1.0f,
    1.0f, 0.0f, 1.0f,
    // left face        
    0.0f, 0.0f, 0.0f,
    0.0f, 0.0f, 1.0f,
    0.0f, 1.0f, 1.0f,
    0.0f, 1.0f, 0.0f,
};

采用全局定位方式:第一个方块位于(0,0,0),其右侧相邻方块位于(1,0,0),每个方块是1x1x1的轴对齐立方体。

已知从玩家位置发射的射线与某个立方体相交,但无法确定具体相交的面,现请求实现以下函数:

glm::vec3 IntersectCube(const glm::vec3& rayOrigin, const glm::vec3& rayDir, const glm::vec3& blockPos)

实现思路

针对轴对齐立方体(AABB)的射线相交,我们可以通过计算射线与立方体6个面的交点,筛选出有效交点(在射线前进方向上),最终取距离射线原点最近的那个交点。

具体步骤:

  1. 确定目标立方体的全局边界:最小点为blockPos,最大点为blockPos + glm::vec3(1.0f)
  2. 计算射线与每个面的交点参数t,并过滤掉t < 0的无效值(只考虑射线前进方向的交点)
  3. 对每个有效t计算对应的交点坐标,然后找到距离射线原点最近的交点

函数实现

#include <glm/glm.hpp>
#include <algorithm>
#include <limits>

glm::vec3 IntersectCube(const glm::vec3& rayOrigin, const glm::vec3& rayDir, const glm::vec3& blockPos) {
    // 立方体的全局边界:min和max顶点
    glm::vec3 cubeMin = blockPos;
    glm::vec3 cubeMax = blockPos + glm::vec3(1.0f);

    // 存储所有有效交点的距离和坐标
    float closestDist = std::numeric_limits<float>::max();
    glm::vec3 closestPoint;

    // 处理X方向的两个面:x=cubeMin.x(左面)和x=cubeMax.x(右面)
    if (std::abs(rayDir.x) > 1e-6) { // 避免除以0(射线平行于X面的情况)
        float t1 = (cubeMin.x - rayOrigin.x) / rayDir.x;
        float t2 = (cubeMax.x - rayOrigin.x) / rayDir.x;

        // 确保t1 <= t2,方便后续处理
        float tMin = std::min(t1, t2);
        float tMax = std::max(t1, t2);

        // 检查tMin对应的点是否在立方体的Y、Z范围内
        if (tMin >= 0.0f) {
            glm::vec3 point = rayOrigin + tMin * rayDir;
            if (point.y >= cubeMin.y && point.y <= cubeMax.y && point.z >= cubeMin.z && point.z <= cubeMax.z) {
                float dist = glm::distance(rayOrigin, point);
                if (dist < closestDist) {
                    closestDist = dist;
                    closestPoint = point;
                }
            }
        }

        // 检查tMax对应的点是否在立方体的Y、Z范围内
        if (tMax >= 0.0f) {
            glm::vec3 point = rayOrigin + tMax * rayDir;
            if (point.y >= cubeMin.y && point.y <= cubeMax.y && point.z >= cubeMin.z && point.z <= cubeMax.z) {
                float dist = glm::distance(rayOrigin, point);
                if (dist < closestDist) {
                    closestDist = dist;
                    closestPoint = point;
                }
            }
        }
    }

    // 处理Y方向的两个面:y=cubeMin.y(底面)和y=cubeMax.y(顶面)
    if (std::abs(rayDir.y) > 1e-6) {
        float t1 = (cubeMin.y - rayOrigin.y) / rayDir.y;
        float t2 = (cubeMax.y - rayOrigin.y) / rayDir.y;

        float tMin = std::min(t1, t2);
        float tMax = std::max(t1, t2);

        if (tMin >= 0.0f) {
            glm::vec3 point = rayOrigin + tMin * rayDir;
            if (point.x >= cubeMin.x && point.x <= cubeMax.x && point.z >= cubeMin.z && point.z <= cubeMax.z) {
                float dist = glm::distance(rayOrigin, point);
                if (dist < closestDist) {
                    closestDist = dist;
                    closestPoint = point;
                }
            }
        }

        if (tMax >= 0.0f) {
            glm::vec3 point = rayOrigin + tMax * rayDir;
            if (point.x >= cubeMin.x && point.x <= cubeMax.x && point.z >= cubeMin.z && point.z <= cubeMax.z) {
                float dist = glm::distance(rayOrigin, point);
                if (dist < closestDist) {
                    closestDist = dist;
                    closestPoint = point;
                }
            }
        }
    }

    // 处理Z方向的两个面:z=cubeMin.z(背面)和z=cubeMax.z(正面)
    if (std::abs(rayDir.z) > 1e-6) {
        float t1 = (cubeMin.z - rayOrigin.z) / rayDir.z;
        float t2 = (cubeMax.z - rayOrigin.z) / rayDir.z;

        float tMin = std::min(t1, t2);
        float tMax = std::max(t1, t2);

        if (tMin >= 0.0f) {
            glm::vec3 point = rayOrigin + tMin * rayDir;
            if (point.x >= cubeMin.x && point.x <= cubeMax.x && point.y >= cubeMin.y && point.y <= cubeMax.y) {
                float dist = glm::distance(rayOrigin, point);
                if (dist < closestDist) {
                    closestDist = dist;
                    closestPoint = point;
                }
            }
        }

        if (tMax >= 0.0f) {
            glm::vec3 point = rayOrigin + tMax * rayDir;
            if (point.x >= cubeMin.x && point.x <= cubeMax.x && point.y >= cubeMin.y && point.y <= cubeMax.y) {
                float dist = glm::distance(rayOrigin, point);
                if (dist < closestDist) {
                    closestDist = dist;
                    closestPoint = point;
                }
            }
        }
    }

    // 题目已知射线与立方体相交,因此closestPoint必然有有效值
    return closestPoint;
}

代码说明

  • 使用1e-6作为容差,避免因浮点精度问题导致的除以0错误
  • 对每个面的交点,额外检查是否落在立方体的另外两个轴的范围内,确保是真正的面交点
  • 筛选出所有有效交点后,取距离射线原点最近的点作为结果

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.27 06:54:54