当相机移动时,不能让光线拾取在我的3D场景中工作

时间:2017-03-31 09:31:19

标签: opengl ray-picking

我有自己的围绕OpenGL(C ++)的3D引擎实现,并且它在过去几年中一直运行良好。

但今天我偶然发现了一个问题。我有这个球体的场景(围绕太阳的行星,轨道环和类似的东西,非常非常简单),我想用鼠标射线拾取它们。

只要相机/视图矩阵是标识,拾取就可以了。当相机旋转然后移动时,拾取完全变得混乱。我一直在寻找解决方案,所以现在我问你们。

这是代码(此问题总结):

mat4f mProj = createPerspective(PI / 4.0f, float(res.x) / float(res.y), 0.1f, 100.0f);
mat4f mCamera = createTranslation(-1.5f, 3, -34.0f) * createRotationZ(20.0f * PI / 180.0f) * createRotationX(20.0f * PI / 180.0f);
... render scene, using shaders that transform vertices with gl_Position = mProj * mCamera * aPosition;

mat4f mUnproject = (mProj * mCamera).getInverse();
vec2f mouseClip(2.0f * float(coord.x) / float(res.x) - 1.0f, 1.0f - 2.0f * float(coord.y) / float(res.y));
vec3f rayOrigin = (mUnproject * vec4f(mouseClip, 0, 1)).xyz();
vec3f rayDir = (mUnproject * vec4f(mouseClip, -1, 1)).xyz();

// In a loop over all planets:
    mat4f mObject = createRotationY(planet.angle) * createTranslation(planet.distance, 0, 0);
    vec3f planetPos = mObject.transformCoord(vec3f(0, 0, 0));
    float R = planet.distance;

    float a = rayDir.dot(rayDir);
    float b = 2 * rayDir.x * (rayOrigin.x - planetPos.x) + 2 * rayDir.y * (rayOrigin.y - planetPos.y) + 2 * rayDir.z * (rayOrigin.z - planetPos.z);
    float c = planetPos.dot(planetPos) + rayOrigin.dot(rayOrigin) -2 * planetPos.dot(rayOrigin) - R * R;
    float d = b * b - 4 * a * c;
    if (d >= 0)
        HIT!

因此,当我使用mCamera的身份时,一切正常,即使我只使用旋转mCamera,它工作正常。当我开始使用翻译时,它完全错误。 谁知道我哪里出错?

1 个答案:

答案 0 :(得分:1)

BDL的回答很明显,让我回到了正确的方向。实际上,当我自己改变坐标时,我忘了做视角分割。在写了这么多着色器代码之后,gpu为你做了这些,你就忘记了这些事情。

合乎逻辑的是,这只会在相机移动时产生问题,而不是在(0,0,0)处时,因为转换矩阵的平移部分保持为0且坐标的w因子不受影响

我立即在我的矩阵类中编写了transformCoord和transformNormal实现,以防止再次发生此错误。

此外,射线的起源和方向是不正确的,虽然我还不明白为什么。我现在从我的相机矩阵(当然倒置)获取原点并以相同方式计算方向,但现在从中减去相机位置以使其成为方向向量。我对它进行了规范化,虽然我不认为在这种情况下它确实是必要的,但正常化它会使它的数字在调试时看起来更具可读性。

这有效:

vec2f mouseClip(2.0f * float(coord.x) / float(res.x) - 1.0f, 1.0f - 2.0f * float(coord.y) / float(res.y));

mat4f mUnproject = (mProj * mCamera).getInverse();
mat4f mInvCamera = mCamera.getInverse();
vec3f rayOrigin(mInvCamera.m[12], mInvCamera.m[13], mInvCamera.m[14]);
vec3f rayDir = (mUnproject.transformCoord(vec3f(mouseClip, 1)) - rayOrigin).normalized();

... per planet
    vec3f planetPos = mObject.transformCoord(vec3f(0, 0, 0));
    float a = rayDir.dot(rayDir);
  float b = 2 * rayDir.x * (rayOrigin.x - planetPos.x) + 2 * rayDir.y * (rayOrigin.y - planetPos.y) + 2 * rayDir.z * (rayOrigin.z - planetPos.z);
  float c = planetPos.dot(planetPos) + rayOrigin.dot(rayOrigin) -2 * planetPos.dot(rayOrigin) - 0.4f * 0.4f;
  float d = b * b - 4 * a * c;
  if (d >= 0)
    ... HIT!