仓库:https://gitee.com/mrxiao_com/2d_game_2
回顾我们之前停留的位置
在这段内容中,讨论了如何处理游戏中的三维效果,特别是如何处理额外的"Z层"。由于游戏中的艺术资源是位图而不是3D模型,因此实现三维效果变得非常具有挑战性。虽然可以通过让游戏保持在一个平面上并避免涉及上下的元素来简化问题,但这并不是当前的目标。相反,目标是探索如何在不放弃位图艺术的情况下,通过各种方法模拟Z深度,以增强游戏的三维感。
接下来,讨论了如何处理这些Z层的复杂性,尤其是在程序开发过程中没有一个完美的解决方案。相比于其他相对容易解决的问题,例如纹理映射,处理Z层涉及更多复杂的决策过程。在这一过程中,目标是逐步探索这些问题,并尝试找到清晰且有效的渲染设计,使得Z深度感能够在游戏中得到体现。
在之前的工作中,已经有一个包含两个Z层的测试世界,并且关闭了地面块的渲染。接下来的工作是重新启用这些地面块的渲染,并开始处理Z切片的概念。在这一过程中,开发者希望通过调整相机的位置并观察不同层级的元素如何变化,来更好地理解Z层的效果。最终的目标是让这些Z切片能够合理地展示,并为游戏中的三维效果提供更清晰的视觉呈现。
解释 Z 切片的必要性
这段内容讨论了在游戏中实现Z层次的具体变化,目标是确保Z层在游戏世界中能够有一致性和结构性。由于游戏使用的是位图艺术,而位图本身不能很好地表达三维信息,因此需要通过"固定的Z切片"来解决这一问题。
首先,提到的核心概念是"Z切片",这些切片与当前的"chunkz"概念相吻合,目的是将世界分成不同的层次。每个层次都被视为一个独立的实体,游戏中的所有对象都被归类到不同的Z切片中,并且不能同时跨越多个Z切片。如果要跨越多个Z层次,则需要在每个层次上都有相应的艺术资源,这样可以避免在视觉上造成混乱。
在设计中,如果不同层次之间存在视觉断层,虽然可能导致一定的视觉破裂,但这也是艺术风格的一部分,因此可以接受。如果这种断层效果影响太大,可能会考虑关闭某些缩放效果,使所有层次之间更为一致,确保艺术资源的顶部和底部对齐。然而,由于游戏并不依赖于高大的结构,基本上可以通过将世界视作一系列独立的"外壳"来处理这些问题。
通过这样的设计,游戏中的实体可以在这些Z层之间自由移动而不会造成问题,整体来看,这种方法应该是可行的。
看一下我们的 SimRegion 如何处理实体的解包
目前的工作是检查如何在游戏中处理实体的解包,特别是关于模拟区域(SimRegion)部分。解包实体的过程大致是这样的:通过相机查看所有被触及的区域,遍历这些区域并将所有的实体提取出来,然后将它们添加到模拟区域中,最后完成解包操作。
在这个过程中,当前的做法是维护一个"Chunkz"概念,它帮助追踪实体的Z层次,但并没有实际利用偏移的Z值(Offsetz)。这意味着,虽然相机的Z坐标是被保留的,避免了Z值丢失,但在当前的实现中,相机在不同Z层之间的过渡并不是平滑的,而是以跳跃的方式进行。也就是说,相机在Z层之间移动时,并不会产生流畅的过渡,而是直接从一个层次跳到另一个层次。
最终,目标是使得相机在Z层之间的过渡更加平滑,而不是采用目前的跳跃式过渡。

