游戏引擎学习第104天

Z轴有点悬而未决

我们正在进行渲染器相关的工作。之前我们已经完成了法线贴图的代码,虽然基本完成,但还有一些与光照相关的工作待完成。目前,天空和地面部分的光照处理已经相对正常,但局部光源的处理还没有做好。

为了进行光照部分的处理,我们决定首先锁定渲染器的坐标系,确保其稳定性,因为之前坐标系的处理比较随意,存在许多不规范的地方。为了避免程序变得复杂,无法理解,我们决定整理这些规则,清理掉一些隐性的问题。周期性地进行这样的整理工作非常重要,因为如果放任这些问题堆积,它们可能会让代码变得越来越难以维护。

本周的目标就是清理坐标系和渲染器中的所有不一致之处,并确保坐标系在任何时刻都能被清晰地理解。这里面有一个细节需要注意,即Z轴的处理尚不确定。目前,Z轴的处理方式还有待明确,特别是在伪3D环境下,物体可能会从屏幕外部移出或移入,这要求我们在Y轴的基础上进行一些额外的处理。所以,在整理渲染器的坐标系时,也要同时考虑Z轴的处理问题。

此次整理工作也将帮助我们进一步规范和优化Z轴的处理,使其更加合理。我们决定在这个过程中同时考虑如何更好地管理Z轴,因为Z轴的正确处理是渲染器能否高效工作的关键之一。

回顾的进展

我们完成了法线贴图的工作,主要实现了天空和地面反射,效果相当不错。然而,我们注意到球体中间部分并没有反射任何东西,这部分的反射还没有处理。这是由于我们还没有处理绿色贴图相关的内容。虽然天空和地面的反射已经大致完成,但对于近景反射的部分(比如球体中间的反射)我们还需要进一步工作。

另外,当前渲染中,天空和地面的位置设置并不完全正确。天空和地面的坐标存在翻转问题,这是由于Y轴坐标翻转导致的。我们决定先不处理这些问题,暂时将重点放在坐标系的修正上。通过这次修正,我们希望确保坐标系的准确性,从而为后续的渲染工作奠定基础,特别是在法线贴图的处理中。

在坐标系修正完成后,我们会回到法线贴图的部分,确保一切都在正确的坐标系中进行处理。这个步骤至关重要,因为如果我们在坐标系处理不正确的情况下就去修复反射问题,实际上是在为已经存在的错误做修复,反而可能导致更多的错误叠加。为了避免这种情况,我们决定首先解决坐标系的问题,确保整个渲染管线中尽可能少的修正和错误。

总之,当前的目标是确保坐标系的正确性,然后再回到反射和法线贴图的工作中,确保所有渲染效果都在正确的基础上进行。

看一下game_render_group.h文件

目前,游戏的渲染工作组已经构建完成,渲染器的核心是通过"推送缓冲区"来实现的。推送缓冲区是用来存储所有需要渲染的元素的地方,任何需要渲染的内容都会被添加到这个缓冲区中,然后由渲染器执行相应的操作,将这些内容渲染到屏幕上。

目前,推送缓冲区中存储的内容类型还不多,但即使在最基础的情况下,也可以看到需要处理的问题。比如,当前用来进行法线贴图测试的部分,并不是我们关注的重点,因此可以暂时忽略它。我们现在的目标是重点查看其他部分,逐步进行优化和调整。

关闭法线贴图测试代码

接下来,要做的事情是切换回渲染之前的游戏版本,也就是在进行法线贴图测试之前的状态。从这个基础上开始,重新整理提交给渲染器的内容,确保所有东西都正确无误。此时,我们将移除法线贴图相关的代码,完全去掉这些部分。

一旦去除这些代码并运行游戏,就不再执行法线贴图,也不再渲染任何与坐标系统相关的内容。此时,游戏将回到一个更简洁、直接的状态,渲染变得更加简单,问题的定位也变得更加清晰。

开启位图渲染

首先,恢复到渲染游戏的正常状态,确保所有内容重新显示出来。记得之前我们有一个标准的房间设置,但已经很久没有处理渲染器部分,导致不记得当时具体的代码开启和关闭情况。看起来世界的构建依旧正常,包括地面块的初始化。之前可能关闭了生成地面缓冲区的功能,但可以将其重新启用,确保一切正常工作。

虽然在重新加载可执行文件时,地面缓冲区的重新生成是强制性的,但似乎没有必要手动开启这些功能。接着回到代码,发现所有调整和世界块的渲染似乎依然有效,所以推测当时其实只是在渲染组内关闭了渲染调用,主要是关闭了位图渲染。

