来自鼠标坐标的OpenGL世界坐标

时间:2018-06-20 19:14:43

标签: haskell opengl

我正在使用下面的Haskell代码从鼠标坐标获取OpenGL世界坐标(该代码有点长,但这是绘制立方体的最小代码)。我的期望是,最后给出的函数worldFromScreen从鼠标坐标返回世界坐标(从左上角以像素为单位)。多亏了我定义的键盘鼠标回调,当用户右键单击鼠标时,这些假定的世界坐标会显示在终端中。

代码在正视图中生成一个长度为1的多维数据集:

enter image description here

立方体的长度为1,并且以原点为中心,因此此面的顶点的(x,y)坐标应为(+/- 0.5,+ /-0.5)。但是,当我右键单击该面的顶点时,大约会得到坐标(+/- 0.2,+ /-0.2)。这个0.2是多少?更改我的代码是否有可能获得(+/- 0.5,+ /-0.5)?

module Basic
  where
import           Graphics.Rendering.OpenGL
import           Graphics.UI.GLUT

white,black,red :: Color4 GLfloat
white      = Color4    1    1    1    1
black      = Color4    0    0    0    1
red        = Color4    1    0    0    1

display :: DisplayCallback
display = do
  clear [ColorBuffer, DepthBuffer]
  loadIdentity
  preservingMatrix $ do
    materialDiffuse Front $= red
    renderObject Solid $ Cube 1
  swapBuffers

resize :: Size -> IO ()
resize s@(Size w h) = do
  viewport $= (Position 0 0, s)
  matrixMode $= Projection
  loadIdentity
  perspective 45.0 (w'/h') 1.0 100.0
  lookAt (Vertex3 0 0 (-3)) (Vertex3 0 0 0) (Vector3 0 1 0)
  matrixMode $= Modelview 0
  where
    w' = realToFrac w
    h' = realToFrac h

keyboardMouse :: KeyboardMouseCallback
keyboardMouse key state _ position@(Position x y) =
  case (key,state) of
    (MouseButton LeftButton, Down) -> print (x,y)
    (MouseButton RightButton, Down) -> do
      (sx, sy, sz) <- worldFromScreen position
      print (sx, sy, sz)
    _                              -> return ()

main :: IO ()
main = do
  _ <- getArgsAndInitialize
  _ <- createWindow ""
  windowSize $= Size 500 500
  initialDisplayMode $= [RGBAMode, DoubleBuffered, WithDepthBuffer]
  clearColor $= black
  materialAmbient FrontAndBack $= black
  lighting $= Enabled
  light (Light 0) $= Enabled
  position (Light 0) $= Vertex4 200 200 (-500) 1
  ambient (Light 0) $= white
  diffuse (Light 0) $= white
  specular (Light 0) $= white
  depthFunc $= Just Less
  shadeModel $= Smooth
  displayCallback $= display
  reshapeCallback $= Just resize
  keyboardMouseCallback $= Just keyboardMouse
  idleCallback $= Nothing
  mainLoop

worldFromScreen :: Position -> IO (GLdouble, GLdouble, GLdouble)
worldFromScreen (Position sx sy) = do
  viewport@(_, Size _ viewSizeY) <- get viewport
  projectionMatrix <- get (matrix $ Just Projection) :: IO (GLmatrix GLdouble)
  modelviewMatrix <- get (matrix $ Just $ Modelview 0) :: IO (GLmatrix GLdouble)
  let screenPos = Vertex3 (fromIntegral sx) (fromIntegral ((viewSizeY - 1) - sy)) 0
  (Vertex3 wx wy wz) <- unProject screenPos projectionMatrix modelviewMatrix viewport
  return (wx, wy, wz)

1 个答案:

答案 0 :(得分:1)

我不了解Haskell,所以我将无法提供有效的代码,但是我想我理解您的问题。

您有一个2D屏幕位置,可以将其解投影到3D中并获得一个点。该点是相机在世界空间中的近平面上的位置。那就是你的“ 0.2”。现在您尚未指定z值,但我可以计算出它在-1.8附近。

单击角时,得到世界上长度为1的立方体的(+/- 0.2,+/- 0.2),因此角应为(+/- 0.5,+/- 0.5)。

  

请注意,您得到的只是世界上相机近平面上的一个点   空间,这不是多维数据集上的一点。

该点可以与您的相机位置一起使用以生成射线,然后将该射线与平面相交以获得该平面上的实际位置。

从您的代码中,

lookAt (Vertex3 0 0 (-3)) (Vertex3 0 0 0) (Vector3 0 1 0)

相机位于(0,0,-3)

如果考虑立方体的右上角,可以说您获得的3D点是(0.2, 0.2, 1.2)。使用这两个点,可以使用相机作为原点并以3D点(0.2, 0.2, -1.8)作为第二点来构造光线。

Ray( (0.0, 0.0, -3.0), (0.2, 0.2, -1.8) )

然后,此射线可以与立方体所处的平面相交以获得所需的点。当您的多维数据集以原点为中心并且正对着它看时,可以通过法线(0,0,1)和原点(0,0,0)来定义平面。

Plane(Normal (0.0, 0.0, 1.0), Point (0.0, 0.0, 0.0)).

现在,如果我们将射线与平面相交,我们将得到:

  

(0.5,0.5,0.0)

大概是您要寻找的点。

PS:我使用此链接“ Plane line intersection”来快速测试射线/平面相交。

希望这会有所帮助。