去掉 ZOffset 并重新启用 DrawRectangle
在这个过程中,决定移除之前添加的Z偏移量(offset),并回退到之前的设置。Z偏移量的移除是为了简化代码并确保功能能够按预期运行。
在做这些修改之后,运行代码时发现之前的矩形绘制功能被禁用了,因此需要重新启用该功能。通过回顾代码,发现矩形绘制逻辑已经正确更新,只是因为工作时忘记启用绘制功能,所以没有显示出来。启用之后,矩形绘制功能恢复正常,房间的边界线再次显示出来。
此外,虽然目前没有艺术资源来填充楼梯的区域,但测试时能够看到角色成功地走上楼梯并进入上层。这表明Z层的逻辑在处理楼层过渡时已经能够正常工作,尽管在视觉效果上还没有完全实现。
关闭相机捕捉
在此过程中,决定关闭Z块的跳跃,使相机的运动更加平滑和连续。通过检查代码,发现相机的Z偏移量没有被使用,因此决定让偏移量基于相机的位置进行调整。通过这样修改,相机的Z轴运动变得更加平滑,能够看到层次之间的过渡效果。
接下来,需要引入一致性的概念,即控制哪些层在不同的Z轴位置上应当显示或隐藏。特别是当视角处于较低的位置时,地面可能会遮挡视线,使得上层内容无法被看到。因此,需要确保在视角向上移动时,原本遮挡的层会逐渐显示出来,避免不需要的内容在不合适的时机显示。
这一步骤的目的是确保视觉上的渐变效果,只有在相机移动到一定高度时,才会让上一层逐渐显现,从而实现更加自然和符合预期的画面过渡。
解释固定 Z 切片的概念
为了实现目标,首先需要引入"Z切片"的概念。这是为了确保能够明确知道相对于这些切片的位置。每个切片表示在世界中的不同高度层次,因此需要设计一个能够处理这些层次查询的系统。
目标是让世界结构能够回答以下问题:给定一个空间中的某个点,返回该点所在的地面层级,以及它上面和下面的层级。通过这种方式,能够更清楚地知道当前的位置以及所在层级,确保在不同的Z轴层次之间切换时能够获得正确的效果。
这意味着不需要在代码的其他地方到处手动管理每个层次的位置,而是通过集中查询来获取有关层次的信息。这样可以简化未来可能的修改,使得即便在未来需要为某些特殊区域设计不同的层次处理逻辑时,也能够在不影响其他部分的情况下处理这些特殊情况。
此外,提供了一个可以灵活处理不同区域特殊需求的查询机制,让世界结构可以在必要时做出相应调整,但对其他部分的逻辑不会造成影响。这种结构可以为以后可能的复杂情况留出空间,并使得世界的层次划分更加清晰、可维护。
移动 GlobalAlpha
在当前的世界结构中,使用了一个全局透明度值(GlobalAlpha),虽然这个值被认为不太理想,但目前会先用它来确保功能正常运行。在确保基础功能后,计划将全局透明度值移除,转而使用渲染目标的方式来处理渐变效果,使得不同层次之间能够实现更精确的过渡。
每个实体的透明度(alpha)值将根据其在世界中的位置来动态调整,而不再依赖全局透明度。这意味着每个实体可以根据其高度来设定透明度,而不需要通过全局透明度控制整个场景。
然而,当前存在一个问题,即在渲染实体时,无法准确确定敌人的最终位置,因为模拟尚未完成。这暴露了一个潜在的问题,即渲染过程与更新过程之间的耦合性过高。为了解决这个问题,可能需要在更新和渲染之间做出明确的分离,尽管这一点最初并不打算提前处理,但这可能是接下来需要考虑的重要改进。
关于分离实体的更新和渲染
之前讨论过一个问题,更新和渲染为什么要合并为一个过程。这样做的架构设计是为了提高效率,使得某些内容能够在同一过程内更新和渲染,避免不必要的操作。不过,在一些情况下,更新和渲染可能需要分开处理,尤其是在需要更多灵活性的实体和粒子系统中。
对于实体,考虑到目前的工作方式,可能会将更新和渲染分成不同的阶段,这样可以提供更多的灵活性和控制。而粒子系统等其他内容,可能更适合同时更新和渲染,因为这样做会更高效。
总的来说,建议在没有明确需求的情况下,保持更新和渲染一起进行,只有在确定分开能够带来效率提升时,才会将其分开。这样可以避免不必要的操作,比如频繁地从缓存中拉取和存入数据。但当前的阶段可以暂时不考虑这些优化,使用一帧的延迟来处理透明度问题,等以后再做进一步调整。
根据 GroundP 设置 GlobalAlpha
首先,设置了一个全局透明度(GlobalAlpha)值,并且希望根据实体的位置来调整这个透明度值。为了实现这一点,首先需要查看实体的实际位置,并通过一个查询函数(比如 getGroundPoint
)来确定该实体的地面位置。
然后,根据实体与相机之间的距离,来调整全局透明度值。相机的位置是固定的,通常位于同一空间区域的中心。通过计算实体位置和相机位置之间的差距,便可以得出透明度应该如何变化。
目前,全局透明度值的设定是一个临时解决方案,只是为了测试而使用,不打算长期保持。在代码中,使用了 getGroundPoint
函数获取实体的地面位置,并基于这个位置来调整透明度。但由于全局变量的使用存在一定的风险,因此希望将这种方法替换为更干净的实现方式。
总的来说,现在的透明度调整机制只是一个临时手段,用来测试效果,未来会考虑更合适的方式来处理这一问题,避免依赖全局变量,保持代码的清晰和可维护性。
引入 CameraP
在这里讨论了相机位置的处理问题。最初,认为相机的位置应该固定在原点,但考虑到将来可能需要支持相机不在原点的情况,因此决定加入相机位置的处理。目的是确保无论相机位置如何,都能正确渲染实体。
为此,决定引入相机位置的概念,并计算相机相对于当前区域的位移。通过这种方式,在渲染时可以根据相机的位置调整实体的位置,确保它们能够相对于相机正确显示。
虽然现在假设相机位于中心,但为了更灵活地处理将来的需求,相机位置会被计算并保存,以便在未来的渲染过程中调整。当前假设相机的初始位置为区域的中心,在实现时可以通过减去相机位置和区域中心点的位置来得出相机相对区域的位置。
这种方法确保了可以在不破坏系统的情况下调整相机位置,并且会自动处理在不同位置的相机。在实际实现时,可能会通过调整相机位置来实现不同的视角需求。

