游戏引擎学习第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
)相加,可以得到所需的坐标变换。这两个公式在本质上是相同的,因此可以用这种方式处理。
另外,也可以对EntityGroundPoint
和EntityBaseP
的偏移进行类似的处理,只需将它们结合起来,再加上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
结构。
在调整对齐之后,检查了所有相关的元素,确保 sword
和 wall
都正确应用了新的对齐方式。至于 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
这个变换将首先对物体进行缩放操作,然后将其平移到新的位置。
通过分离线性变换和仿射变换的部分,可以更清晰地理解它们在几何变换中的作用和组合方式。在计算机图形学中,仿射变换被广泛应用于物体的变形、动画、摄像机视角转换等操作中。
二维齐次矩阵的例子
-
平移变换:
平移变换表示将一个点沿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 是平移的距离。 -
旋转变换:
旋转变换表示绕原点(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 θ 是旋转角度。 -
缩放变换:
缩放变换表示对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轴的缩放因子。
三维齐次矩阵的例子
-
平移变换:
在三维空间中,平移变换的齐次矩阵表示为:
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轴的平移距离。 -
旋转变换:
绕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轴)形式类似。 -
缩放变换:
三维缩放的齐次矩阵表示为:
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 渲染的细节。
相关文章:
游戏引擎学习第104天
Z轴有点悬而未决 我们正在进行渲染器相关的工作。之前我们已经完成了法线贴图的代码,虽然基本完成,但还有一些与光照相关的工作待完成。目前,天空和地面部分的光照处理已经相对正常,但局部光源的处理还没有做好。 为了进行光照部…...
OpenAI GPT-4.5技术详解与未来展望
一、GPT-4.5的技术突破 OpenAI在人工智能领域的持续创新再次引领了技术潮流。近期,OpenAI内部已经成功实现了GPT-4.5的开发,这一版本相较于前代在多个方面实现了显著的技术突破。 GPT-4.5在算法优化和数据处理上进行了多项创新,使得模型在对自然语言的理解上,尤其是在…...
【科技革命】颠覆性力量与社会伦理的再平衡
目录 2025年科技革命:颠覆性力量与社会伦理的再平衡目录技术突破全景图认知智能的范式转移量子霸权实现路径生物编程技术革命能源结构重构工程 产业生态链重构医疗健康新范式教育系统智能进化金融基础设施变革制造范式革命 科技伦理与文明演进 2025年科技革命&#…...
好好说话:深度学习扫盲
大创项目是和目标检测算法YOLO相关的,浅浅了解了一些有关深度学习的知识。在这里根据本人的理解做一些梳理。 深度学习是什么? 之前经常听到AI,机器学习,深度学习这三个概念,但是对于三者的区别一直很模糊。 AI&…...
一款基于若依的wms系统
Wms-Ruoyi-仓库库存管理 若依wms是一套基于若依的wms仓库管理系统,支持lodop和网页打印入库单、出库单。毫无保留给个人及企业免费使用。 前端采用Vue、Element UI。后端采用Spring Boot、Spring Security、Redis & Jwt。权限认证使用Jwt,支持多终…...
【ISO 14229-1:2023 UDS诊断(会话控制0x10服务)测试用例CAPL代码全解析⑥】
ISO 14229-1:2023 UDS诊断【会话控制0x10服务】_TestCase06 作者:车端域控测试工程师 更新日期:2025年02月14日 关键词:UDS诊断、0x10服务、诊断会话控制、ECU测试、ISO 14229-1:2023 TC10-006测试用例 用例ID测试场景验证要点参考条款预期…...
img标签的title和alt
img标签的title和alt 显示上 title:鼠标移入到图片上时候显示的内容; alt:图片无法加载时候显示的内容; <div class"box"><div><!-- title --><h3>title</h3><img src"./image/poster.jpg" title"这是封…...
redis cluster测试
集群节点信息这时候停掉一个master 172.30.60.31 从集群信息集中我们可以看到172.30.60.31的slave是172.30.60.41,查看41的日志,发现他成为了新的master 这时候我们在将172.30.60.41也杀死,会发现集群异常了 尝试把172.30.60.31启动ÿ…...
harmonyOS的文件的增、删、读、写相关操作(fs/content)
注意: 操作harmonyOS的文件只能对app沙箱内的文件进行操作 牵扯到两个支持点: fs和content这两个API; 具体的操作方法看下图: 创建文件 //js 引入 import fs from "ohos.files.fs" import featureAbility from "ohos.ability.featureAbility"; // 上下…...
12. Function Call实践
本文主要讲解大模型的function call能力,包括使用国内外在线模型和本地模型,以及如何微调以增强大模型的function call能力。 回顾下大模型学习思路: 1. 环境准备 1.1 平台上【模型调试】中创建 2. 调用国内外大模型测试 2.1 调用GPT-4 代…...
随机生成多孔介质matlab程序
matlab生成随机分布纤维代码,用于后续模拟处理 资源文件列表 随机生成多孔介质matlab程序.pdf , 5821...
华为云kubernetes基于keda自动伸缩deployment副本(监听redis队列长度)
1 概述 KEDA(Kubernetes-based Event-Driven Autoscaler,网址是https://keda.sh)是在 Kubernetes 中事件驱动的弹性伸缩器,功能非常强大。不仅支持根据基础的CPU和内存指标进行伸缩,还支持根据各种消息队列中的长度、…...
是时候说再见了
说再见 2018 to 2025 2018:学习 2018年开始读研。师兄师姐们说可以写写CSDN博客,对找工作也有帮助。于是在12月4日,发布了自己的第一篇文章[翻译] 神经网络与深度学习 首页 - Index。当时还在学习各种基础知识,看到了这个英文文…...
数据结构与算法-栈与队列的应用递归表达式求值
参考学习:B站-逊哥带你学编程 栈与队列的应用 题1: 答案:B 题2: 答案:C 题3: 答案:D 题4: 答案:C 题5: 答案:B 题6: 答案&…...
vue3.x 的shallowReactive 与 shallowRef 详细解读
在 Vue 3.x 中,shallowReactive 和 shallowRef 是两个用于创建浅层响应式数据的 API。它们与 reactive 和 ref 类似,但在处理嵌套对象时的行为有所不同。以下是它们的详细解读和示例。 1. shallowReactive 作用 shallowReactive 创建一个浅层响应式对…...
前缀和(Prefix Sum)算法笔记C++
前缀和(Prefix Sum)算法介绍 前缀和是一种预处理技术, 用于快速计算数组中任意子区间的元素之和. 它通过一次遍历创建一个辅助数组来存储从数组起始位置到当前索引位置所有元素的累加和, 从而使得后续查询操作的时间复杂度降低至 O ( 1 ) O(1) O(1). 定义 对于给定的数组 n…...
负载测试工具有哪些?
Apache JMeter Apache JMeter 是一款开源的性能测试工具,主要用于对 Web 应用程序进行功能、负载和压力测试。JMeter 支持多种协议和技术,包括 HTTP, HTTPS, FTP 和 WebSocket 等。通过模拟大量并发用户访问来评估应用程序的表现1。 jmeter -n -t testp…...
ROS进阶:使用URDF和Xacro构建差速轮式机器人模型
前言 本篇文章介绍的是ROS高效进阶内容,使用URDF 语言(xml格式)做一个差速轮式机器人模型,并使用URDF的增强版xacro,对机器人模型文件进行二次优化。 差速轮式机器人:两轮差速底盘由两个动力轮位于底盘左…...
windows编译使用gtest
下载: Release v1.12.1 google/googletest GitHub 编译: 编译源码中的CMakeLists.txt: 编译后生成的目录是BUILD: *(编译之前需要安装cmake: ) 关注下面这两个目录: 在vs中打开: 配置属性: 点击编译&a…...
C++编程,#include <iostream>详解,以及using namespace std;作用
在C编程中,#include <iostream> 是用来包含输入/输出流头文件的预处理指令。它允许程序使用标准的输入/输出对象如 std::cout 和 std::cin,以便与标准输入和输出流进行交互。这一头文件是编写输入输出操作时必不可少的部分。 讲到这里,…...
【愚公系列】《Python网络爬虫从入门到精通》008-正则表达式基础
标题详情作者简介愚公搬代码头衔华为云特约编辑,华为云云享专家,华为开发者专家,华为产品云测专家,CSDN博客专家,CSDN商业化专家,阿里云专家博主,阿里云签约作者,腾讯云优秀博主,腾讯云内容共创官,掘金优秀博主,亚马逊技领云博主,51CTO博客专家等。近期荣誉2022年度…...
Golang并发编程最佳实践:协程与通道
Golang并发编程最佳实践:协程与通道 本文旨在介绍Golang并发编程的最佳实践,重点讨论协程和通道的使用方法,以及相关的实际案例和代码示例。 一、Golang并发编程简介 又称Go语言)是一种由Google开发的编程语言,旨在提供…...
neural_gcm模型进行气象预测教程
简介 NeuralGCM (General circulation models)是Google开发的一种新型的天气和气候模型,它结合了传统的物理建模与机器学习技术,相关论文于2024年发表在nature上。它既可以做短期的天气预报,也可以做长期的气候预测。…...
怎么让DeepSeek自动化写作文案
在数字化时代,内容创作已成为企业争夺用户注意力的核心竞争力。面对海量信息需求,企业往往面临内容创作效率低下、质量参差不齐、周期长等问题。如何用技术手段解决这些痛点,成为企业迫切需要破解的难题。今天,我们将以DeepSeek为…...
淘宝/天猫获得淘宝商品评论 API 返回值说明||API评论接口
item_review-获得淘宝商品评论 taobao.item_review 公共参数 名称类型必须描述keyString是调用key(必须以GET方式拼接在URL中)secretString是调用密钥api_nameString是API接口名称(包括在请求地址中)[item_search,item_get,ite…...
全面理解-c++11的引用折叠
在 C 中,引用折叠(Reference Collapsing) 是一组用于处理多重引用(如 T& & 或 T&& &&)的规则,主要出现在模板参数推导、auto 类型推导和 decltype 的上下文中。它是实现**通用引用…...
考研高数复习规范
前言 这里记录我的高数复习规范与规划,希望能给需要考研的同学一点启发 规范原因 高数的内容很多,关键的是:会做题、拿高分首先最重要的就是抓住概念。比如有界无界的概念,间断点的概念、极限的概念其次是做题过程中得到的方法…...
2025 docker可视化管理面板DPanel的安装
1.什么是 DPanel ? DPanel 是一款 Docker 可视化管理面板,旨在简化 Docker 容器、镜像和文件的管理。它提供了一系列功能,使用户能够更轻松地管理和部署 Docker 环境。 软件特点: 可视化管理:提供直观的用户界面&#…...
「vue3-element-admin」Vue3 + TypeScript 项目整合 Animate.css 动画效果实战指南
🚀 作者主页: 有来技术 🔥 开源项目: youlai-mall ︱vue3-element-admin︱youlai-boot︱vue-uniapp-template 🌺 仓库主页: GitCode︱ Gitee ︱ Github 💖 欢迎点赞 👍 收藏 ⭐评论 …...
Python函数进阶250215
函数返回值进阶 函数返回的内容也是地址 def func01():data [1, 2, 3]# 执行到此步后,[1, 2, 3]这块内存会有一个引用计数器,且值为2,因为内部的data与外部的v1都指向这块空间return data v1 func01()# v1 point the address of data, if…...
VMware Workstation Pro 下载(无需注册账号)
VMware Workstation Pro 目前对个人用户完全免费。有两种下载方式,分别为 1、Broadcom 官网下载 需要注册账号,我懒得注册 2、VMware 更新服务器下载 如果不想注册账号,可以通过 VMware 更新服务器直接下载。访问以下链接: ht…...
USB2.03.0摄像头区分UVC相机在linux中的常用命令
这里是引用 一. USB2.0 & 3.0接口支持区分 1.1. 颜色判断 USB接口的颜色并不是判断版本的可靠标准,但根据行业常见规范分析如下: USB接口颜色与版本对照表: 接口颜色常见版本内部触点数量传输速度黑色USB2.04触点480 Mbps (60 MB/s)白…...
Linux入侵检查流程
1. 初步信息收集 1.1 系统信息 • 目的:了解当前系统的基本情况,包括操作系统版本、内核版本等。 • 命令: # 查看操作系统发行版信息 cat /etc/os-release # 查看内核版本 uname -r 1.2 网络信息 • 目的:查看网络连接状态、…...
(尚硅谷 Java 学习 B 站大学版)Day17 多态练习
Instanceof 关键字 一:引言 不能调用子类特有的方法、属性; 如何才能调用子类特有的属性和方法 向下转型:使用强制类型转换符 Man m1(Man) p2 注:其中p2 为 Person 类型 使用强转时,可能出现 ClassCastException …...
vscode配置django环境并创建django项目(全图文操作)
文章目录 创建项目工作路径下载python插件:创建虚拟环境 1. 命令方式创建2. 图文方式创建 在虚拟环境中安装Django创建Django项目安装Django插件选择虚拟环境 创建项目工作路径 输入 code . 下载python插件: 创建虚拟环境 1. 命令方式创建 切换在工作…...
NLP 八股 DAY1:BERT
BERT全称:Pre-training of deep bidirectional transformers for language understanding,即深度双向Transformer。 模型训练时的两个任务是预测句⼦中被掩盖的词以及判断输⼊的两个句⼦是不是上下句。在预训练 好的BERT模型后⾯根据特定任务加上相应的⽹…...
53倍性能提升!TiDB 全局索引如何优化分区表查询?
作者: Defined2014 原文来源: https://tidb.net/blog/7077577f 什么是 TiDB 全局索引 在 TiDB 中,全局索引是一种定义在分区表上的索引类型,它允许索引分区与表分区之间建立一对多的映射关系,即一个索引分区可以对…...
基于单片机的常规肺活量SVC简单计算
常规肺活量 SVC(Slow Vital Capacity)是指尽力吸气后缓慢而又完全呼出的最大气量。 成年男性的肺活量通常在 3500-4000ml 之间,成年女性的肺活量通常在 2500-3000ml 之间。 单片机一般通过外接流量传感器,使用ADC高速采集的方式…...
html 点击弹出视频弹窗
一、效果: 点击视频按钮后,弹出弹窗 播放视频 二、代码 <div class="index_change_video" data-video-src="</...
机器学习:二分类和多分类
1. 二分类(Binary Classification) 定义 二分类是指将输入样本分成两个互斥的类别。例如: 邮件 spam 或不是 spam。病人是有病或健康。物品是正品或假货。实现方法 二分类任务可以通过多种算法实现,包括: 逻辑回归(Logistic Regression):通过sigmoid函数将输出值映射…...
Win11配置wsl、ubuntu、docker
系统要求 安装WSL。 开通虚拟化: 准备工作 dism.exe /online /enable-feature /featurename:Microsoft-Windows-Subsystem-Linux /all /norestartdism.exe /online /enable-feature /featurename:VirtualMachinePlatform /all /norestartwsl --set-default-versi…...
机试题——稀疏存储
题目描述 在虚拟化技术、芯片仿真器等领域,存在一种场景,即实际读写的数据量比较小,但要求可访问的地址空间却很大(要求 4GB、甚至 128GB 地址空间)。 实现一个地址范围为 32G的,可在该地址范围内任意位置…...
双指针专题1:有效三角形的个数
描述 给定一个正整数n,输入一行包含非负整数的数组 nums ,返回其中可以组成三角形三条边的三元组个数。 输入描述 第一行输入一个正整数n 第二行输入n个nums[i] 输出描述 输出其中可以组成三角形三条边的三元组个数。 解释一个样例: 4…...
Ansible内置模块之file
ansible.builtin.file 模块用于管理文件和目录的属性。可以创建、删除文件或目录,修改文件权限、所有者等属性。 1. 选 项 说 明 选项必须类型默认值说明path是str无要管理的文件或目录的路径state否str无状态或类型。可为 file,directory,absent,link 等mode否str…...
DeepSeek 助力 Vue 开发:打造丝滑的侧边栏(Sidebar)
前言:哈喽,大家好,今天给大家分享一篇文章!并提供具体代码帮助大家深入理解,彻底掌握!创作不易,如果能帮助到大家或者给大家一些灵感和启发,欢迎收藏关注哦 💕 目录 Deep…...
手写一个Java Android Binder服务及源码分析
手写一个Java Android Binder服务及源码分析 前言一、Java语言编写自己的Binder服务Demo1. binder服务demo功能介绍2. binder服务demo代码结构图3. binder服务demo代码实现3.1 IHelloService.aidl3.2 IHelloService.java(自动生成)3.3 HelloService.java…...
考研操作系统----操作系统的概念定义功能和目标(仅仅作为王道哔站课程讲义作用)
目录 操作系统的概念定义功能和目标 操作系统的四个特征 操作系统的分类 编辑 操作系统的运行机制 系统调用 操作系统体系结构 操作系统引导 虚拟机 操作系统的概念定义功能和目标 什么是操作系统: 操作系统是指控制和管理整个计算机系统的软硬件资源&…...
java.lang.IllegalArgumentException: 在请求目标中找到无效字符。有效字符在RFC 7230和RFC 3986中定义
Tomcat 屏蔽错误信息。java.lang.IllegalArgumentException: 在请求目标中找到无效字符。有效字符在RFC 7230和RFC 3986中定义 <h1>HTTP状态 400 - 错误的请求</h1><hr class"line" /><p><b>类型</b> 异常报告</p><p&…...
ros:ur机械臂初识
这是用来可视化的launch文件 比如,我运行 roslaunch ur_description view_ur3.launch ur3模型 ur3e模型 ur5模型 ur5e模型 ur10模型 ur20模型 ur30模型 后来我搜了一下 UR5 和 UR10 都是由 Universal Robots(简称 UR)生产的协作机器人&…...
计算机视觉-局部特征
一、局部特征 1.1全景拼接 先用RANSAC估计出变换,就可以拼接两张图片 ①提取特征 ②匹配特征 ③拼接图像 1.2 点的特征 怎么找到对应点?(才能做点对应关系RANSAC) :特征检测 我们希望找到的点具有的特征有什么特…...