通过简单的重新开启代码,渲染的状态很快恢复,整个世界的显示也恢复正常。唯一需要更新的是颜色值的更改,所以运行游戏后,所有元素再次出现在屏幕上,渲染恢复到了预期的状态。

debug 模式会比较卡,release 模式稍微快一点

目前就在release 貌似下面查看,如果有问题导release 模式,以前在进行优化

模式vs的debug 模式不能开启O2 选项

关闭黄色调试线

首先,为了清楚地看到地面块之间的接缝,决定关闭用于调试的黄色线条。黄色线条之前用于标识地面块的边界,但在当前调整中,这些线条可能会遮挡住一些接缝或边缘问题。因此,需要禁用这些线条。

关闭黄色线条后,能够看到地面块之间的接缝,这表明之前的渲染中可能存在顺序错误,尤其是与Y轴翻转(Yflip)相关。接缝看起来正好是在地面块的底部,暗示可能存在顺序问题,这可能导致渲染时块的位置不对。

因此,决定在进行坐标系统调整时关闭这些调试线条,以便能够更清楚地识别并解决可能的渲染问题,避免被调试信息遮挡住潜在的错误。

在game_render_group.h中定义几个内容

首先,确定了渲染器的坐标系统约定:在渲染器的外部,X轴始终向右,Y轴始终向上,这一规则不会有问题,唯一需要注意的是避免Y轴向上与向下的混淆。因此,需要确保在整个渲染过程中,Y轴始终按照统一的方向处理。

接下来,决定将所有位图切换为自底向上的格式。之前已经做了将位图设为自顶向上的工作,但现在由于渲染过程中的一系列变化,决定统一使用自底向上的位图格式。这意味着所有位图,包括渲染目标,都会按照自底向上的顺序存储,即位图的第一行指针指向屏幕上最底部的一行。

为了实现这一点,需要修改平台相关代码,尤其是在位图加载过程中进行相应的调整。

此外,还决定所有输入渲染器的坐标系统默认使用世界坐标,单位为米,而非像素。任何像素值的输入都将显式标记出来,确保坐标系统的统一性。

关于Z轴坐标的处理,确定世界将被分割成离散的Z层。这是基于前期工作经验,Z轴不应是任意的,而应当按照离散的层次进行处理。渲染器将理解这些Z层,用于排序和其他相关处理。虽然Z层之间可能存在间隙,但整体框架要求渲染器能够处理离散的Z切片。

最终,首先进行的任务是将渲染目标统一设为自底向上的格式,这相对较为简单,可以作为今天的任务开始。

回忆我们之前在做ResizeDIBSection时的工作

回顾最初工作时的代码,尤其是在处理窗口调整大小的部分,其中涉及了使用 virtual alloc 来分配内存块,该内存块用于作为显示缓冲区。当时按照窗口的规范,我们传递了一个 Buffer->Info 给 Windows,这个 Buffer->Info 告诉 Windows 如何解释我们分配的内存,以便能够正确地将其绘制到屏幕上。

需要注意的是,在这个 Buffer->Info 中,height 的值是负数。这是 Windows 的约定,用于表示图像是从上到下的显示方式。历史上,Windows 由于继承自 OS/2 操作系统,采用了自底向上的存储方式,而某些情况下需要从上到下的存储方式。为了实现这一点,Windows 使用负数的高度值来作为标志,表示该图像应按照自顶向下的方式处理。

因此,要实现这一点,只需要在传递给 Windows 的 Buffer->Info 中,将高度设为负值,这样就能确保图像按照预期的自顶向下方式被正确解析和显示。

翻转Buffer->Height的符号

首先,需要对平台端的操作进行调整。通过将该设置取反,当运行时应该能够看到变化,重新启动后,图像的确如预期被翻转过来了,第一行已经位于底部,这符合预期。

接下来,需要进入渲染器部分,进一步调整渲染流程,确保它能够按照新的方向正确地渲染图像。

浏览game_render_group.cpp中的函数

首先,检查了渲染组中的代码,发现代码中有很多与坐标相关的部分,包括处理负偏移的情况。虽然无法一次性解决所有问题,但可以逐一查看这些部分。这也是当前进行调整的合适时机,因为之前的代码大多是探索性的,现在已经进入优化阶段,编写的代码将需要长期保留,并且一旦有很多代码后,再进行细微调整会变得困难。因此,确保从现在开始使用一个合理的坐标系统,避免以后再进行类似调整。