从 CameraRelativeGroundP 设置 GlobalAlpha
首先,想要做的是计算相机相对地面的位置。具体方法是,将地面点的坐标减去相机的位置,这样就能得到相对位置。接下来,通过计算该位置的 z 值来确定实体的高度,从而得出该实体相对于相机的高低。
为了实现渐变效果,使用某种函数来控制透明度,确保透明度的值在 0 到 1 之间。具体操作是,如果实体的位置在相机下面(即 z 值为负或零),则不进行处理。否则,采用某种方式来控制其透明度,设定一个距离阈值(例如 1.5 米),当实体超过这个距离时,它的透明度将稳定下来,逐渐消失。
看看游戏中的淡入效果
整体上,效果看起来相当不错。当角色向上移动时,透明度逐渐增加,产生了平滑的淡入效果;而当角色向下移动时,透明度则逐渐减少,产生了淡出效果。通过这种方式,实体的位置变化与透明度的过渡相结合,营造出了良好的视觉效果,表现出随着位置变化,实体在视觉上的渐变变化。
使其更正式一点
当前的透明度渐变处理方式采用了一种临时的、即兴的方式,因此需要进行规范化,以便能够基于实际值进行调整。首先,定义渐变的起始FadeStartZ和结束位置FadeEndZ,在z轴上划分切片,这些切片需要进行渐变处理。目标是明确渐变何时开始,何时结束,确保没有任何混乱。通过设定渐变的开始和结束位置,可以更加精确地控制每个切片的透明度变化,从而实现更加灵活和清晰的渐变效果。
Blackboard: 指定关卡的可见性
为了实现透明度渐变效果,需要对渐变区域进行参数化处理,确保能够控制不同层次之间的可见性。具体来说,可以通过设置多个渐变起始和结束位置来定义可见区域与不可见区域之间的过渡。首先,在摄像机与某个特定高度之间的区域内,物体将保持完全可见。然后,随着高度的增加,物体将开始逐渐消失,直到完全不可见。为了进一步细化控制,可能需要设置不同的渐变参数,以处理上下不同的渐变区域。比如,可以定义上方和下方的渐变起始和结束点,确保在某些特定高度区域物体不会完全消失,而是在视线中逐渐淡出。
此外,由于物体的高度范围可能不同,需要考虑不同层次的地面之间的渐变效果。为了避免遮挡视线,尤其是在俯视场景时,需要将每一层的渐变值根据其在场景中的实际位置进行调整。因此,每个地面层的渐变范围可能需要不同的处理方式,从而确保每一层的地面在适当的时候可以逐步显现或消失,而不会影响玩家的视野。
引入FadeIn淡入位置标记
为了实现渐变效果,需要定义渐变的起始和结束高度,并在这些高度之间进行平滑的透明度变化。首先,可以设定一个"渐变开始"的上方高度和下方高度(例如上方距离1米,下方距离2层楼)。这些值会决定物体开始淡出的时机和位置。上方和下方的渐变将根据设置的起始位置、结束位置以及透明度值来调整。
对于上方的渐变,物体会在从摄像机开始的某个高度处逐渐淡出,直到到达一个特定的高度(例如地面高度或某个层次),并且一旦超过这个高度,物体就完全不可见。对于下方的渐变,物体会在一个较低的高度开始淡出,直到到达另一个不可见的底部区域。
透明度(Alpha)值的计算将根据物体与摄像机的相对高度来决定。如果物体的相对高度大于"渐变上方起始值",则物体开始在上方渐变消失;如果物体的相对高度小于"渐变下方起始值",则物体开始在下方渐变消失。根据这些条件,可以动态调整透明度,实现上方和下方的渐变效果。
最终,整个渐变过程会根据具体的游戏世界设置来调整,使物体在不同高度区域内平滑过渡,不会直接消失或突然出现。
引入 Clamp01MapToRange
为了实现透明度渐变,计划引入一个范围函数,用于将一个值标准化到0到1之间。这个函数接收三个参数:最小值(min)、最大值(max)和目标值(t)。它的目标是计算出目标值相对于最小值和最大值的位置,并将结果转化为一个0到1之间的值。
首先,定义一个范围值,即最大值减去最小值。接着,通过检查范围是否大于零,避免除零错误。如果范围大于零,则计算目标值与最小值的差,并将其除以范围值,从而将目标值规范化为0到1之间。如果范围为负值,计算方式也能正常工作,只是会反转计算的方向,这依然是可以接受的。
通过这个函数,可以将相对位置(如相机相对的Z坐标)与渐变起始和结束的Z坐标进行比较,并计算出该值在渐变范围内的位置。根据计算的结果,可以决定渐变的透明度,从而实现平滑的过渡效果。



