当相机移动时,无法让光线拾取在我的 3D 场景中工作
Can't get ray picking to work in my 3D scene when the camera moves
我围绕 OpenGL (C++) 实现了自己的 3D 引擎,并且它在过去几年中运行良好。
但是今天我偶然发现了一个问题。我有一个球体场景(围绕太阳的行星、轨道环和类似的东西,非常非常简单),我想用鼠标对它们进行光线拾取。
只要 camera/view 矩阵是恒等矩阵,选择就可以进行。当相机旋转然后移动时,采摘完全失控。我一直在寻找解决方案,所以现在我问你们。
这是代码(为这个问题总结):
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 使用旋转时,它也工作正常。当我开始使用翻译时,它就完全错了。
有人知道我哪里错了吗?
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!
我围绕 OpenGL (C++) 实现了自己的 3D 引擎,并且它在过去几年中运行良好。
但是今天我偶然发现了一个问题。我有一个球体场景(围绕太阳的行星、轨道环和类似的东西,非常非常简单),我想用鼠标对它们进行光线拾取。
只要 camera/view 矩阵是恒等矩阵,选择就可以进行。当相机旋转然后移动时,采摘完全失控。我一直在寻找解决方案,所以现在我问你们。
这是代码(为这个问题总结):
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 使用旋转时,它也工作正常。当我开始使用翻译时,它就完全错了。 有人知道我哪里错了吗?
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!