检查中发现,大部分新的代码已经按照"Y轴向上"的规范来处理坐标,这包括了环境贴图采样和其他相关部分,表现正常。对于图像的预处理步骤,也没有发现问题,显示的结果符合预期。

进一步检查了绘制位图(draw bitmap)部分,发现这部分代码也遵循了预处理步骤,确保没有任何偏移问题。对于矩形绘制部分,Y轴和X轴的处理方式也很直观,没有出现方向问题。

综上,代码大部分部分都看起来很正常,没有出现任何明显的错误或不符合预期的行为。

到达GetRenderEntityBasisP,并修改它以对称处理X和Y

现在开始处理包含了内嵌逻辑的部分,发现所有坐标点在传入时Y轴方向是反向的,且可以看到存在负数的情况。一个有趣的地方是,如果需要的话,可以通过调整EntityGroundPoint来处理。具体来说,利用屏幕中心和实体基点坐标(EntityBaseP.XY)相加,可以得到所需的坐标变换。这两个公式在本质上是相同的,因此可以用这种方式处理。

另外,也可以对EntityGroundPointEntityBaseP的偏移进行类似的处理,只需将它们结合起来,再加上Z轴的偏移量。这样一来,逻辑看起来是合理的,并且没有发现任何异常的情况。

总结来说,现有的坐标变换方式在整体结构上是合适的,所做的修改与调整也没有引入不正常的行为。

继续浏览函数,并去除所有Y取反的实例

在渲染组的输出调用中,代码主要用于解释推送缓冲区中的内容。检查代码后发现,没有特别奇怪的地方,基本上只是通过调用工具函数来传递数据,并没有特别处理Y轴。在测试代码部分,也没有太多需要关注的内容,稍后会再回顾。在内存分配器部分,没有发现异常,推送渲染元素只是用于在推送缓冲区中创建数据。进入到推送部分时,发现代码依然有负号的处理,需要去除这些负号,尤其是对齐部分。

查看资产加载时如何计算Align,并引入SetTopDownAlign

对齐部分很有意思,因为它涉及到Y轴如何处理的问题。要理解对齐的来源,需要回顾加载位图时如何计算对齐值。加载位图时,发现对齐值是基于自上而下的方式来计算的,这种方式实际上是错误的。因此,考虑在头部进行修复,可能需要引入一个新函数来确保数据在加载时仍能按原始方式指定,因为这些值通常来自艺术工具(例如GIMP),而这与目标坐标系统不兼容。

计划通过引入一个函数来调整这些对齐值,使其符合新的坐标系统。此函数将确保数据处理方式正确,并且可以处理位图类型的情况。由于对齐值的大小一致,修复对齐问题的复杂度会相对较低。

在尝试解决这些问题时,遇到了一个问题,似乎有程序关闭或崩溃的情况,导致对齐处理过程中出现了一些混乱。

黑板:翻转位图对齐方式

在处理位图时,之前的做法是从上到下指定坐标,但现在需要改为从下到上指定,以符合新的坐标系统。为此,必须先知道位图的高度,因为只有知道了高度,才能计算出正确的差值。尽管目前所有位图的尺寸相同,这种方式暂时可以工作,但随着动画的引入,每个位图可能会有不同的大小。因此,未来可能需要为每个位图单独处理对齐值,而不是统一使用相同的对齐方式。

编写SetTopDownAlign函数

在实现 setTopDownAlign 函数时,主要任务是检查第一个英雄位图的高度,并调整对齐的Y坐标。由于位图的高度比最大Y值大1,需要确保在处理对齐时,Y坐标的最大值是49时,要将其设置为0。因此,需确保将对齐值减去1,避免出现"越界"错误。这种调整确保了当对齐值在边界时,能够正确地显示为0。这样,位图的对齐方式就得到了正确的处理。

回到game_render_group.cpp

在渲染组中,当我们减去对齐时,X 和 Y 坐标可以对称地正确处理,确保它们在坐标系统中的值是正确的。然而,这一切都是基于假设位图采用自下而上的坐标系统,而这一点还没有完成。因此,还需要确保位图的坐标系统是自下而上的。

在代码中,仍然存在 Y 偏移量的负值,这个负号是不需要的。此外,推送渲染元素的部分看起来没有问题,它只是将一些数据存储起来,并没有出现特别异常的情况。矩形轮廓部分也没有发现问题。整体来看,经过初步检查,所有这些部分都显得相对良好。

接下来需要继续处理剩下的部分,确保完成所有必需的调整。