游戏中查看并修正淡入方向
接着,发现问题的根源可能是 fade end z
的设置问题。为了避免物体在未达到目标高度时就部分可见,fade end z
必须与地面高度对齐。即如果 fade end z
没有在地面高度时结束,物体就会部分可见,从而导致问题。
方便调试改一下窗口大小

允许我们上升到另一个层次
目前的世界生成系统只允许楼层之间堆叠两个房间,但希望能够扩展到多个楼层,以支持更多复杂的结构。当前的实现存在限制,无法自由选择上楼或下楼。要实现这一目标,可以修改房间生成逻辑,让门的方向(向上或向下)可以分别独立选择,从而允许房间在不同的高度进行堆叠,避免一开始就局限于两层。
在尝试解决这个问题时,遇到一个困境:生成的楼层没有正确堆叠,导致玩家可以走下楼梯进入一个空的空间。这是因为生成门时没有正确处理门的方向,默认假设门总是指向正确的位置。为了解决这一问题,需要修改生成逻辑,确保根据门的方向调整相应的高度。如果门是向下的,则需要调整 Z 坐标以便正确生成下层,如果是向上的,则相应调整 Z 坐标以生成上层。
为进一步调试,尝试优化了墙壁的生成,限制了墙壁的数量,以便减少渲染负担,同时避免不必要的计算。然而,当前的生成逻辑依然存在问题,例如玩家可以随意穿越墙壁,这需要进一步改进。
此外,为了测试多层堆叠房间的效果,需要调整生成逻辑,使其更倾向于创建多个上下楼的门,而不是随机生成。具体来说,可以引入一种方法,使得生成的楼梯不会重叠,从而可以自由地堆叠多个楼层,避免生成逻辑中出现重复的楼梯。最终,目标是创建一个能够生成多层建筑,并且每层之间有清晰上下通道的世界。
做了什么
在尝试修改生成逻辑以确保门始终朝下时,意图是将门的方向固定为向下,从而避免其他情况的出现。然而,这样的修改带来了一些意外的后果。现在生成的楼层似乎过多,并且发生了交叉现象,看起来好像超出了预定的阈值,并且可能触发了某种翻转错误。这个问题需要进一步调试,以了解为什么会有这么多层级,并找出原因。
同时,也对实体的生成产生了疑问。原本只打算在一个有限的区域内生成实体,但现在发现有许多实体出现在不该出现的地方。这个问题需要检查生成过程中是否存在误操作,特别是在 Z 坐标的处理上,可能导致了不必要的实体生成。


