求实现类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个面的交点,筛选出有效交点(在射线前进方向上),最终取距离射线原点最近的那个交点。
具体步骤:
- 确定目标立方体的全局边界:最小点为
blockPos,最大点为blockPos + glm::vec3(1.0f) - 计算射线与每个面的交点参数
t,并过滤掉t < 0的无效值(只考虑射线前进方向的交点) - 对每个有效
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
相关产品推荐
相关产品推荐

