当鼠标没有锁定时,使用鼠标坐标进行3D射线拾取。

14

基本上,我使用OpenGL制作了一个程序,可以执行3D Ray Picking。如果相机视线方向光线与任何实体(不是空气)相交/触碰,则会在相交点/点处呈现一个小紫色框。

如果光线与任何“红色框”相交,则与光线相交的那些框将变为绿色。地面和墙壁不会改变颜色或纹理。

示例:

我目前进行3D Ray Picking的方式是获取相机的视线方向光线,然后计算相交点。 我的计算相交函数返回的不是布尔值,而是一个3D向量(相交点的坐标)

问题

因此,我想要实现的是计算Picking Ray,但根据鼠标在屏幕上未锁定时的位置进行计算。

示例 - 在这里,您可以看到紫色框位于十字准线上,但是,如果我解锁鼠标并将其移动(在屏幕上方,像通常一样),并将其移动到我绘制的绿色X标记的中心,则希望从相机中心到屏幕上的鼠标坐标计算射线。

当前测试和想法

这应该只是一个数学问题。以下是我目前用于计算光线(和尝试计算第二条光线)的简短清单:

  • 相机X、Y、Z
  • 相机俯仰偏航滚转(当前未使用滚转)
  • 相机近远距离(距离)
  • 相机Fov
  • 相机Aspect
  • 鼠标X、Y(在屏幕上方)
  • 屏幕宽度、高度

鼠标X&Y原点(0x0)位于窗口/框架的左下角。

计算主要的Picking Ray

Vector3D position = new Vector3D(
        camera.x,
        camera.y,
        camera.z);

Vector3D direction = new Vector3D(
        Math.cos(Math.toRadians(camera.pitch)) * -Math.sin(Math.toRadians(-camera.yaw)) * camera.far,
        Math.cos(Math.toRadians(camera.pitch)) * cameara.far,
        Math.cos(Math.toRadians(camera.pitch)) * -Math.sin(Math.toRadians(-camera.yaw)) * camera.far);

direction.normalize();

Ray3D ray = new Ray(position, direction);
这就是我计算主捕捉光线本身的方法(对于锁定鼠标的捕捉光线)。这些类是我自己创建的,尽管它们应该很好理解(例如Vector3DRay3D等),normalize() 方法确切地执行了它所说的操作,即将向量归一化。

思路

所以,当我尝试使用鼠标坐标进行计算时,我在创建Vector3D direction之后、调用direction.normalize();之前插入了以下代码。
if (!Mouse.isGrabbed())
{
    float mx = Mouse.getX() / (float) scene.width - 0.5f;
    float my = Mouse.getY() / (float) scene.height - 0.5f;

    mx *= camera.far;
    my *= camera.far;

    line.b.x += mx;
    line.b.y += my;
    line.b.z += mz;
}

当鼠标未被锁定/抓取时,这给我带来了奇怪的结果。这很有道理,因为我只是在尝试一些脑海中首先出现的东西。

我猜我需要根据俯仰角、偏航角和滚转角来转换鼠标坐标。虽然我不知道如何去做。

所以我希望有人可以帮助我实现这个,或者给我一些资源,让我能够理解我正在尝试做的事情。

额外信息

如果您需要更多信息,请写评论,我会尽力提供。

答案 - 感谢fen

我现在最终使用了fen的方法,因为它比计算一切要简单得多!

FloatBuffer projection = BufferTools.createFloatBuffer(16);
FloatBuffer modelview = BufferTools.createFloatBuffer(16);
IntBuffer viewport = BufferTools.createIntBuffer(16);

glGetFloat(GL_PROJECTION_MATRIX, projection);
glGetFloat(GL_MODELVIEW_MATRIX, modelview);
glGetInteger(GL_VIEWPORT, viewport);

float win_x = Mouse.getX();
float win_y = Mouse.getY();

FloatBuffer position_near = BufferTools.createFloatBuffer(3);
FloatBuffer position_far = BufferTools.createFloatBuffer(3);

gluUnProject(win_x, win_y, 0f, modelview, projection, viewport, position_near);
gluUnProject(win_x, win_y, 1f, modelview, projection, viewport, position_far);

Ray3D ray = new Ray3D(
    new Vector3D(
        position_near.get(0),
        position_near.get(1),
        position_near.get(2)),
    new Vector3D(
        position_far.get(0),
        position_far.get(1),
        position_far.get(2)));
2个回答

11

这是我创建鼠标射线的代码:

double matModelView[16], matProjection[16]; 
int viewport[4]; 

// get matrix and viewport:
glGetDoublev( GL_MODELVIEW_MATRIX, matModelView ); 
glGetDoublev( GL_PROJECTION_MATRIX, matProjection ); 
glGetIntegerv( GL_VIEWPORT, viewport ); 

// window pos of mouse, Y is inverted on Windows
double winX = (double)mouseX; 
double winY = viewport[3] - (double)mouseY; 

// get point on the 'near' plane (third param is set to 0.0)
gluUnProject(winX, winY, 0.0, matModelView, matProjection, 
         viewport, m_start.x, &m_start.y, &m_start.z); 

// get point on the 'far' plane (third param is set to 1.0)
gluUnProject(winX, winY, 1.0, matModelView, matProjection, 
         viewport, m_end.x, &m_end.y, &m_end.z); 

// now you can create a ray from m_start to m_end

OpenGL 2.0,但希望你能理解。

一些链接选择+鼠标+OpenGL


我认为,正如Andon M. Coleman所写的那样:您必须使用相机和投影变换来创建适当的光线。 - fen
@fen,最终我采用了你的方法,尽管在你的示例中有一个缺陷,而且我也不需要反转 winY,但无论如何还是谢谢! - vallentin
@fen,你只给了viewport大小为4,但实际需要16。:) 另外,我不需要反转Y坐标,所以我删除了那部分代码(这不是一个缺陷)。如果你还没有看到,我已经在帖子中发布了我的结果,并附上了我现在使用的代码。 :) - vallentin
@Vallentin 不不不... viewport 需要 4 个元素 :) 在这里看看:http://www.opengl.org/sdk/docs/man/xhtml/glGet.xml,GL_VIEWPORT:“params 返回四个值:视口的 x 和 y 窗口坐标,以及其宽度和高度。” 矩阵需要 16 个元素。 - fen
@fen 嗯...听起来很奇怪,因为我使用了viewport[4],它给了我一个错误,说“viewport”数组需要有16个大小。 :) - vallentin
显示剩余3条评论

3
你需要做的就是从相机的原点发射一条射线,穿过屏幕空间点(x,y)。问题在于,为了从相机的原点到达屏幕空间中的某个点,通常会进行多次转换(实际上有两个矩阵和一个视口映射)。另一个问题是,这将所有事情都搞乱了,通常你从世界空间位置开始,最终在OpenGL管道中得到屏幕空间 -- 你要走反向的路线 :)
仅凭相机的方向无法解决此问题。您需要知道场景如何投影到您的观察平面上,因此需要投影矩阵。您还需要知道视口尺寸和相机的原点。如果您知道视口尺寸、投影矩阵和模型视图矩阵,则可以解决整个问题。
我建议您查看gluUnProject(...),它可以完成您需要的所有操作。在Google上快速搜索结果是这样的,看起来非常有帮助:http://myweb.lmu.edu/dondi/share/cg/unproject-explained.pdf

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接