在游戏中查看效果

目前,位图在正确的方向上移动,但它们应该是上下颠倒的。问题的根本原因是,位图还没有按照自下而上的方式加载。因此,接下来的任务是将这些位图加载为自下而上的坐标系统,这个过程应该比较直接。

下一步是打开游戏,检查加载位图的代码,并确保它们能正确地按自下而上的方式加载。

使位图从底部向上加载

在加载位图时,当前代码处理了位图的翻转问题。BMP 文件格式通常是自下而上的存储图像数据,为了适应这种格式,代码会先跳到位图的末尾,并将步长设置为负值,从而实现自上而下的加载。如果需要恢复到原来的顺序,只需要将步长设置为正值,去掉之前的跳步操作。

此外,考虑到未来可能加载不是按自上而下顺序存储的位图,代码会做出调整来支持这个功能。具体来说,通过检查位图头部的高度值是否为负,来判断位图是自上而下的格式。如果是,则不会影响当前的加载顺序,必要时可以通过条件语句恢复原有的处理方式。因此,虽然目前不使用这一功能,但代码并没有删除这一部分,而是保留以便将来根据需要使用。

在游戏中查看效果

目前的状态是,虽然大部分对齐工作已经做得相对正确,但仍然存在一些偏移问题。具体来说,在与树木和地面交互时,角色可以穿过树木线,而这个位置实际上应该在洞的下方。与此同时,角色在跳跃时,Z轴的方向也出现了问题,角色的Z值应该是向上,而现在却是向下。这些问题需要解决,以确保角色在环境中的行为符合预期。解决这些问题后,应该就接近完成了。接下来,重点将是修复Z轴的方向问题。

修正跳跃方向

首先,解决Z轴的问题时,发现它的根本原因是之前在处理时有一个取反操作,这个取反操作原本是为了让Y轴的上升方向从负数变为正数,因此导致了Z轴的方向问题。通过去掉这个取反操作,Z轴的方向就得到了修复。

接下来,仍然存在一个偏移问题,导致场景中的元素略微上移。这个问题可能是由于对齐设置错误,尽管尝试调整过对齐方式。经过检查,发现某些绘制元素,如树木或剑的对齐方式仍然存在问题。这些元素的对齐方式是硬编码的,因此可能导致它们的位置不正确。

为了修复这个问题,需要重新调整这些元素的对齐方式,确保它们的对齐方式正确并符合顶端向上的渲染顺序。通过修正这些对齐设置,应该能够解决偏移问题,使得元素的显示和位置更加准确。

修正位图对齐(暂时)

为了处理对齐问题,首先创建了一个简单的 TopDownAlign 函数,它能够根据给定的对齐方式调整Y轴位置,使得位图的对齐方式适应从上到下的顺序。这个函数会根据位图的高度来计算调整后的Y轴位置,确保位图按照正确的顺序渲染。

在代码中,使用这个新函数来处理 hero_bitmaps 的对齐,确保它们按照正确的顺序加载。这个函数的实现方法和之前处理其他对齐函数的方法类似,但它更灵活,允许直接对向量进行操作,而不需要依赖完整的 hero_bitmaps 结构。

在调整对齐之后,检查了所有相关的元素,确保 swordwall 都正确应用了新的对齐方式。至于 familiar,它仍然使用 hero_bitmaps 的对齐方式,因此不需要额外处理。

总之,新的 TopDownAlign 函数使得处理对齐更加简便,尤其是在加载图像数据时。通过这种方式,确保了所有位图和元素都能够正确对齐,解决了之前的偏移问题。

回到原始状态

现在,所有的Y轴翻转问题已经解决,位图已成功调整为自底向上的顺序,这对后续的图形渲染(无论是OpenGL还是Vulkan)都有很大帮助。这样做可以确保在软件渲染时,性能足够支撑游戏运行,同时避免未来硬件渲染管线中出现不必要的差异。软件渲染和硬件渲染的坐标系统保持一致,减少了混淆和调试的麻烦。

此外,虽然未来可能会使用Vulkan进行渲染,但当前的工作已经让渲染系统有了较好的基础,这为以后采用低级图形API(如Vulkan)提供了更多的灵活性。整体而言,底部向上的坐标系统不仅有助于当前的开发进度,还为未来的图形处理管线奠定了稳定的基础。

查看GroundBuffer排序

