简单的3D投影和方向处理?

简单的3D投影和方向处理?,3d,matrix,lua,projection,quaternions,3d,Matrix,Lua,Projection,Quaternions,我目前正在做一个古怪的复古飞行模拟,我的3d项目遇到了一些问题,以至于我找不到关于这个主题的任何可靠的通用文档 在给定以下信息的情况下,如何将游戏世界中的简单位置向量转换为屏幕上的2d向量: 摄像机的位置 a摄像机方向(见下文) 视野 屏幕的高度和宽度(以及纵横比) 我也在寻找一种存储方向的方法,我已经编写了一个基本的向量库,但我不确定如何存储旋转,以便在相机(和投影代码)以及游戏中对象旋转的实际处理中使用。我目前正在考虑使用四元数,但是使用四元数而不是矩阵进行投影变换是否可能(而且容易) 在代

我目前正在做一个古怪的复古飞行模拟,我的3d项目遇到了一些问题,以至于我找不到关于这个主题的任何可靠的通用文档

在给定以下信息的情况下,如何将游戏世界中的简单位置向量转换为屏幕上的2d向量:

摄像机的位置 a摄像机方向(见下文) 视野 屏幕的高度和宽度(以及纵横比)

我也在寻找一种存储方向的方法,我已经编写了一个基本的向量库,但我不确定如何存储旋转,以便在相机(和投影代码)以及游戏中对象旋转的实际处理中使用。我目前正在考虑使用四元数,但是使用四元数而不是矩阵进行投影变换是否可能(而且容易)

在代码中实现四元数有什么好的来源吗?我必须为复数编写一个单独的库吗

谢谢您的时间和帮助:)

注意:回答太长了

我在Love2D中做过一个类似的项目,它运行得非常快,所以我不认为在Lua中自己做数学运算而不是使用OpenGL(它无论如何都不会公开)有什么问题

与评论相反,你不应该气馁。3D方向和透视背后的数学其实很简单,只要你有了感觉

对于方向而言,四元数可能有些过分。我发现要进行旋转3D投影,只需要
Vec2
Vec3
Camera
类。虽然在数学上有一些细微的差别,但在实践中,向量的向量可以生成完全合适的变换矩阵,变换矩阵可以生成完全合适的方向。矩阵是向量的向量,其优点是只需编写一个类即可处理这两个类

要投影矢量
v
,请考虑摄像机的3个参数:

  • loc
    ,摄像机位置的
    Vec3
  • trans
    ,一个
    Mat3by3
    (也称为
    Vec3
    Vec3
    ),用于与相机方向相反
    • (免责声明:从技术上讲,使用矩阵进行相机定向是有害的,因为小的舍入误差可能会累积,但在实际使用中这是可以的)
  • 缩放
    ,用于确定透视图的比例因子。
    zoom
    z
    (相对于相机)相当于处于2D中;也就是说,从透视图来看,没有缩放
投影的工作原理如下:

function Camera:project(v)
    local relv -- v positioned relative to the camera, both in orientation and location
    relv = self.trans * (v - self.loc) -- here '*' is vector dot product
    if relv.z > 0 then
        -- v is in front of the camera
        local w -- perspective scaling factor
        w = self.zoom / relv.z
        local projv -- projected vector
        projv = Vec2(relv.x * w, relv.y * w)
        return projv
    else
        -- v is behind the camera
        return nil
    end
end
这假定Vec2(0,0)对应于窗口的中心,而不是角点。设置它是一个简单的翻译

trans
应以恒等矩阵开始:
Vec3(Vec3(1,0,0)、Vec3(0,1,0)、Vec3(0,0,1))
并以增量方式计算,每次进行方向更改时进行小调整

我觉得你已经知道矩阵的基本知识了,但如果你不知道,你的想法是: 矩阵是向量的向量,至少在这种情况下,可以将其视为坐标系。每个向量都可以看作是坐标系的一个轴。通过更改矩阵的元素(这些元素是向量,被认为是矩阵的列),可以更改该坐标系中坐标的含义。在正常使用中,向量的第一个分量表示向右移动,第二个分量表示向上移动,第三个分量表示向前移动。但是,使用矩阵,可以使每个组件指向任意方向。点积的定义是

函数Vec3.dot(a,b)返回a.x*b.x+a.y+b.y+a.z*b.z结束

对于矩阵

Vec3(axis1、axis2、axis3)

给定点积的定义,用向量v点缀的矩阵将产生

axis1*v.x+axis2*v.y+axis3*v.z

这意味着
v
的第一个元素表示要移动多少
axis1
s,第二个元素表示要移动多少
axis2
,第三个元素表示要移动多少
axis3
,最终结果是
v
,如果用标准坐标表示,而不是用矩阵坐标表示。当我们把一个矩阵和一个向量相乘时,我们改变了向量分量的意义。从本质上讲,它是一个陈述的数学表达式,比如“任何向右的东西现在都不向右,而向前”或任何类似的东西。在一句话中,矩阵变换空间

回到手头的任务,使用矩阵以角度
theta
表示“俯仰”(即围绕x轴)旋转,您可以编写:

function pitchrotation(theta)
    return Vec3(
        -- axis 1
        -- rotated x axis
        -- we're rotating *around* the x axis, so it stays the same
        Vec3(
            1,
            0,
            0
        ),
        -- axis 2
        -- rotated y axis
        Vec3(
            0,
            math.cos(theta),
            math.sin(theta)
        ),
        -- axis 3
        -- rotated z axis
        Vec3(
            0,
            -math.sin(theta),
            math.cos(theta)
        )
    )
end
对于“偏航”(绕y轴):

最后是“滚动”(围绕z轴),这在飞行模拟中特别有用:

function rollrotation(theta)
    return Vec3(
        -- axis 1
        -- rotated x axis
        Vec3(
            math.cos(theta),
            math.sin(theta),
            0
        ),
        -- axis 2
        -- rotated y axis
        Vec3(
            -math.sin(theta),
            math.cos(theta),
            0
        ),
        -- axis 3
        -- rotated z axis
        -- we're rotating *around* the z axis, so it stays the same
        Vec3(
            0,
            0,
            1
        )
    )
end
如果你想象这会对你大脑中的x、y和z轴产生什么影响,那么所有这些余弦、正弦和符号翻转都可能开始有意义了。他们都在那里是有原因的

最后,我们到达拼图的最后一步,即应用这些旋转。矩阵的一个很好的特点是很容易合成它们。您可以非常轻松地变换变换-您只需变换每个轴!要将现有矩阵
A
转换为矩阵
B

function combinematrices(a, b)
    return Vec3(b * a.x, b * a.y, b * a.z) -- x y and z are the first second and third axes
end
这意味着,如果要对相机应用更改,只需使用此矩阵组合机制在每帧中稍微旋转方向。camera类的这些功能将提供进行更改的简单方法:

function Camera:rotateyaw(theta)
    self.trans = combinematrices(self.trans, yawrotation(-theta))
end
我们使用负θ,因为我们希望trans与相机的方向相反,用于投影。您可以创建类似的函数
function Camera:rotateyaw(theta)
    self.trans = combinematrices(self.trans, yawrotation(-theta))
end