简单的3D投影和方向处理?
我目前正在做一个古怪的复古飞行模拟,我的3d项目遇到了一些问题,以至于我找不到关于这个主题的任何可靠的通用文档 在给定以下信息的情况下,如何将游戏世界中的简单位置向量转换为屏幕上的2d向量: 摄像机的位置 a摄像机方向(见下文) 视野 屏幕的高度和宽度(以及纵横比) 我也在寻找一种存储方向的方法,我已经编写了一个基本的向量库,但我不确定如何存储旋转,以便在相机(和投影代码)以及游戏中对象旋转的实际处理中使用。我目前正在考虑使用四元数,但是使用四元数而不是矩阵进行投影变换是否可能(而且容易) 在代码中实现四元数有什么好的来源吗?我必须为复数编写一个单独的库吗 谢谢您的时间和帮助:)注意:回答太长了 我在Love2D中做过一个类似的项目,它运行得非常快,所以我不认为在Lua中自己做数学运算而不是使用OpenGL(它无论如何都不会公开)有什么问题 与评论相反,你不应该气馁。3D方向和透视背后的数学其实很简单,只要你有了感觉 对于方向而言,四元数可能有些过分。我发现要进行旋转3D投影,只需要简单的3D投影和方向处理?,3d,matrix,lua,projection,quaternions,3d,Matrix,Lua,Projection,Quaternions,我目前正在做一个古怪的复古飞行模拟,我的3d项目遇到了一些问题,以至于我找不到关于这个主题的任何可靠的通用文档 在给定以下信息的情况下,如何将游戏世界中的简单位置向量转换为屏幕上的2d向量: 摄像机的位置 a摄像机方向(见下文) 视野 屏幕的高度和宽度(以及纵横比) 我也在寻找一种存储方向的方法,我已经编写了一个基本的向量库,但我不确定如何存储旋转,以便在相机(和投影代码)以及游戏中对象旋转的实际处理中使用。我目前正在考虑使用四元数,但是使用四元数而不是矩阵进行投影变换是否可能(而且容易) 在代
Vec2
、Vec3
和Camera
类。虽然在数学上有一些细微的差别,但在实践中,向量的向量可以生成完全合适的变换矩阵,变换矩阵可以生成完全合适的方向。矩阵是向量的向量,其优点是只需编写一个类即可处理这两个类
要投影矢量v
,请考虑摄像机的3个参数:
,摄像机位置的loc
Vec3
,一个trans
(也称为Mat3by3
的Vec3
),用于与相机方向相反Vec3
- (免责声明:从技术上讲,使用矩阵进行相机定向是有害的,因为小的舍入误差可能会累积,但在实际使用中这是可以的)
,用于确定透视图的比例因子。缩放
的zoom
(相对于相机)相当于处于2D中;也就是说,从透视图来看,没有缩放z
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