在调试过程中,发现地面渲染出现了错误,地面元素位置发生了翻转。这可能是由于在地面渲染过程中某个地方引入了bug,导致地面元素的位置被错误地计算。当前的代码使用了Y轴向上的坐标系统,但在地面缓冲区的处理过程中,有可能出现了与坐标系相关的问题。

具体来说,在渲染地面时,使用了PushBitmap调用,该调用依赖于对坐标系统的理解。推测其中一个可能的问题是,当进行米到像素的转换时,可能未正确处理Y轴翻转,导致渲染结果出错。此外,GetRenderEntityBasisP函数也可能在处理中存在不一致之处,造成地面渲染出错。

为了进一步排查问题,需要检查地面块构建代码中是否有硬编码的Y轴翻转。如果确实存在这种情况,可能需要去除其中的翻转操作来修正问题。

翻转FillGroundChunk中的ChunkOffsetY符号

在遍历多个地面块时,发现了问题的根源:地面块构建代码中存在一个硬编码的Y轴翻转操作。这一问题导致了渲染错误。通过识别并解决这个问题,已经成功修复了地面块的渲染问题。

讨论专注于当前任务的重要性,以及知道何时停止并进行修正

在处理代码时,曾经采用过一些快捷的方式,比如否定Y轴等,这些做法并不是错误的,而是为了集中精力解决当前的任务。这种做法是可以接受的,尤其在需要快速推进时。然而,重要的是要知道什么时候该停下来修正这些"临时"解决方案,因为这需要一定的经验。每次修正都会牺牲一些当前任务的进展和上下文,但如果不进行修正,问题会不断积累,最终影响代码的可扩展性和稳定性。因此,在有了足够的进展后,应该适时停下来整理代码,避免问题积累,从而保证代码的长期健康和可维护性。

现在的进展不错

目前已经解决了大部分问题,虽然可能还有一些细节没有完全修复,但整体上已经掌握了正确的处理方式。所有的位图,包括渲染目标,默认都是自下而上的坐标系统。此外,还有一些部分没有完全检查,这些需要进一步处理。预计这些任务将在接下来完成

提问:这项工作是否解决了法线贴图问题,还是需要专门处理?

需要仔细检查法线贴图的问题,特别是翻转是否会影响它。可以通过仔细检查来确保所有的细节都处理到位,尤其是对于完全不同的原因。当前,至少已经解决了位图的上下翻转问题,这是由于之前的代码将图像误解释为一个方向所导致的。

你认为将Y分量设为从下到上是否也有助于理解像重力这样的游戏机制?

将Y轴设置为向上并不会直接解决像重力这样的游戏机制问题,因为之前并没有遇到这些问题。大多数问题来源于缺乏真实的几何形状,需要在2D游戏中进行很多模拟,这增加了复杂性。然而,将Y轴设置为向上确实有一个好处,那就是它让数学运算变得更直观。比如,像正弦和余弦的圆形计算、顺时针和逆时针的旋转都能更符合预期。

我们会实现矩阵/仿射变换来处理坐标系的变化吗?

为了处理坐标系的变化,使用了仿射变换(affine transform)。这就是所做的内容。仿射变换是一种矩阵变换,可以用于实现坐标系之间的转换。

黑板:将传入的向量乘以矩阵

在处理矩阵乘法时,提到了齐次坐标系。给定一个输入向量 [ x y 1 ] \begin{bmatrix} x \\ y \\ 1 \end{bmatrix} xy1 包含x、y和一个常数1,其中x和y是坐标,1是齐次坐标。矩阵乘法的基本过程是将输入的坐标与矩阵中的各个元素相乘并求和。具体来说,矩阵中的第一列与x值相乘,第二列与y值相乘,第三列与常数1相乘。最终得到的新坐标值就是这些乘积的和。通过矩阵的这种形式,可以进行坐标转换,处理平移、旋转和缩放等操作。这样,使用矩阵乘法计算新的坐标值与原始方程式相一致,能通过这种方法来简化计算和转换。

在矩阵中,x轴和y轴的坐标分量分别在矩阵的相应位置填充,而原点的坐标则位于矩阵中的另一个位置。最后,通常会使用一个常数1来表示齐次坐标。尽管具体计算过程的细节可能会根据需求有所不同,但这种矩阵乘法的基本方法对于处理坐标系变换是非常有效的。

黑板:齐次矩阵

齐次矩阵通常用于仿射变换。仿射变换可以通过线性变换和一个平移部分来表示。线性部分负责旋转和缩放,而仿射部分则通过平移来调整坐标系的位置。

