我正在使用下面的Haskell代码从鼠标坐标获取OpenGL世界坐标(该代码有点长,但这是绘制立方体的最小代码)。我的期望是,最后给出的函数worldFromScreen
从鼠标坐标返回世界坐标(从左上角以像素为单位)。多亏了我定义的键盘鼠标回调,当用户右键单击鼠标时,这些假定的世界坐标会显示在终端中。
代码在正视图中生成一个长度为1的多维数据集:
立方体的长度为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)
答案 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”来快速测试射线/平面相交。
希望这会有所帮助。