这是一个 bug
这是一个明显的错误,需要尽快修复。同时,另一个问题是为什么没有任何实体在逐渐消失。似乎某个环节出现了问题,导致应有的渐隐效果没有生效。需要进一步排查,找出问题的根源。
调查发生了什么
问题可能出在Z轴范围的计算上,导致从不应该包括的区域加载了过多的实体。看起来在查询时,不仅是与实体的Z轴坐标不匹配,而且查询范围异常大。这个问题可能是由最大实体半径导致的,使得查询范围过于宽泛,导致不需要的区域也被加载进来。
目前的解决方案是暂时关闭优化,逐步调试代码,以查看Z轴范围的实际值。通过这一步可以进一步确认是哪个参数(例如最大速度或最大实体半径)导致了异常范围的出现。
将 Z 的 MaxEntityRadius 设置为 0.0f
问题的根源在于由于最大速度的影响,Z轴的查询范围过大,因此会加载到不必要的区域。为了解决这个问题,决定暂时不使用实体半径来限制查询范围,这样可以减少加载的实体数量。接下来需要查看是否可以通过调整安全边距(safety margin)来进一步减少Z轴上的波动。
目前的思路是,只在Z轴上添加一个较小的边距,而不是过多地扩展范围,避免不必要的实体被加载。同时,检查update safety margin
是否正确生效,以确保在没有附加额外限制的情况下,Z轴的波动不会过大。接下来的步骤是检查这些调整是否能够有效地缩小查询范围。
将 Z 的 SimBoundsExpansion 设置为 0.0f,然后设置为 GameState->TypicalFloorHeight
首先,查询的初始范围被发现过大,最大值为15,最小值为-15,显得非常不合理。问题在于Z轴的处理没有考虑中心对齐,代码的设计显得非常随意,没有深思熟虑。考虑到这一点,认为在Z轴上不应添加任何额外的扩展,只需使用当前的楼层高度,并通过安全边距扩展来处理。
为了修正这一点,决定不进行任何Z轴的边界扩展,只在每一侧添加一个单位的安全边距。这样处理的目标是确保只包含当前区域,同时避免不必要的扩展。接下来,将尝试检查这一调整后的效果,确保没有遗漏或其他潜在问题,并观察这种方式是否能更好地控制范围。