在计算机图形学中,仿射变换常常被分为两部分:线性部分和仿射部分。线性部分负责对点进行缩放和旋转操作,但不会改变原点的位置。而仿射部分则通过添加一个平移量来将变换后的坐标框架移到正确的位置。

通过对比线性变换,仿射变换的一个特点是,它可以改变坐标系的原点位置。如果原始坐标系和变换后的坐标系原点重合,变换后的坐标就会正确显示;但如果原点不重合,则需要通过添加一个平移量(即仿射部分)来调整。

线性变换的一个关键特点是,零坐标点(原点)在变换过程中不会发生变化。无论如何旋转或缩放,原点的位置不会被改变。但仿射变换通过引入平移部分,允许在变换中改变坐标系的位置,确保所有点都能正确变换。

因此,仿射变换的"仿射"部分就是通过添加平移量来实现的,能够将坐标系从一个位置平移到另一个位置,区别于线性变换只做旋转和缩放的部分。

齐次矩阵的常见例子是在二维和三维空间中的平移、旋转和缩放变换。为了方便表示,齐次矩阵使用了一个额外的维度来处理这些变换,使得线性变换(如旋转和缩放)和仿射变换(如平移)可以统一在一个矩阵中进行运算。

仿射变换(Affine Transformation)通常可以分为两部分:线性部分仿射部分。这两部分分别处理不同的变换操作,且通过矩阵的形式可以组合在一起。

1. 线性部分(Linear Part)

线性部分主要包括对坐标的旋转、缩放、剪切等操作,它们是保留原点的变换,即它们不涉及平移。线性变换保持了平行性和比例关系,但不会改变物体的位置,只是对其形状进行变换。

例子:

  • 旋转 :绕原点旋转一个角度θ。矩阵表示为:
    R = [ cos ⁡ θ − sin ⁡ θ sin ⁡ θ cos ⁡ θ ] R = \begin{bmatrix} \cos \theta & -\sin \theta \\ \sin \theta & \cos \theta \end{bmatrix} R=[cosθsinθ−sinθcosθ]

    这会对物体进行旋转,保持其形状和比例不变。

  • 缩放 :沿着x轴和y轴分别缩放一个因子sx和sy。矩阵表示为:
    S = [ s x 0 0 s y ] S = \begin{bmatrix} sx & 0 \\ 0 & sy \end{bmatrix} S=[sx00sy]

    这会改变物体的尺寸,但保持物体的角度和形状。

  • 剪切 :在x轴或y轴方向上进行剪切。矩阵表示为:
    H = [ 1 k x k y 1 ] H = \begin{bmatrix} 1 & k_x \\ k_y & 1 \end{bmatrix} H=[1kykx1]

    这会使物体沿某个轴发生倾斜。

2. 仿射部分(Affine Part)

仿射部分涉及到平移操作,即对坐标进行平移,使物体的位置发生变化。仿射变换允许在执行线性变换的同时,对坐标进行平移,这样可以使物体在平面中移动。

例子:

  • 平移 :将物体从原点移动到新的位置,平移向量为(tx, ty)。矩阵表示为:
    T = [ 1 0 t x 0 1 t y 0 0 1 ] T = \begin{bmatrix} 1 & 0 & tx \\ 0 & 1 & ty \\ 0 & 0 & 1 \end{bmatrix} T= 100010txty1
    这会将物体在平面上移动到新的位置,而不改变其形状。

线性变换和仿射变换的组合

仿射变换通常是线性变换和仿射变换的组合。在矩阵形式中,它可以表示为:

A = [ a 11 a 12 t x a 21 a 22 t y 0 0 1 ] A = \begin{bmatrix} a_{11} & a_{12} & tx \\ a_{21} & a_{22} & ty \\ 0 & 0 & 1 \end{bmatrix} A= a11a210a12a220txty1

其中,前两列组成了线性变换部分(旋转、缩放、剪切等),最后一列是平移(仿射部分)。通过这个组合,可以同时处理物体的旋转、缩放、剪切和位置变换。

例子:平移和缩放的仿射变换

假设我们要对一个点进行缩放(sx, sy)和平移(tx, ty)的变换。这个变换的矩阵表示为:

A = [ s x 0 t x 0 s y t y 0 0 1 ] A = \begin{bmatrix} sx & 0 & tx \\ 0 & sy & ty \\ 0 & 0 & 1 \end{bmatrix} A= sx000sy0txty1

这个变换将首先对物体进行缩放操作,然后将其平移到新的位置。

