使用物理库(OPENGL & BULLET 3D

Picking with a physics library (OPENGL & BULLET 3D

我正在尝试使用子弹物理绘制一条射线来击中场景中的游戏对象,这样我就可以 select 它,我正在使用相机矩阵绘制一条射线,然后选择一个对象在 space 中,然后查看游戏对象列表并寻找相同的位置。

在鼠标按下时我有以下代码,它似乎已关闭并且有时只选择项目:

    glm::vec4 lRayStart_NDC(
    ((float)lastX / (float)RECT_WIDTH - 0.5f) * 2.0f, 
    ((float)lastY / (float)RECT_HEIGHT - 0.5f) * 2.0f,
    -1.0,
    1.0f
);

glm::vec4 lRayEnd_NDC(
    ((float)lastX / (float)RECT_WIDTH - 0.5f) * 2.0f,
    ((float)lastY / (float)RECT_HEIGHT - 0.5f) * 2.0f,
    0.0,
    1.0f
);


projection = glm::perspective(glm::radians(SceneManagement::getInstance()->MainCamera->GetVOW()), (float)RECT_WIDTH / (float)RECT_HEIGHT, 0.1f, 100.0f);
glm::mat4 InverseProjectionMatrix = glm::inverse(projection);

view = SceneManagement::getInstance()->MainCamera->GetViewMatrix();
glm::mat4 InverseViewMatrix = glm::inverse(view);

glm::vec4 lRayStart_camera = InverseProjectionMatrix * lRayStart_NDC;    
lRayStart_camera /= lRayStart_camera.w;
glm::vec4 lRayStart_world = InverseViewMatrix * lRayStart_camera; 
lRayStart_world /= lRayStart_world.w;
glm::vec4 lRayEnd_camera = InverseProjectionMatrix * lRayEnd_NDC;      
lRayEnd_camera /= lRayEnd_camera.w;
glm::vec4 lRayEnd_world = InverseViewMatrix * lRayEnd_camera;   
lRayEnd_world /= lRayEnd_world.w;

glm::vec3 lRayDir_world(lRayEnd_world - lRayStart_world);
lRayDir_world = glm::normalize(lRayDir_world);

glm::vec3 out_end = SceneManagement::getInstance()->MainCamera->GetCamPosition() + SceneManagement::getInstance()->MainCamera->GetCamFront() * 1000.0f;

btCollisionWorld::ClosestRayResultCallback RayCallback(
    btVector3(SceneManagement::getInstance()->MainCamera->GetCamPosition().x, SceneManagement::getInstance()->MainCamera->GetCamPosition().y, SceneManagement::getInstance()->MainCamera->GetCamPosition().z),
    btVector3(out_end.x, out_end.y, out_end.z)
);

PhysicsManager::getInstance()->dynamicsWorld->rayTest(
    btVector3(SceneManagement::getInstance()->MainCamera->GetCamPosition().x, SceneManagement::getInstance()->MainCamera->GetCamPosition().y, SceneManagement::getInstance()->MainCamera->GetCamPosition().z),
    btVector3(out_end.x, out_end.y, out_end.z),
    RayCallback
);

if (RayCallback.hasHit()) 
{
    btTransform position = RayCallback.m_collisionObject->getInterpolationWorldTransform();
    printf("Collision \n");
    for (int i = 0; i < SceneManagement::getInstance()->gObjects.size(); i++)
    {
        if (SceneManagement::getInstance()->gObjects.at(i)->transform.Position.x == position.getOrigin().getX() &&
            SceneManagement::getInstance()->gObjects.at(i)->transform.Position.y == position.getOrigin().getY() &&
            SceneManagement::getInstance()->gObjects.at(i)->transform.Position.z == position.getOrigin().getZ())
        {
            int select = i;
            SceneManagement::getInstance()->SelectedGameObject = SceneManagement::getInstance()->gObjects.at(select);
            SceneManagement::getInstance()->SelectedGameObject->DisplayInspectorUI();
            return;
        }
    }
}

这张支票

SceneManagement::getInstance()->gObjects.at(i)->transform.Position.x == position.getOrigin().getX() &&
SceneManagement::getInstance()->gObjects.at(i)->transform.Position.y == position.getOrigin().getY() &&
SceneManagement::getInstance()->gObjects.at(i)->transform.Position.z == position.getOrigin().getZ()

由于数值精度问题而失败。

您应该检查向量的“相等性”,直到达到一定的精度。

请使用一些距离函数和以下检查来代替您的 if() 条件:

 const double EPSILON = 1e-4; // experiment with this value
 auto objPos = SceneManagement::getInstance()->gObjects.at(i)->transform.Position;
 bool isWithinRange = distance3D(objPos, position.getOrigin()) < EPSILON;
 if (isWithinRange)
 {
      int select = i;
      ...
 }

distance3D函数就是3D中的欧氏距离。 glm::distance 函数应该可以,只需将两个向量转换为 glm::vec3 格式。