如何在OpenGL中处理透视投影?

3

我正在使用Haskell的OpenGL绑定编写基本渲染演示。问题是它几乎无法处理超过2000个顶点。我的伪代码如下:

terrain = The set of points generated from [-1...1] x [-1...1] x [-1...1].
camera = Camera at position (xc, yc) with angles (ax, ay, az).
while running:
    input = anything that moves the camera's position or angles
    projected = []
    for point in terrain:
        projected.append(camera.perspectiveProjection(point))
    renderPoints(projected)

我认为问题在于我手动将我的三维点转换为二维点,然后使用OpenGL来绘制这些点。我的问题是:我应该提供给OpenGL三维点,然后使用OpenGL内置的投影吗?我感觉我理解透视投影的工作原理——只是不确定我是否应该手动执行此操作。
编辑:以下是我的代码大部分内容。我省略了一些仅凭函数定义就可以自我解释的部分。
main :: IO()
main = do
    (_progName, _args) <- getArgsAndInitialize
    initialDisplayMode $= [DoubleBuffered]
    _window <- createWindow "Hello, World"
    -- The camera position followed by pitch, yaw and roll.
    camera <- newIORef Camera [0,0,0] 0 0 0
    displayCallback $= display camera
    mainLoop

display :: IORef Camera -> DisplayCallback
display camIO = do
    camera <- get camIO
    clear [ColorBuffer, DepthBuffer]
    clear [ColorBuffer]
    renderPrimitive Points $ mapM_ vertex 
        $ map perspectiveProjection camera points
    swapBuffers
    postRedisplay Nothing

OpenGL是一个光栅化器,它可以为您完成所有的投影。请查看GLFW-b-demo以获取一个相当简洁的演示。 - DiegoNolan
4
我的问题是:我应该向OpenGL提供三维点,然后使用OpenGL内置的投影来处理吗? 很可能是这样,但由于缺乏细节,很难确定。您想要实现什么样的效果,以及您当前是如何实现的?您提供了一些粗略的伪代码,但更多地介绍您正在使用的确切API调用将会有所帮助。同时,您要渲染的内容也不太清楚。 - Jason Dagit
@JasonDagit 我已经编辑了我的代码示例 - 希望这样更加清晰明了。我的目标是在三维空间中绘制一堆顶点(points)。然后,我想监听按键事件,并在该空间内移动一个 Camera 对象。这些点应该根据相机的位置和角度进行渲染。 - sdasdadas
1
只是为了确认一下:您是否推出了自己的(基于CPU的)投影算法? - Charles Welton
你的OpenGL版本是什么?根据版本,有多种处理投影的方法,你可以使用内置代码传递3D点并让OpenGL处理投影,或者你可以有一个投影矩阵,将其传递给顶点着色器进行渲染。第二种情况是目前行业标准,因为它给你更多的灵活性。 - Raxvan
显示剩余2条评论
1个回答

5
正如您猜测的那样,编写自己的投影算法可能会非常慢。此外,除非您正在进行极其复杂的操作,否则OpenGL(或更具体地说GLU)有一组函数可以解决大多数问题。
传统的透视投影最简单的方法是使用具有位置、查看点和上向量的摄像机。就个人而言,我认为这比使用旋转角度定义相机轴更简单。一旦您拥有了这个,您的显示功能就可以像这样:
import Graphics.Rendering.OpenGL.GLU.Matrix

display :: IORef Camera -> DisplayCallback
display camIO = do
    camera <- get camIO
    perspective fov aspect zNear zFar
    lookAt (position camera) (lookAt camera) (upVector camera)
    -- call clear functions
    -- call renderPrimitive with the untransformed points.
lookAt函数可以改变相机的位置和方向,给相机属性。而perspective是一个函数,它获取有关相机和窗口的信息,并创建适当的透视投影。如果您发现它不能提供足够的投影控制,则可以使用Graphics.Rendering.OpenGL.GL.CoordTrans中的frustum函数。
PS:正确的方法是有一个setup函数来设置投影矩阵,并在需要时让显示函数更改模型视图矩阵。然而,上述代码应该可以工作。
PS2:如评论中指出的那样,实现这个的方式严重依赖于OpenGL版本,我不知道Haskell支持哪些版本的OpenGL。此实现基于OpenGL 2.1及以下版本。

谢谢您的回答。我更倾向于使用OpenGL 3+。 - sdasdadas
1
OpenGL 2.1 在大多数系统中作为默认启用,如果驱动程序允许,您可以使用其他版本。如果您真的想要针对它们进行目标设置,您可以创建着色器并在其上执行所有转换。对 API 进行肤浅的了解告诉我,Haskell(完全)不支持它。 - Charles Welton
我明白了 - 那么,我会接受这个答案,因为它非常有帮助。顺便说一下,我也发现这很有用(虽然我不知道它使用的是哪个版本的OpenGL):http://yannesposito.com/Scratch/en/blog/Haskell-OpenGL-Mandelbrot/ - sdasdadas

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接