关于haskell:OpenGL世界坐标从鼠标坐标

OpenGL world coordinates from mouse coordinates

我使用下面的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)?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
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)


我不认识Haskell,所以我无法提供工作代码,但我想我理解您的问题。

你有一个二维屏幕的位置,你取消投影到三维,得到一个点。这个点是相机在世界空间中的近平面上的位置。这就是你的"0.2"。现在您还没有指定z值,但是我可以计算它在-1.8附近。

当你点击角时,你会得到一个立方体的(+/-0.2,+/-0.2),这个立方体的世界长度是1,所以角应该是(+/-0.5,+/-0.5)。

Note that what you get is a point on camera's near plane in world
space, it is NOT a point on your cube.

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

从您的代码中,

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

摄像机在(0,0,-3)处。

如果我们考虑你的立方体的右上角,假设你得到的三维点是(0.2, 0.2, 1.2)。利用这两个点,可以以相机为原点,以三维点(0.2, 0.2, -1.8)为第二点来构造光线。

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

然后,该光线可以与立方体所在的平面相交,从而获得要查找的点。当你的立方体在原点的中心,并且你正直视它时,平面可以由正常的(0,0,1)和原点(0,0,0)定义。

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

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

(0.5, 0.5, 0.0)

大概是你想要的。

PS:我用这个链接"平面线相交"来快速测试光线/平面相交。

希望这有帮助。