3D Ray Picking在鼠标未锁定时使用鼠标坐标

时间:2013-08-15 00:51:53

标签: opengl vector 3d projection ray-picking

所以基本上我用OpenGL制作了一个可以执行3D Ray Picking的程序。如果Camera View Direction Ray触摸/交叉任何东西(不是空气),则会在交点/点处渲染一个紫色的小框。

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

示例:

我现在进行3D射线拾取的方法是获取相机的视线方向光线,然后只计算交叉点。 我计算交叉点的函数不作为布尔值返回,它作为3D矢量返回(交集本身的坐标)

问题

所以我想要实现的是计算 Picking Ray ,但是根据鼠标锁定到屏幕时。

示例 - 所以在这里你可以看到紫色的盒子在十字准线上,但是如果我要解锁鼠标并移动它(在屏幕顶部,就像通常那样)并移动它到我绘制的绿色X标记的中心,然后我想计算从摄像机中心到屏幕顶部鼠标坐标的光线。

当前的测试和想法

这应该是一个数学问题。这里只是我目前用来计算Ray(并尝试计算第二条射线)的一些简短列表

  • Cameara X,Y,Z
  • 相机间距偏转辊(现在不使用)
  • 相机近远(距离)
  • Camera Fov
  • 相机方面
  • 鼠标X,Y(在屏幕顶部)
  • Sceen Width,Height

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

计算主采摘射线本身

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()方法完全按照它的说法进行,使矢量规范化。

因此,当我尝试使用鼠标坐标进行计算时,我在调用direction.normalize();之前插入了以下代码,因此在创建Vector3D direction之后。

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 个答案:

答案 0 :(得分:10)

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

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,但希望你明白这一点。

部分链接Select + Mouse + OpenGL

答案 1 :(得分:3)

您需要做的就是从通过屏幕空间点(x,y)的相机原点拍摄光线。这里的问题是,从摄像机的原点到屏幕空间中的某个点,通常会发生许多变换(实际上是2个矩阵和一个视口映射)。另一个问题是,这会将所有内容抛到脑后,通常你会从世界空间位置开始,并在OpenGL管道中结束屏幕空间 - 你想要采取另一种方式:)

单独使用相机的方向无法解决此问题。您需要知道场景如何投影到您的观察平面上,因此需要投影矩阵。您还需要知道视口尺寸和相机的原点。如果您知道视口尺寸,投影矩阵和模型视图矩阵,则可以解决整个问题。

我建议你研究gluUnProject (...),它会做你需要的一切。快速搜索Google会产生这种情况,这看起来非常有用:http://myweb.lmu.edu/dondi/share/cg/unproject-explained.pdf