通过分离线性变换和仿射变换的部分,可以更清晰地理解它们在几何变换中的作用和组合方式。在计算机图形学中,仿射变换被广泛应用于物体的变形、动画、摄像机视角转换等操作中。

二维齐次矩阵的例子

  1. 平移变换

    平移变换表示将一个点沿x轴和y轴移动指定的距离。齐次矩阵形式为:
    T = [ 1 0 t x 0 1 t y 0 0 1 ] T = \begin{bmatrix} 1 & 0 & tx \\ 0 & 1 & ty \\ 0 & 0 & 1 \end{bmatrix} T= 100010txty1

    其中, t x tx tx 和 t y ty ty 是平移的距离。

  2. 旋转变换

    旋转变换表示绕原点(0,0)旋转一个角度θ。齐次矩阵形式为:
    R = [ cos ⁡ θ − sin ⁡ θ 0 sin ⁡ θ cos ⁡ θ 0 0 0 1 ] R = \begin{bmatrix} \cos \theta & -\sin \theta & 0 \\ \sin \theta & \cos \theta & 0 \\ 0 & 0 & 1 \end{bmatrix} R= cosθsinθ0−sinθcosθ0001

    其中, θ \theta θ 是旋转角度。

  3. 缩放变换

    缩放变换表示对x轴和y轴分别进行缩放。齐次矩阵形式为:
    S = [ s x 0 0 0 s y 0 0 0 1 ] S = \begin{bmatrix} sx & 0 & 0 \\ 0 & sy & 0 \\ 0 & 0 & 1 \end{bmatrix} S= sx000sy0001

    其中, s x sx sx 和 s y sy sy 是在x轴和y轴的缩放因子。

三维齐次矩阵的例子

  1. 平移变换

    在三维空间中,平移变换的齐次矩阵表示为:
    T = [ 1 0 0 t x 0 1 0 t y 0 0 1 t z 0 0 0 1 ] T = \begin{bmatrix} 1 & 0 & 0 & tx \\ 0 & 1 & 0 & ty \\ 0 & 0 & 1 & tz \\ 0 & 0 & 0 & 1 \end{bmatrix} T= 100001000010txtytz1

    其中, t x tx tx、 t y ty ty 和 t z tz tz 是沿x、y、z轴的平移距离。

  2. 旋转变换

    绕x轴旋转的三维旋转矩阵齐次表示为:
    R x = [ 1 0 0 0 0 cos ⁡ θ − sin ⁡ θ 0 0 sin ⁡ θ cos ⁡ θ 0 0 0 0 1 ] R_x = \begin{bmatrix} 1 & 0 & 0 & 0 \\ 0 & \cos \theta & -\sin \theta & 0 \\ 0 & \sin \theta & \cos \theta & 0 \\ 0 & 0 & 0 & 1 \end{bmatrix} Rx= 10000cosθsinθ00−sinθcosθ00001

    其它轴的旋转矩阵(绕y轴、z轴)形式类似。

  3. 缩放变换

    三维缩放的齐次矩阵表示为:
    S = [ s x 0 0 0 0 s y 0 0 0 0 s z 0 0 0 0 1 ] S = \begin{bmatrix} sx & 0 & 0 & 0 \\ 0 & sy & 0 & 0 \\ 0 & 0 & sz & 0 \\ 0 & 0 & 0 & 1 \end{bmatrix} S= sx0000sy0000sz00001

    其中, s x sx sx、 s y sy sy 和 s z sz sz 分别是x、y、z轴的缩放因子。

组合变换

多个变换可以通过矩阵乘法组合成一个变换矩阵。例如,旋转和缩放变换可以组合成一个矩阵:
M = R ⋅ S M = R \cdot S M=R⋅S

这样可以在一个变换中同时执行旋转和缩放。

齐次坐标的优势

  • 统一变换处理:齐次坐标允许将旋转、平移、缩放等操作统一表示为矩阵运算,简化了变换操作。
  • 便于组合变换:通过矩阵乘法可以轻松将多个变换组合在一起,避免了手动计算每个变换的结果。
  • 处理投影变换:齐次坐标通过引入一个额外的维度,可以处理投影变换,这对于计算机图形学中的透视投影非常重要。

可能我错过了,但Z排序什么时候会做,以便在角色前后正确显示精灵?

关于在角色前后视觉上纠正精灵的零Z值问题,目前还需要解决Z切片的相关工作。一旦Z切片的处理方式确定,就可以开始添加排序来实现该功能。然而,目前仍然有一些不确定因素,因此还需要一些时间,尚不适合立即做出决策。