查看这会带来什么
在修正了Z轴范围之后,发现问题仍然存在,尤其是在实体渐变淡出的部分。怀疑问题出在ClampMapToRange
的实现,可能是因为编写时有误。检查代码后发现问题的根源:在处理范围时,计算最大值和最小值,并进行加减运算后再除以它们,但没有考虑支持负数范围。原本的实现假设范围必须大于零,但实际上,应该支持负数范围。因此,这个问题的原因就是没有正确处理负数的情况,导致无法正确判断值是否大于零。
通过这一发现,能够确定问题出在对范围的处理方式上,需要调整代码以支持负值范围,从而解决这个问题。
修复 Clamp01MapToRange
经过检查发现,之前编写的代码其实是正确的,只是由于忽略了负数范围的处理,导致没有按照预期工作。曾经认为代码有问题,但实际上是理解上的错误。这次的发现确认了之前的代码是合理的,问题出在对负数范围的支持不够,导致了错误的行为。
在面对这类问题时,始终保持"总是假设存在bug"的心态是重要的,尤其是在处理复杂的物理计算时。
查看淡入效果并让 FadeBottom 生效
在进行调试时,发现淡出效果在顶层现在已经正确工作,采用了合适的代码。之前的代码本应该有效,但当时未能坚持正确的方法。现在,淡出效果从起始Z位置到结束Z位置,应该如预期般工作。然而,问题依然存在:效果消失了,可能是因为同一区域包含了某些过低的实体。
为了解决这个问题,需要重新审视相机的区域范围,考虑从相机的位置向下扩展一些,通常可以向下扩展到2或3层楼,向上则查看1层楼。此举可以避免不必要的区域扩展,尤其是在Z轴方向上的扩展。
接下来调整了相机范围,但发现没有正确执行。错误在于相机范围计算时使用的矩形没有正确设置最小和最大值。因此,需要通过手动设置这些值来确保正确的范围计算。最终,调整为从相机位置向下扩展3层楼,上面1层楼,这样可以避免不必要的复杂计算。
然而,问题依旧出现,即淡出效果的方向是错误的。通过回顾发现,这又是由于alpha值的设置问题。尽管代码应该按照从起始位置到结束位置的顺序写,但实际上需要反转alpha值,以便正确实现淡出效果。



查看所有效果并调整楼梯位置
现在,淡出效果已经能够按照预期正常工作,可以在两侧进行淡出。然而,堆叠的楼梯相互之间存在碰撞问题,导致玩家无法走下另一侧的楼梯。为了解决这个问题,决定采取一种方法,通过错开楼梯的位置来避免碰撞。
具体做法是判断楼梯的Z坐标是偶数还是奇数,通过检查Z值的绝对值是否为偶数来决定楼梯的位置。如果Z值为偶数,则将楼梯放置在一个位置;如果Z值为奇数,则将楼梯放置在另一个位置。这样做可以有效避免楼梯之间的相互碰撞问题。

