Haskell 从鼠标坐标到OpenGL世界坐标
我使用下面的Haskell代码从鼠标坐标获得OpenGL世界坐标(代码有点长,但这是绘制立方体的最小代码)。我的期望是,最后给出的函数Haskell 从鼠标坐标到OpenGL世界坐标,haskell,opengl,Haskell,Opengl,我使用下面的Haskell代码从鼠标坐标获得OpenGL世界坐标(代码有点长,但这是绘制立方体的最小代码)。我的期望是,最后给出的函数worldFromScreen返回鼠标坐标的世界坐标(以左上角的像素为单位)。多亏了我定义的键盘-鼠标回调,当用户右键单击鼠标时,这些假定的世界坐标会打印在终端上 代码在前视图中生成一个长度为1的立方体: 立方体的长度为1,且以原点为中心,因此该面的顶点的(x,y)坐标应为(+/-0.5,+/-0.5)。但是,当我右键单击该面的顶点时,我得到的坐标大约为(+/-
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)
基本模块
哪里
导入Graphics.Rendering.OpenGL
导入Graphics.UI.GLUT
白色、黑色、红色::彩色4 GLfloat
白色=彩色4 1
黑色=彩色4 0 0 1
红色=颜色41 0 0 1
display::DisplayCallback
显示=do
清除[颜色缓冲区,深度缓冲区]
负载标识
保存矩阵$do
材料扩散前$=红色
renderObject实体$Cube 1
天鹅垫
调整大小::大小->IO()
调整大小s@(大小w h)=do
视口$=(位置0,s)
矩阵模式$=投影
负载标识
透视图45.0(w'/h')1.0 100.0
注视(顶点3 0(-3))(顶点3 0 0 0)(向量3 0 1 0)
matrixMode$=Modelview 0
哪里
w'=realToFrac w
h'=realtofrach
键盘鼠标::键盘鼠标返回
键盘鼠标键状态@位置@(x y位置)=
案例(关键、状态)
(鼠标左键,向下)->打印(x,y)
(鼠标按钮右键,向下)->do
(sx、sy、sz)返回()
main::IO()
main=do
_我不知道Haskell,所以我无法提供有效的代码,但我想我理解你的问题
您有一个二维屏幕位置,可以将其取消投影到三维并获得一个点。该点是世界空间中摄影机近平面上的一个位置。这就是你的“0.2”。现在您还没有指定z值,但我可以计算它大约为-1.8
当你们点击角点时,你们会得到(+/-0.2,++/-0.2)一个立方体,它的世界长度是1,所以角点应该是(+/-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)
定义
现在,如果光线与平面相交,我们得到:
(0.5,0.5,0.0)
大概就是你要找的那一点
PS:我使用此链接“”快速测试光线/平面交点
希望这有帮助。屏幕坐标是二维坐标。要获得3D世界坐标,你需要第三个坐标。是的,Ripi,但我只需要(x,y)坐标。看起来我得到的x或y世界坐标是0.5/(h-0.5)
,其中h
是相机位置的z坐标。我观察到,对于不同的h
,如果投影是正交的,那么任何z值都可能是后缀。对于透视投影,这是不正确的。画一个熔丝,用相机连接点,看看它们在近平面上的相交点。谢谢@Ripi2,我开始理解了。我将尝试使用正交投影(我还不知道它是什么,但我想它是正交投影?我稍后再看,我现在很忙)。谢谢。我刚刚醒来,我不知道什么都明白,但我明白了其中的道理。您是如何获得1.2
的?@StéphaneLaurent,很抱歉出现了错误,我输入了矢量而不是点值(现在已更正为-1.8
)。我将剩余的已知数字插入到直线/平面交点、摄影机(0,0,-3
)和未投影点(0.2,0.2,z
)的链接表单中,对于z=-1.8,交点是(0.5,0.5,0.0
),这是我认为在世界空间原点的X-Y平面上的预期点。
Plane(Normal (0.0, 0.0, 1.0), Point (0.0, 0.0, 0.0)).