更准确地说,这部分应该叫"平移部分"。整个上部分是仿射的

关于矩阵变换的讨论,首先强调了平移部分是整个变换中的一个独立部分,其他部分主要涉及旋转、缩放等线性变换。线性部分包括旋转和缩放,平移则被认为是位移。尽管某些数学术语可能让数学工作者感到不适,建议使用无歧义的术语,以便于理解。具体而言,平移部分也可以理解为位移,而不是原点,因为原点可能带有主观性。

在实现中,实际上平移部分的矩阵并不需要被单独存储。矩阵通常会使用三个向量来表示,而不需要额外的矩阵部分。这些矩阵形式通常在进行投影变换时才会有所变化,特别是引入齐次坐标时,需要额外的部分来处理,但这在目前的情境中不需要。

目前阶段,对于理解这些数学细节,不必过早深入,因为这些概念还未被广泛应用。了解它们的基本概念和应用背景就足够了,不必急于完全掌握,等待实际应用时再进一步理解会更有效。

当你退出程序时,Visual Studio显示大约有8个线程终止。目前game Hero中有多个线程吗?

当程序退出时,Visual Studio 会显示大约有八个线程已终止。这是因为操作系统会在没有明确请求的情况下创建许多线程。可以通过调试器查看当前的线程情况,发现有许多线程并不是由代码本身创建的,而是操作系统在运行过程中自动生成的。例如,DirectSound 音频系统就需要创建三个线程,即使只是播放声音时也会涉及这些线程。此外,还有许多空的 DLL 线程,可能是内核线程在处理一些操作。

这些线程是 Windows 服务的一部分,并且是由 Windows 操作系统创建的,与游戏代码本身无关。虽然这些线程看起来是多余的,但它们是操作系统提供的基础设施,无法直接控制或避免。最终,游戏程序可能会实现自己的线程,但目前阶段还没有显式地编写任何线程,所有看到的线程都属于 Windows 服务,无法避免。

能否让渲染器设置为随着Y轴的移动而使世界向后弯曲?

实现让世界在沿着 y 轴移动时向后弯曲是可能的,但这实际上属于 3D 渲染的范畴。虽然实现这一效果并不复杂,但有一些额外的工作需要做。首先,需要对纹理进行透视校正,虽然如果不做透视校正,效果也能接受。在当前的渲染方法中,纹理映射是沿着某一方向进行的,而要实现弯曲效果,就需要改变纹理映射的方式,使得它沿着另一方向映射。

通常,3D 渲染使用三角形而非四边形进行操作,因此在实现时,可以考虑使用三角形网格进行纹理映射。尽管可以继续使用四边形网格,但实现起来会有一些麻烦,可能需要额外的工作来确保效果正常。

虽然不会详细讲解如何实现这一点,但对于有兴趣的人,可以通过参考一些博客文章,尤其是关于图形管线的文章,深入理解透视校正等相关内容。此外,学习这些技术可以作为一个挑战,帮助更好地理解 3D 渲染的细节。

相关推荐
饮长安千年月2 小时前
Linksys WRT54G路由器溢出漏洞分析–运行环境修复
网络·物联网·学习·安全·机器学习
红花与香菇2____2 小时前
【学习笔记】Cadence电子设计全流程(二)原理图库的创建与设计(上)
笔记·嵌入式硬件·学习·pcb设计·cadence·pcb工艺
一天八小时4 小时前
Docker学习进阶
学习·docker·容器
前端没钱4 小时前
前端需要学习 Docker 吗?
前端·学习·docker
拥有一颗学徒的心5 小时前
鸿蒙第三方库MMKV源码学习笔记
笔记·学习·性能优化·harmonyos
车端域控测试工程师5 小时前
【ISO 14229-1:2023 UDS诊断(ECU复位0x11服务)测试用例CAPL代码全解析⑰】
经验分享·学习·汽车·测试用例·capl
车端域控测试工程师5 小时前
【ISO 14229-1:2023 UDS诊断(ECU复位0x11服务)测试用例CAPL代码全解析⑪】
经验分享·学习·汽车·测试用例·capl
charlie1145141918 小时前
(萌新入门)如何从起步阶段开始学习STM32 —— 0.碎碎念
c语言·stm32·单片机·嵌入式硬件·学习·教程
网安Ruler10 小时前
泷羽Sec-黑客基础之html(超文本标记语言)
前端·学习·网络安全·html
啥也不会的菜鸟·11 小时前
Redis7——基础篇(五)
redis·学习·缓存