走下各个层次并记录一些待解决的问题
目前,系统已经能够正常处理淡出效果,并且堆叠的楼梯碰撞问题得到了修复。接下来,观察到的一个新问题是,某些元素的缩放效果不正确,特别是当物体距离远时,它们似乎会快速缩放。这是因为没有正确处理透视效果的问题。
此外,还有另一个未解决的问题,就是当前的"伙伴物体"并没有被放置在正确的楼层上。这个问题的暴露正是为了后续的调整。虽然这些问题仍然存在,但它们也提供了一个清晰的目标,即要处理透视和地面处理相关的代码。地面处理还没有实现,当前的情况是相机的Z轴位置被误认为是地面位置,因此物体跟随相机的移动而下落。
整体来说,尽管存在一些问题,这些问题的暴露为后续的工作提供了非常清晰的方向,现在已经进入了能够彻底解决Z轴相关问题的阶段。接下来的重点是处理地面相关的功能,这样就可以进一步完善引擎的稳定性和性能。
为什么有些树是倒立的?
树木出现倒立的原因是因为系统尚未正确处理透视效果。当前所使用的Z轴"调整值"只是通过乘法将物体在屏幕上的X和Y位置放大或缩小,以模拟物体靠近或远离相机时的大小变化,但这种做法并没有实际计算透视。因此,这只是一种不准确的方法。
具体来说,当Z值足够负时(例如低于-0.0015),该表达式会变为负值,导致物体翻转。因为这种方法是基于乘法,而不是正确的透视计算,物体会被镜像翻转。正确的透视计算应该使用除法,而不是乘法。通过除法,物体会随着远离相机而变小,但不会发生翻转。
因此,解决这个问题的方法是引入真正的透视计算,通过除法来确保物体的缩放和显示更加真实,避免出现翻转现象。在实施之前,需要先在黑板上详细讲解透视原理,确保每个人理解这种转换的过程。
头部和身体实体偏离主角的位置是否应该随主角一起缩放?
头部和身体实体应该与主角一起缩放,当前它们的移动与Z轴有关。当实体移动时,它们被错误地当作与相机一起移动,而不是固定在正确的地面位置。问题的根本原因是目前系统没有正确处理地面位置,导致实体随着相机的位置变化而发生不正确的行为。
具体来说,地面处理的逻辑存在问题,这些实体被错误地认为与地面直接关联,似乎在相机下方移动,就像是地面在它们下面移开一样。当前的代码没有完成,导致无法确定实体为何会这样移动,但可以确定问题与地面值的处理有关,可能是因为这些实体正在与相机一起移动,而不是固定在正确的地面高度。
为了测试和找出问题的根源,可以尝试不更新相机的Z值,只让实体沿着Z轴移动。如果不更新相机Z值,实体会保持在原位不动,表明问题出在地面和相机的关系上。目前系统没有一个明确的地面处理方式,导致实体位置随着相机的移动而改变。
解决方法是加强Z轴的处理,确保实体和地面的位置正确关联,这样问题就能解决。
什么是 Z-fighting,它在这种游戏中需要担心吗?
Z-fighting 是指在3D图形中,由于两个物体的表面非常接近,导致它们在渲染时不断相互干扰,显示出闪烁或跳动的效果。通常发生在两个平面或多边形几乎在同一个位置时,尤其是在深度缓冲区的精度不足时。虽然Z-fighting是一个常见的问题,但在这种游戏引擎中不需要特别担心它。
简而言之,Z-fighting是因为渲染系统无法区分非常接近的两个表面,导致它们在屏幕上产生视觉上的冲突。尽管这个问题在某些情况下可能出现,但在当前的开发中,暂时不需要特别关注这个问题。
Blackboard: Z-Fighting
Z-fighting 是和深度缓冲区相关的。如果没有深度缓冲区,通常不会发生 Z-fighting。虽然可以在其他方面出现类似问题,但我这里先专注讲解深度缓冲区中的 Z-fighting。
在三维图形中,如果你没有处理 Z-fighting,通常会依赖一个叫做深度缓冲区(Depth Buffer)的东西来解决隐藏面移除(Hidden Surface Removal)的问题。这个技术可以用来决定在两个物体相交的地方,哪个物体应该遮挡另一个物体,从而避免错误的渲染。
举个例子,假设有两个立方体,一个离相机近,另一个远一些。当渲染这些立方体时,正确的顺序是先绘制远离相机的立方体,后绘制靠近相机的立方体,这样重叠的部分就能正确地被覆盖。
然而在三维图形中,由于物体的位置、角度的不同,如何判断哪个物体应该先绘制并不总是那么简单。如果物体比较复杂,可能需要不断调整绘制顺序。为了简化处理,通常的做法是逐像素处理:每个像素都有一个深度值,告诉它距离相机的远近。如果一个新的物体在绘制时,像素的深度值比原来的值小,就会替换原来的像素。
但如果深度计算精度不足,就会出现 Z-fighting。这个问题出现在计算机的浮点数运算中。如果两个物体非常接近,它们之间的距离差异可能非常小,以至于在浮点数表示中无法精确区分。这种情况下,当物体越来越远时,精度会丢失,导致这两个物体的深度值变得无法区分,从而导致 Z-fighting 的现象------物体在重叠区域交替显示,产生不希望看到的"闪烁"或"锯齿"效果。
特别是在深度值非常接近时,深度缓冲区的精度可能会不足,导致两个物体的深度信息丢失,从而产生 Z-fighting。比如,当一个物体的表面比另一个物体的表面更接近相机时,如果浮点精度不足,计算机会将它们的深度值视为相同,从而导致渲染错误。
此外,Z-fighting 还可能出现在没有深度缓冲区的情况下,比如在某些情况下物体的排序会受到浮点数精度的影响。没有深度缓冲区时,你可能会依赖排序算法来决定物体绘制顺序,但如果 Z 值过大或精度不足,可能会出现错误的排序,导致 Z-fighting。
总体来说,Z-fighting 的本质是由于浮点数计算精度导致深度值无法区分,最终导致错误的图形显示。