我听说在OpenGL游戏中,让玩家移动的方法不是移动相机,而是移动整个世界。
例如,这里是本教程的摘录:OpenGL视图矩阵
在现实生活中,您习惯于移动相机以更改特定场景的视图,而在OpenGL中则相反。OpenGL中的相机无法移动,并且被定义为面向负Z方向位于(0,0,0)。这意味着无需移动和旋转相机,而是围绕相机移动和旋转世界以构建适当的视图。
我们为什么要这样做?
我听说在OpenGL游戏中,让玩家移动的方法不是移动相机,而是移动整个世界。
例如,这里是本教程的摘录:OpenGL视图矩阵
在现实生活中,您习惯于移动相机以更改特定场景的视图,而在OpenGL中则相反。OpenGL中的相机无法移动,并且被定义为面向负Z方向位于(0,0,0)。这意味着无需移动和旋转相机,而是围绕相机移动和旋转世界以构建适当的视图。
我们为什么要这样做?
Answers:
为什么呢
因为,摄像机代表投影视图。
但是在3D相机(虚拟相机)的情况下,相机会移动而不是移动。我稍后对此答案做了详细的解释。
数学上的理解
投影视图在空间中移动并更改其方向。首先要注意的是,屏幕上所需的投影不会随视图方向改变。
因此,我们变换其他东西以获得所需的投影。
为了使外观看起来像移动照相机,您的OpenGL应用程序必须以与照相机变换相反的顺序移动场景。在OpenGL方面,没有相机。更具体地说,相机始终位于眼睛空间坐标(0、0、0)
还希望从http://open.gl/transformations的View矩阵部分共享以下行
要模拟摄影机转换,您实际上必须以该转换的逆向来转换世界。例如:如果要向上移动摄像机,则必须向下移动世界。
透视理解
在现实世界中,我们以一种“透视”的方式看待事物。
透视是指这样的概念,即距离较近的对象看起来要小于距离您较近的对象。透视图还意味着,如果您坐在一条直线道路的中间,则实际上会将道路的边界视为两条会聚线。
那是观点。透视对于3D项目至关重要。没有透视图,3D世界看起来并不真实。
尽管这看起来很自然且显而易见,但重要的是要考虑到,当您在计算机上创建3D渲染时,您试图在计算机屏幕(即2D表面)上模拟3D世界。
想象一下,在计算机屏幕后面有一个真实的3D场景,而您正在通过计算机屏幕的“玻璃”观看它。使用透视图,您的目标是创建代码,以渲染在屏幕“玻璃”上“投影”的内容,就像在屏幕后面有真实的3D世界一样。唯一需要注意的是,这个3D世界不是真实的……它只是3D世界的数学模拟。
因此,当使用3D渲染模拟3D场景,然后将3D场景投影到屏幕的2D表面时,该过程称为透视投影。
从直观地设想您要实现的目标开始。如果对象离观看者更近,则该对象必须看起来更大。如果对象距离较远,则必须看起来较小。另外,如果某个对象正沿着一条直线远离查看器移动,则当它向远处移动时,您希望它朝屏幕中心收敛。
将视角转化为数学
当您查看下图的插图时,请想象一个对象位于3D场景中。在3D世界中,对象的位置可以描述为xW,yW,zW,是指以原点为视点的3D坐标系。在屏幕之外的3D场景中,实际就是对象的位置。
当观看者在屏幕上观看该对象时,会将3D对象“投影”到称为xP和yP的2D位置,该位置参考屏幕的2D坐标系(投影平面)。
要将这些值转化为数学公式,我将使用3D坐标系作为世界坐标,其中x轴指向右侧,y指向上方,z指向屏幕内侧。3D原点是指观看者眼睛的位置。因此,屏幕玻璃位于与z轴正交(直角)的平面上,位于我称为zProj的z处。
您可以通过将世界位置xW和yW除以zW来计算投影位置xP和yP,如下所示:
xP = K1 * xW / zW
yP = K2 * yW / zW
K1和K2是从几何因素得出的常数,例如投影平面(您的视口)的纵横比和眼睛的“视场”(考虑了广角视觉的程度)。
您可以看到此转换如何模拟透视图。随着与眼睛的距离(zW)的增加,靠近屏幕两侧的点将被推向中心。同时,更靠近中心(0,0)的点受眼睛距离的影响要小得多,并且保持靠近中心。
z除法是著名的“透视除法”。
现在,考虑将3D场景中的对象定义为一系列顶点。因此,通过将这种变换应用于几何图形的所有顶点,可以有效地确保对象在远离视点时会收缩。
其他重要案件
为了更好地理解3D相机,请想象您正在拍摄电影。您必须设置要拍摄的场景,并且需要照相机。要获取素材,您将使用相机在场景中漫游,以不同的角度和视角拍摄场景中的对象。
3D相机执行相同的拍摄过程。您需要一个“虚拟”摄像机,该摄像机可以在您创建的“虚拟”场景中漫游。
两种流行的拍摄方式包括通过角色的眼睛观看世界(也称为第一人称相机)或将相机指向角色并保持其视线(称为第三人称相机)。
这是3D摄像机的基本前提:这是一个虚拟摄像机,可用于在3D场景中漫游,并从特定角度渲染素材。
了解世界空间并查看空间
要对这种行为进行编码,您将从摄像机的角度渲染3D世界的内容,而不仅仅是从世界坐标系的角度或其他某些固定的角度进行渲染。
一般来说,3D场景包含一组3D模型。这些模型定义为一组顶点和三角形,并参考它们自己的坐标系。定义模型的空间称为模型(或局部)空间。
将模型对象放置到3D场景中后,您将使用“世界变换”矩阵来变换这些模型的顶点。每个对象都有其自己的世界矩阵,该世界矩阵定义了对象在世界中的位置以及方向。
这个新的参考系统称为“世界空间”(或全局空间),一种简单的管理方法是将世界变换矩阵与每个对象相关联。
为了实现3D相机的行为,您需要执行其他步骤。您将参考世界,而不是参考世界原点,而是参考3D摄像机本身的参考系统。
一个好的策略是将相机视为3D世界中的实际3D对象。像任何其他3D对象一样,您可以使用“世界变换”矩阵将摄影机放置在3D世界中所需的位置和方向上。该摄影机世界变换矩阵将摄影机对象从原始的向前旋转(沿z轴)转换为实际世界(xc,yc,zc)位置和世界旋转。
下图显示了世界(x,y,z)坐标系和视图(相机)(x',y',z')坐标系之间的关系。
glMatrixMode()
引用的其他功能。但是,数学描述仍然正确且有用。
Mahbubar R Aaman的答案很正确,并且他提供的链接可以准确地解释数学,但是如果您想要的是技术性/数学性较差的答案,我将尝试另一种方法。
物体在现实世界和游戏世界中的位置是通过某些坐标系定义的。坐标系赋予位置值含义。如果我告诉你我处于“ 100,50”,除非您知道这些数字的含义(它们是英里,公里,纬度和经度等),否则对您没有帮助。如果它们是笛卡尔坐标(“正常”类型的坐标),则还需要知道它们相对于什么原点。如果我只说“我向东100英尺”,则需要知道“东西偏东 ”,这就是坐标原点。
有一个简单的方法可以考虑这一点。您可以告诉某人“火车站位于城市西南角以北3公里,以东1.5公里。” 您还可以告诉某人“火车站位于我现在所在的北部以北1英里处”。两个坐标都是正确的,并且标识相同地标的位置,但是它们是从不同的原点测量的,因此具有不同的数值。
在3D应用程序中,通常存在一个“世界”坐标系,该坐标系用于表示相机和游戏中物体的位置,该坐标系是使用由设计师指定的任意任意原点(通常是任意水平的中心)的笛卡尔坐标进行测量的或您正在玩的地图)。游戏中还存在其他坐标系,例如以相机为原点的笛卡尔坐标系。您可以随时随地定义任何新的坐标系,这在3D模拟中非常频繁地完成,以简化数学运算。
实际上将单个三角形渲染到屏幕上的算法以特定方式工作,因此渲染时直接使用世界坐标并不方便。数学运算并未真正处理诸如“对象在世界中心右边100个单位”之类的信息中。取而代之的是,数学希望使用“对象直接在相机前面,并且相距20个单位”。因此,在渲染数学上增加了一个附加步骤,以获取对象的世界位置并将其从其转换为相机坐标系。
当然,相机也具有位置和方向。因此,如果某个对象位于20,100,50位置,而摄像头位于10,200,-30位置,则该对象相对于摄像头的位置为10,100,80(该对象的位置减去摄像头的位置)。当照相机在游戏中移动时,该照相机在世界坐标中的位置将完全按照您的期望移动。
请注意,对象不会移动。他们保持原样。但是,现在相对于不同的坐标原点来表达它们的位置。仅当对象本身移动时,对象的世界坐标才会移动,但是只要相机移动,其相机坐标也会更改,因为它们是相对于相机位置的。
还要注意,您引用的教程中的描述只是简化的解释,不一定是OpenGL的准确描述。我认为这篇文章的作者并不理解这一点。作者只是试图使用简化的类比,在这种情况下引起混乱而不是消除混乱。
如果它有助于进一步理解数学为何关心相机坐标,请尝试以下练习:举起双手,用拇指和食指组成一个矩形(我们称其为“视口”),然后环顾四周入。找到一个对象,然后对其进行查看,然后环顾四周,但不直接对其进行查看。这样做时,请问自己:“对象在我的视口中在哪里?” 该对象具有一些特定的现实世界经度和纬度,可用于确定其在地球上的位置,但这并不能告诉您所看到的一切。不过,说“该对象在我的视口的左上角,看起来在2米左右”可以告诉您很多情况。您已经创建了相对于头部和方向的坐标系 根据您的视野重新定义对象。这基本上就是OpenGL / Direct3D的三角形栅格化器部分所需要的,这也是数学要求将对象位置和方向从其方便的世界坐标转换为相机坐标的数学要求。
仅仅添加其他两个(优秀的)答案就可以进一步解释Mahbubur R Aaman提到的一点:“没有相机”。
这是完全正确的,并且代表了常见“相机”类比的失败,因为“相机”实际上并不存在。重要的是要意识到相机的类比恰好是一个类比。它没有描述(或假装描述)事物在幕后的实际工作方式。
因此,将其视为(双关语意为)一种手段,可以帮助您快速了解这些东西(如果对您来说是新东西),但请始终记住,它只是一个帮助者,而不是对事物实际方式的任何描述。
现在,您有两类与此处相关的对象:视点和世界上的所有事物。您希望将视点移近一些对象,但是对于这种移动,无论视图移近对象还是物体移近视图,最终结果都是相同的。您正在做的就是改变它们之间的距离。由于当前距离为X并且您希望新距离为Y,因此移动哪个都无所谓,只要移动后新距离为Y即可。因此,您根本就没有移动只是在改变距离。(我并不是故意要以此来超越整个爱因斯坦……老实!)
但是,由于相机不存在,因此唯一可以更改距离的是物体。因此,您更改对象的距离即可得出非常相同的结果。由于所有对象无论如何都要进行转换,因此这或多或少是昂贵的。
简单的数学解释可能会有所帮助。假设所有坐标均为1D-视点位于0,您的对象位于4,并且您希望视点转到3。这意味着它们之间的距离将从4(4-0)变为1(4- 3)。但是由于相机不存在,因此您无法将其更改为0;它始终将为0。因此,您无需从3中添加3(您不能这样做),而是从4中减去3(您可以这样做)-这些对象现在为1,最终结果是相同的-视点和对象为1。
移动相机或移动世界是两个同等有效的选择,两者都是同一件事。最终,您将从一个坐标系切换到另一个坐标系。上面的答案是正确的,但是您以哪种方式看待它是同一枚硬币的两个侧面。转换可以以任何一种方式进行-它们只是彼此相反。
渲染过程的一部分确实将世界坐标转换为眼睛坐标。但是,对此建模的一种简单方法是在应用程序中使用虚拟相机对象。摄像机既可以表示投影矩阵(负责透视效果),又可以表示用于从世界空间转换为眼睛空间的视图矩阵。
因此,尽管顶点着色器使用视图矩阵将几何坐标更改为眼睛空间,但通常更容易想到在虚拟世界中移动的相机对象,当它移动时会重新计算视图矩阵。
因此,在您的应用程序中,您以世界坐标移动摄影机,更新camer'as视图矩阵,将新的视图矩阵作为统一或块的一部分传递给顶点着色器,以渲染场景。
我认为这种说法并非绝对正确,因为人们很少会“移动”游戏中的世界坐标,而是实际上会更改虚拟相机的坐标。
相机的概念实际上是将有限的视锥转化为一个具有8个角点(或由6个平面的交点定义)的截顶金字塔,转换为一个单位立方体,该立方体代表了openGL最终阶段的剪辑空间渲染管道。
从这个意义上说,世界并没有移动,而是仅在剪辑空间的坐标系中计算世界坐标。
这里有很多好的答案。我将不重复任何一个。有时候,从摄像机的角度来看更容易想到,例如Direct3D的工作方式(请注意:在9.0c之后的版本中玩的不多)
就像在Futurama中那样,“移动世界”的意思是有人引用它是一种很好的观察方式(“引擎根本不会移动飞船。飞船停留在原处,并且引擎绕着宇宙移动它!”)。对于2D游戏,这实际上很常见。从字面上看,您很难调整视口,有时这是您的视频RAM或UI窗口。如果OpenGL是出于这种原因而做的,那很难说。
当然,您也可以从相机的角度来思考2D运动,而这种思考过程可以使计算效果更加容易。
从OpenGL文档的作者开始,这里似乎存在很多误解。
让我迅速恢复您的理智:世界不会动,它会保持不变。任何试图在玩家周围移动来实现世界的人都将在多人游戏模式下迅速陷入困境。更不用说在每位玩家移动时更新世界上数百万(或数十亿)个对象的位置将使游戏过程变得相当缓慢...
那么,那里到底发生了什么,报价又是怎么回事?
好吧,首先,您需要了解坐标系的概念。通常,您选择世界上的一个点并将其声明为“原点”,即坐标为(0,0,0)的点。您还可以选择三个“主”方向,分别称为X,Y和Z。显然,有很多方法可以分配坐标系。通常有一个“世界坐标系”,在这个系统中世界是静止的(或多或少)。在游戏中,该系统将由关卡设计师选择。
现在,考虑与玩家眼睛相关的另一个坐标系也很方便。在此坐标系中,玩家始终处于坐标(0,0,0),世界围绕他移动并旋转。因此,如果您理解这是在玩家的坐标系中做出的,则报价是正确的。
但是,世界不是在玩家的坐标系中运行,而是在世界的坐标系中运行。在涉及两个坐标系的地方,总有一种方法可以将一种坐标转换为另一种坐标。在OpenGL中,这是使用4x4视图矩阵完成的。
最终,当玩家移动时,世界保持静止,而玩家移动。这是世界坐标,即对象在游戏中的存储方式。玩家还拥有一个与他相关联的观察相机,并且该相机类似地在世界各地移动(尽管OpenGL文档似乎在说什么)。但是,为了在用户的屏幕上显示世界,所有可见对象的坐标都使用转换矩阵转换为玩家的坐标系,然后应用其他投影来创建透视效果。在此玩家的坐标系中,世界实际上似乎在玩家周围移动。但这只是一种极其无助且令人困惑的思考方式。