游戏引擎学习第129天

仓库:https://gitee.com/mrxiao_com/2d_game_3

小妙招:

vscode:定位错误行 一顿狂按F8

重构快捷键:F2 重构相关的变量

回顾并为今天的内容做准备

今天的工作主要集中在渲染器的改进上,渲染器现在运行得相当不错,得益于一些优化和组织上的改进。我们计划在清理一些代码方面做一些工作,尤其是整理那些我们现在已经理解和运作良好的部分。

昨天,我们意外地将渲染器恢复到了预期的工作状态,这让我有些惊讶。也许我做了些修改后,出现了预期的效果,我觉得这挺不寻常的,但幸运的是,我们成功了。

接下来,我们想做的事情是:我们已经把地面块(ground chunks)重新放回来了,并且标记了这些块的位置。接下来,我打算关闭这些标记,因为我们要处理渲染器的另一部分内容,特别是重新使用渲染器来重新合成位图。这涉及到做一些不使用透视变换的工作。我们希望在生成这些地面块时不引入透视变换,避免对其进行不必要的透视操作。

game.cpp: 关闭调试线

我们现在关闭了显示黄色边框的标记线,目的是让场景的边缘更清晰地显示出来。关闭这些标记线后,我们会看到地面块的渲染不再是无缝的,这正是我们预期的结果。问题的原因其实有些微妙,可能是大家已经很久没有讨论过地面块的事情了,所以这也是我今天重新提起它的原因。

最初我们使用地面块时,它能够生成无缝的纹理,但是现在为什么渲染结果会出现这种不规则的图案呢?这是因为我们已经在进行透视变换,而这种变换影响了地面块的渲染,导致它们不再是无缝的。这是一个技术细节,可能之前并没有专门处理过,直到现在才有机会来调整和修复这个问题。

打开是这样的

黑板:为什么地面瓦片在进行透视变换后不再无缝

现在出现这种图案的原因非常重要。之前在绘制地面块时,我们有多个瓦片相邻排列,比如瓦片A和瓦片B,它们是紧挨着的。当我们绘制瓦片时,我们会确保邻近的瓦片也一起绘制出来,以保证这些瓦片的边缘是连续的。因此,假如我们绘制了瓦片A,而瓦片B的某部分延伸到了A的边界,我们会确保在绘制A时,也会把B的部分一同绘制出来,这样就能保证图案的无缝衔接。

然而,这种方式假设了一个前提,即坐标系始终是一样的。这意味着在进行这些绘制时,无论怎样偏移,坐标系统是统一的,而我们只是简单地通过改变坐标的偏移量来调整瓦片位置。这个假设在我们使用正交变换(Orthogonal Transform)时是成立的。正交变换的特点是,无论物体离观察者有多远,投影到屏幕上的位置都不会发生变化。换句话说,正交变换下,远近对物体在屏幕上的位置没有影响,物体的水平和垂直位置保持不变。

然而,透视变换(Perspective Transform)则不同。透视变换的原理是,随着物体离观察者越来越远,它的投影会逐渐向屏幕中心收缩。物体越远离观察者,它在屏幕上的位置就越靠近屏幕的中心。这意味着,在使用透视变换时,物体的距离会影响其在屏幕上的表现位置。

在当前的渲染中,问题出在我们在使用透视变换时,忽略了这种影响。由于没有考虑到透视变换带来的效果,导致瓦片之间的无缝连接出现了问题。虽然我们并不一定非要切换到正交变换,依然可以保持透视变换,但我们至少需要意识到透视变换的影响,并在设置渲染时进行相应的调整,确保物体在渲染时能够正确地保持相对位置,从而解决这个问题。

黑板:使用透视变换做位移

例如,如果我们决定继续使用透视模式,在进行位置偏移时,我们需要确定一个准确的偏移量,这个偏移量用于调整当前视图中的中心点。为了实现这一点,我们需要计算出我们想要捕捉的区域的宽度,并按照这个宽度来进行位移。

然而,在之前的做法中,我们只是按照位图的像素大小来进行位移,这样做是没有意义的。位图的像素大小并不能告诉我们从一个位置到另一个位置需要偏移多少,因此这种做法是不合适的。

为了解决这个问题,最合理的做法是,虽然保持透视模式,但我们应该先确定一个我们希望捕捉的区域,并确保将相机放置在正确的位置,以便可以捕捉到该区域。然后再进行渲染操作。这样的方法会更加合理,也能解决透视变换带来的问题。

查看 FillGroundChunk 的工作方式

在填充地面块的过程中,当前的实现存在一些问题。首先,创建了一个渲染组,并通过地面块的高度来创建缓冲区,接着进行绘制操作。然而,在这个过程中,存在一个问题,就是没有考虑到相机的位置以及相机视野的大小。

在代码中,我们通过地面块的宽度和高度来绘制,使用的是地面块的尺寸(以米为单位)。看似这些操作会正确执行,但实际上,代码并没有考虑到当前相机的视野范围。虽然我们传递了地面块的偏移量和尺寸,但并没有告诉渲染器这些值应该如何映射到屏幕上。

渲染器会根据假设的显示器尺寸和变换来进行处理,而这些假设并没有考虑到地面块的实际尺寸。换句话说,渲染器只是根据默认设置假设所有东西都会对齐,但是没有明确指定渲染区域的大小,导致在调整相机视角或位置时,渲染结果会有差异。

目前,唯一执行的操作就是依赖地面块的宽度和高度进行渲染,但没有实际设置渲染区域的尺寸,因此不能确保渲染结果的准确性。为了使渲染正确工作,需要明确设置渲染区域的大小,并确保与相机的位置和视野匹配。

提议根据现实世界的坐标来处理 GroundBuffers

在考虑如何处理地面块时,未来可能需要向地面块中插入一些元素,例如当角色在上面走动时,可能会在地面上留下脚印。为了实现这一点,需要确保能够使用现实世界中的坐标系统来进行操作,而不仅仅是局限于像素级的坐标。

目前,渲染系统已经在一定程度上使用了这种方式,比如通过地面块的中心来进行计算和写入操作。为了实现更灵活的控制,应该考虑在地面块中写入数据时,能够依据现实世界中的坐标来进行操作,而不是仅仅依赖于块的局部坐标系统。这将使得插入数据(如脚印或其他元素)变得更加直观,并且能够与现实世界的坐标系统相对应,从而为未来的扩展和功能提供更多可能性。

遍历 FillGroundChunk

在处理地面块的渲染时,考虑到宽度和高度应该根据现实世界中的尺寸来设置,而不再仅仅依赖于像素或虚拟坐标系。因此,需要确保渲染缓冲区的宽度和高度是与实际的物理尺寸(如米或厘米)相对应的。这意味着,地面块的坐标应该是相对于这些实际尺寸来设置的。

具体来说,当前的坐标应该以地面块的中心为基准进行计算,而不需要外部干预。因此,所有的坐标和操作都应该基于现实世界的坐标系,以便在进行渲染时能够得到正确的结果。这样处理后,坐标将变得更加直观,并且避免了额外的复杂性。

这也意味着,在进行渲染时,我们应该使用世界坐标系,而不是依赖于局部的坐标系,这样可以确保渲染过程更符合预期,且方便后续的扩展和功能实现。

考虑告诉渲染器使用透视变换

在考虑如何让渲染器正确使用坐标变换时,首先发现中心坐标不再需要单独处理,因为默认情况下所有操作都会以中心点为基准进行。因此,实际上在渲染过程中,只需要确保所有操作都是从零点出发的,尤其是在处理当前地面块时,中心点只用于处理其他地面块的关系。

接下来需要确保的是,渲染中所有的缩放比例与标准地面块在平面上的缩放比例一致。也就是说,当从一定高度看地面块时,缩放应该与观察到的尺寸相匹配。

目前的难点在于如何在保持世界坐标的情况下调整渲染方式。即使将坐标变换方式更改为正交投影(orthographic projection),似乎也能正常工作。因此,如何处理这些坐标变换和缩放比例是目前需要理清的一个关键点。

首先写一个 API 调用给渲染器,启用无缝地面瓦片使用正投影变换

首先,计划是通过调用渲染器接口来设置所需的转换方式,并将其传递给渲染器。这样,在渲染器中会有一个方法来处理不同的变换情况,支持正交投影(orthographic)或其他方式。具体来说,目标是能够设置一个正交投影模式,传入地面块的宽度和高度(单位为米),确保在渲染过程中,可以将元素准确地放入对应的区域。

对于渲染的具体实现,首先会将渲染器组设置为正交模式,并传入缓冲区的像素尺寸和世界单位尺寸。这样,所有后续的渲染操作都会基于这些参数进行调整。通过这种方式,确保渲染过程中一切都按照世界单位进行,不会发生尺寸上的错误或错位。

总之,最终的目标是使渲染过程完全以世界单位为基础来进行,从而使得渲染的对象在地面块中准确显示,避免任何视觉上的不一致性。

提供在透视和正投影之间切换的功能

计划是要实现一个灵活的渲染设置,可以在正交模式和透视模式之间自由切换。首先,在渲染时,如果是使用正交模式,渲染器将会使用给定的宽度和高度(以米为单位)进行渲染。对于透视模式,渲染器将使用像素宽度和高度,并将其他必要的参数传递给渲染器,这些参数包括焦距、目标的距离以及MetersToPixels的转换比例。

具体实现上,透视变换的设置将包括传入焦距、目标高度、以及米与像素的比例,这些值将直接影响透视变换的计算方式。在渲染过程中,渲染器会使用这些数据来正确处理渲染物体的缩放和位置。对于渲染的设置,像素到米的转换比例会被计算出来,并用来调整屏幕上显示的物体大小和位置,确保物体的显示符合期望的视觉效果。

此外,在渲染过程中还需要动态调整相关设置,以便在每次变换时都能够准确地处理物理空间与屏幕空间之间的映射关系。因此,渲染器的配置也会包含一个持续跟踪显示尺寸(例如监视器的尺寸和显示区域)的方法,确保无论何时变换渲染模式,渲染效果都能够保持一致性。

总之,目标是通过清晰的设置和转换,确保无论是正交模式还是透视模式,都能准确反映物体在世界坐标和屏幕坐标之间的映射,并且允许灵活地在这两种模式之间切换。

game_render_group.cpp: 写透视函数

为了实现所需的渲染设置,首先需要定义一个渲染区域,其中包括一些关键参数。首先是宽度和高度,以像素为单位。这些像素尺寸将决定渲染区域的大小。然后需要一个MetersToPixels的转换比例,用来调整渲染区域的缩放。接着,还需要焦距和目标的距离等参数,这些决定了渲染的视角和深度效果。

虽然我们有这些参数,但目前有一些名称上的混淆。例如,"分辨率像素"(resolution pixels)和"分辨率像素X"并不完全相同,可能会造成一定的困惑。因此,需要在后续考虑是否要将这些值分开指定,或者是否它们需要不同的处理方式,特别是在渲染的不同场景下。

一旦设置了透视变换,就能够配置所有需要的渲染参数,这些设置将确保渲染区域的尺寸和视角符合预期。但同时,也需要考虑到可能需要根据实际情况调整这些参数,特别是在实现正交视角(orthogonal)时,可能需要略微调整参数以适应不同的渲染效果。

因此,当前的设置是基础,但仍需考虑未来可能的调整和优化,确保渲染过程中能够灵活应对不同的需求。

写正投影函数

在处理正交渲染时,首先需要定义一些关键参数,包括渲染区域的像素宽度和高度以及其在实际世界中的米制宽度和高度。这些参数将用于设置渲染区域的缩放和显示。值得注意的是,"像素到米"(PixelsToMeters)转换实际上不再需要,因为现在的需求更多是处理"MetersToPixels"的转换。

具体而言,"MetersToPixels"的转换会因不同的使用情况而有所不同。例如,转换比例的计算和应用依赖于具体的渲染需求。在渲染过程中,"MetersToPixels"转换会影响到如何根据实际世界的尺寸调整显示内容。

至于"PixelsToMeters"的转换,它在多个地方都有使用。为了处理反向映射和投影,渲染过程中只需执行一个简单的计算,这个计算基于焦距和距离的变化来进行调整。这样,通过直接的焦距计算,可以确定从相机到目标物体的距离,从而正确地显示渲染结果。

考虑如何让透视和正投影通过管道流动

如果我们要全面支持三维渲染,可能需要将现有的计算转换为齐次坐标(homogeneous coordinates)来处理这种转换,但目前不确定是否真的需要这么做。因此,正在考虑如何在渲染管线中便捷地处理两种模式(正交和透视)。

在这一过程中,一些参数的处理是非常简单的。例如,屏幕中心在正交模式和透视模式下是相同的,因此不需要做额外的转换。监视器的半尺寸(即"MonitorHalfDimInMeters")也很简单,因为这个参数直接由传入的实际尺寸决定。

总的来说,虽然需要确保两种模式之间的兼容性,但其中一些基本参数并不复杂,处理起来非常直接。

强制它们为直线型

可以考虑将正交和透视模式的处理方式分开,通过固定一些参数来避免引入扭曲现象。具体来说,可以强制要求在渲染时,传入的像素宽度和像素高度与实际的米到像素的转换值完全一致。这样就避免了引入扭曲的复杂性,因为如果同时处理这两者,就容易产生不一致的变换,导致渲染效果出现问题。

为了解决这个问题,可以让大部分渲染设置直接沿用已有的配置,这样就能确保渲染效果没有问题,并且避免了复杂的修改。然后只需要考虑如何设置焦距和目标的距离,确保变换正常运行。

在填充地面块(FillGroundChunk)时,可以将宽度和高度固定,不再允许它们产生变形。具体来说,可以将块的维度(ChunkDim)转化为米单位,并根据像素宽度与米单位之间的比例来确定米到像素的转换值。这样,就能确保渲染时处理的一致性和准确性。

清理并编译后再继续

目前面临的问题是,我们需要确保在进行任何进一步的操作之前,先检查当前的实现是否能够正常工作。在此之前,需要确保在传递给渲染组时去除了多余的参数。特别是要清理掉一些不必要的传入数据,比如分辨率的像素值,只保留实际需要的值。

为了确保没有引入错误,首先要编译并检查代码,确保没有因为去除或修改这些参数而破坏现有的功能。一旦确认这些基本的修改不会造成问题,可以再进行更复杂的处理和改进。

game.cpp: 使正投影版本正常工作

现在需要确保能够正确实现正交视图。当前的任务是,确保传入的"米到像素"的转换和其他附加信息能够顺利工作,并且已经传递了这些信息。然而,当前的渲染使用的是透视投影,因此,接下来需要思考如何关闭透视效果,以便能够正确切换到正交投影模式。

关键在于,找到一种方法来禁用透视投影的影响,从而使得正交视图能够正确呈现。这涉及到控制投影模式的切换,并确保所有的渲染操作都能按照正交投影的需求进行。

game_render_group.cpp : 在 entity_basis_p_result 中写入正投影路径

在渲染过程中,需要对原有函数进行修改,特别是函数的命名,可以改为"project",以便更好地描述其作用。现在的函数有一些操作是我们在使用正交投影时不需要的,比如与实体相关的部分。对于正交投影,我们希望做的是简化操作,比如:

  1. 结果缩放:正交投影下的结果缩放始终为1,因为正交投影不会进行任何缩放变化。
  2. 结果有效性:由于正交投影没有变换,因此结果的有效性始终为真,不会受到其他因素的影响。
  3. 坐标变换:对于正交投影,坐标变换仅仅是将传入的坐标值从米转换为像素,具体来说就是对原始坐标进行米到像素的转换。因此,正交投影下的结果坐标将直接等于原始坐标(在米到像素转换后)。

对于透视投影,则需要其他的处理,比如应用焦距与深度距离的计算。

为了适应正交投影,必须保证原始坐标(x, y)在变换过程中保持不变,因此,在正交投影下,不会进行任何额外的坐标变换。为了保证这一点,需要引入一些新的计算参数,确保能够正常处理坐标系的转换。

此外,问题还涉及到如何处理缩放因子。在正交投影下,不需要复杂的缩放计算,而透视投影需要关注焦距与目标距离的比例。所以,在处理不同投影模式时,需要考虑这些因素,确保每种模式都能够正确渲染。

最终,正交投影下,直接使用传入的原始坐标进行简单的米到像素转换,避免了不必要的复杂变换,而透视投影则保留原有的焦距和深度计算,确保两种投影方式能够正确切换。

game_render_group.h : 引入 bool32 Orthographic

在处理正交投影和透视投影的情况下,可以通过引入一个条件判断来区分两者的行为。相比于引入系数或进行仿射变换,条件判断显得更加简单直接。通过这种方式,可以在处理渲染时区分投影类型,从而简化计算。

具体来说,正交投影的设置中会将"orthographic"设置为true,而透视投影则将其设置为false。这样,渲染时可以根据"orthographic"的值来决定使用哪种投影类型,正交投影时只需要做米到像素的转换,透视投影则继续使用焦距和深度等计算。

关于焦距和目标距离的问题,正交投影不需要焦距和目标距离的具体值,因为它不涉及透视效果。而对于透视投影,焦距和目标距离是必须的。虽然可以将焦距和目标距离设置为非常大的值,但要注意确保Z剪裁平面不会造成任何物体被剪裁掉。为此,可能需要调整这些参数,以避免出现物体被不必要地裁剪的情况。

最后,在代码实现中,需要注意正交和透视投影的相关处理。特别是在设置"orthographic"值时,确保正确区分不同的渲染模式,以便在渲染时正确应用相应的转换和计算。如果遇到错误或问题,可以通过调试和修改相关参数来解决问题。

查看游戏内效果

现在需要做的就是确保一切正常工作,尽管目前看到的结果只是一个很小的块,但是接下来只需要解决具体的细节问题,应该就能顺利完成。

game_render_group.cpp : 遍历 entity_basis_p_result

我们需要检查渲染组的内容,特别是渲染基准的部分。在进行正交投影的变换时,目标是获取屏幕中心的坐标,并将其与米到像素的转换系数相乘,将传入的x、y值扩展到相应的像素范围。此时不需要对任何内容进行缩放,所有的东西应该保持其实际的米尺度。因此,计算时要确保所使用的尺度符合实际的米制单位。

更正 OriginalPP

发现了一个问题,原本的计算是错误的。应当使用 P 而不是 OriginalP。虽然这个错误可能对问题影响不大,但还是需要纠正。这一修改应该会给我们带来正确的变换结果,尽管当前依然存在一些问题。做出这个修正后,预期结果应该会符合要求。

game.cpp : 考虑 PushBitmapFillGroundChunk 中的作用

在进行操作时,遇到了一些问题,主要是在处理位图时。当前传递的高度值似乎是以米为单位的,这让我对每个"splat"在地面上的实际宽度产生了疑问。原本以为每个"splat"可能是5米宽,这显得过大,因此推测它们的尺寸应该更小,可能更接近半米。虽然仍然不清楚这些"splat"应有的准确尺寸,但合理的假设是它们应该相当小,接近地面上的草块尺寸。

不过,仍然存在一些问题,尽管尝试做了一些调整,输出的结果依然不太对,可能还需要进一步的修改。

game_render_group.cpp : 确保 MetersToPixels 计算正确

在检查当前设置时,确认了"米到像素"(MetersToPixels)转换是正确的。重点是确保缩放比例设置得当。对于正交视图来说,"米到像素"和"像素到米"是互为反转的关系。同时,显示器的半尺寸(MonitorHalfDimInMeters)也被正确计算。因此,当前的设置应该没有问题。

对于"米到像素"的转换,它应该是一个相对较大的值,因为这是在进行缩放时的关键因素。

领悟时刻:比例不应该是 1.0f

在调试过程中,发现了问题的根源。原本预期的比例应该不是 1.0f,而是与"米到像素"(meters to pixels)转换值相关的比例。因为原本的设置忽视了这一点,导致了比例计算错误,导致尺寸异常。经过调整,应该将比例值设置为正确的"缩放值",而不是固定为 1.0f。

尽管找到了问题所在,但现在依然看不到任何渲染结果,说明还有其他问题存在,需要进一步检查和解决。

game.cpp: 更正正射中的像素/米计算

在分析代码后,发现了一个简单的错误:在传递转换时,实际上传递的是"像素到米"(pixels to meters),而不是应该传递的"米到像素"(meters to pixels)。这个错误导致了问题的发生,逻辑上应该是米转换为像素,而不是反过来。因此,一旦修正了这个错误,问题应该就能得到解决。

离远一点方便查看

地面有缝查看一下填充的位置

把乘以宽高去掉

查看游戏内效果

目前,尽管距离预期的效果已经越来越近,但问题仍然没有完全解决。在渲染场景时,仍然可以看到一些明显的问题,尽管部分场景看起来已经接近正确。对于这些渲染出来的地面补丁,有些场景似乎比较接近预期,而其他场景则明显不对。

一些渲染的区域似乎出现了较大的错误,而有些区域则稍微接近正确,可能只是偶然的结果。问题可能出现在尺寸设置上,虽然这还不确定,可能是转换设置的错误。要确认这个问题,必须进一步调试,检查每个地面块的渲染方式,确定转换逻辑是否正确。

最终目标是确保地面补丁能够正确生成并显示,尽管目前的问题还没完全解决,正在接近最终的正确状态。

返回到全瓦片溅射并调整大小

目前的情况是,渲染出的大小似乎大致是合理的,但还不确定是否完全正确。对于某些物体的尺寸,可能存在一些疑虑。例如,如果我们将物体的大小设置为半米,感觉它们可能会稍显不合理,甚至1米的长度也似乎有些过大,尽管从渲染效果来看,物体的尺寸有点比预期的小。

有时,这些尺寸可能比我们常用的标准尺寸要小得多,因此对于设置为2米的物体,可能显得过于庞大。而且,实际使用中的尺寸可能比我们想象的要小一些,或者需要根据实际效果调整。整体来说,当前渲染结果看起来有些怪异,需要进一步检查和调整这些尺寸,以确保渲染出的物体与预期一致。

草地 和草 高度分别设置为2.0f和0.1f 比较合理

创建棋盘格模式以突出显示接缝

目前的思路是通过改变物体的颜色来帮助调试和查看效果。可以考虑通过某种方式,例如使用棋盘格模式来检查不同区域的显示。具体来说,可以在代码中根据地块(chunk)的坐标来设置不同的颜色。例如,当某个地块的X坐标和Y坐标满足特定条件时,给它们指定不同的颜色,如红色和蓝色,这样就能更清晰地看到不同区域的渲染效果。

接下来,在渲染位图时,需要将这些颜色应用到相应的区域,以便能直观地了解渲染是否符合预期。这个过程中可能会使用到一些调试工具,帮助检查问题所在,确保渲染结果能够正确显示出想要的效果。

虽然这只是一个调试思路,但通过这种方式,可以更容易识别渲染中的问题,找到需要修正的部分。

看到了接缝

目前的调试结果有些奇怪,虽然已经可以看到不同区域的渲染效果,但没有预期中的那么明显。不过,至少能在一些地方看到渲染的差异,虽然不如预期那么突出。问题虽然不大,但还是需要进一步调整。

接下来计划是继续修复那些不一致的部分,尤其是接缝(seams)问题,确保所有的渲染都能正常显示,避免出现明显的错误。现在的情况是,整体的工作进展还是比较顺利的,除了少数地方的轻微错误,其他部分看起来已经接近完成。

由于时间问题,需要暂停一下,等明天再继续完善。

改为从外面把参数传进去

build.bat: 切换到 -O2 并环游世界

目前,渲染的速度已经足够快,即使还没有实现多线程渲染,性能也没有明显的卡顿。观察到在重新创建地面区块时,只有很小的暂停,所以现在的实现已经相当平稳了。这表明,一旦将渲染过程放到独立线程中,地面区块的系统应该会变得非常稳定,几乎不会有卡顿现象。

总的来说,渲染过程已经接近理想状态,性能方面也在不断改进,下一步就是实现多线程,进一步提高效率。

我们什么时候会让所有内容硬件加速,利用旋转的强大能力?

我们目前已经具备了旋转的能力,只是目前并没有实际的需求去使用它。可能之前没跟上进度的朋友没有注意到,我们已经实现了旋转功能,只是现在还没有真正应用到项目中。如果以后有需要旋转的场景,我们完全可以启用这个功能,已经具备了相应的支持。

game_render_group.cpp: 演示旋转

在渲染过程中,如果需要的话,可以随时改变绘制的位图。例如,我们可以修改位图的坐标轴,使得渲染时所有的位图都被旋转。这意味着,不需要依赖硬件加速,也能实现旋转效果。只需调整位图的坐标变换,就可以达到预期的旋转效果。如果需要的话,甚至可以让不同线程上的渲染做不同的旋转,尽管这种方法会有些奇怪并且可能造成一些不可预见的效果。不过,这完全是可以实现的。总之,旋转功能已经可以在没有硬件加速的情况下实现,完全由软件完成。如果以后不需要这个旋转效果,只需要恢复正常的坐标轴就可以了。

不同线程旋转的不一样

我们会看到如何找到每帧之间"昂贵"的函数吗?目前我们只在像素渲染器上使用了 SIMD,因为我们知道

在调试和优化代码时,寻找每帧之间调用的开销较大的函数是非常重要的。当前,我们主要专注于调试视图,已经有了一些基本的工具来帮助我们识别性能瓶颈。接下来,我们计划添加一些额外的功能,比如一个简单的分析器,这样就可以更方便地识别那些在每一帧中被频繁调用的昂贵函数。

对于一个较大的代码库,尤其是当你对这个代码库不太熟悉时,通常会使用采样型的分析工具来帮助找出性能瓶颈。例如,可以使用像 tune 这样的工具来进行采样分析。如果你没有直接控制代码(比如你在一个别人写的项目中工作),你可能需要借助这些外部工具来获得分析数据。

而如果你是自己编写所有代码并且对代码库非常熟悉,就可以直接通过一些内置的工具来监控性能,并且进行优化。在这种情况下,性能分析工具的使用方法也会有所不同,可以选择在编写代码时直接集成一些性能监控代码,来帮助自己跟踪和分析函数的执行情况。

总结起来,寻找性能瓶颈的技巧有很多种,既可以依赖内建的调试功能,也可以借助外部的工具。如果在自己的代码中没有合适的工具,采样型分析工具将是非常有用的帮手。

你能简要说明一下我们在开始实现游戏玩法代码之前,计划对引擎做哪些改进吗?

在实现游戏玩法代码之前,还有一些事情需要完成。大部分内容已经列在待办事项中,所以接下来要做的主要是完成渲染器的工作。具体来说,想要加入一个粒子系统,并且引入一些光照效果。此外,我们在渲染器方面已经做了一些工作,现在需要完善这些内容。虽然有一些不确定因素,但基本思路已经有了。

关于资源流的管理,虽然实现起来比较简单,但我们需要实际去编写代码,因为现在我们是在启动时加载所有资源,接下来我们需要确保能够在后台异步加载资源。

调试代码方面也有一些简单的工作要做,比如处理字体、日志记录、图形化调试界面以及一些调整功能。这些工作虽然比较直接,但非常重要,因为它们能够帮助我们更好地调试和开发。预计这些任务会花费几周时间,工作量不小,但它们是必不可少的基础工作。

音频方面,计划实现一个基础的音频系统,预计大约花费一周时间。我们会展示如何简单地写入音频缓冲区,这是一个非常基础的实现,但也是实现音频功能的必要步骤。

总体来说,还有一些工作需要完成,虽然不复杂,但也是非常重要的。

这些将全部是非常游戏代码的内容

接下来的工作主要是进入游戏开发阶段。一旦完成了之前的基础工作,就需要重新审视一些已经完成的内容,确保它们适合游戏的实际需求。这一阶段将会更加关注游戏相关的内容,并且是游戏化的核心部分。

整个过程将会分为两个阶段。第一个阶段是架构展示阶段,这时会先处理一些关键的、准备好进入生产阶段的内容,比如实体系统的搭建、动画以及路径规划等。这个阶段的目标是确保一些核心系统能够稳定运行,为后续的开发打下基础。

第二个阶段则是生产阶段,主要是在确保了核心内容稳定的前提下,开始大量构建其他功能和内容。尽管这两个阶段有不同的目标和侧重点,但它们之间的关系是密切的,许多工作会相互交织在一起。例如,实体系统的开发可能会和其他系统同时进行,在不断调整和完善中推动整体进展。因此,这两个阶段并非完全分离,而是相互影响,动态调整。

现在,为了从渲染器中获得更多性能,你是否已经多线程化以在块中渲染屏幕?GPU 是按设计并行的,那这两者有什么不同?

目前为了提高渲染性能,采用了多线程渲染技术,将屏幕划分为多个块来并行处理。这种做法的目的是通过并行计算来加速渲染过程,从而提高整体的渲染效率。

关于问题中提到的"并行设计"和"多线程渲染"的不同,实际上它们是紧密相关的,但也有所区别。并行设计(parallel by design)通常指的是在设计系统时就考虑到如何通过并行处理来提高效率。它强调的是从系统架构上考虑任务分配和处理方式,使得多个任务可以同时进行。而多线程渲染则是并行设计的一种具体实现方式,通过使用多个线程同时处理渲染任务的不同部分(如渲染不同的屏幕区域),从而提高渲染的效率和性能。总的来说,前者是系统设计层面的概念,后者是具体的实现方式。

黑板:瓦片式与非瓦片式 GPU

有两种不同类型的GPU使用方式,一种是"分块(tiled)"方式,另一种是"非分块(non-tiled)"方式。非分块的方式与我们所用的方式基本不同,但也有一些相似之处。我们处理像素的方式与GPU的处理方式非常相似,唯一的区别在于我们通常一次处理4个像素,而GPU通常一次处理16个像素,且处理的块通常更大一些。比如,在我们所用的硬件上,处理的通常是4个像素,而在一些常见的GPU上,它们会以16个像素的块进行处理。这种处理方式与我们所使用的方式是相似的,但由于硬件的不同,处理的块大小有所不同。

非分块的GPU架构通常是通过处理三角形来进行像素操作,这种方式的内存控制与我们的方式没有太大关系。而分块架构的GPU与我们的处理方式非常类似,它们也会将屏幕划分成若干个块,然后独立地处理每个块。GPU通常会将这些块的处理任务放入队列中,然后使用多个独立线程并行处理不同的块。当一个块处理完成后,GPU会将其从队列中移除,并取出新的块进行处理。这种方式与我们处理图像的方式非常相似,尤其是对于移动端的GPU,它们通常采用这种分块架构。与此不同的是,桌面级的GPU通常不是分块架构的,它们的工作方式可能与我们所描述的有所不同,但这些具体的实现细节不太了解,因为并没有深入接触过桌面GPU的硬件设计或编程。

总体来说,分块架构的GPU常见于移动设备,而非分块架构的GPU则更多出现在桌面设备中。

如果没有更好的问题,是否内部将对象链接成任意数量的链表会对性能有影响?还是我错过了什么智能解决方案?

关于在任意数量的链表中内部链接对象的问题,通常来说,内部链接对象到多个链表并不会带来性能上的问题。但实际上,通常不会将对象内部链接到任意数量的链表中。原因是,当需要将对象链接到多个链表时,通常会选择外部链接。

如果你有一个已知数量的链表,那么可以在内部实现链表链接。也就是说,内部链接适用于链表数量固定的情况,而如果链表的数量是动态的或不确定的,通常会选择外部管理这些链表。因此,问题的关键在于链表的数量是否是已知的,已知数量的链表可以使用内部链接来管理对象。

我感觉地面块比树木在同一平面上移动得更快

感觉像是地面部队的移动速度比平面上的树木还要快。可能是这样,因为实际上并不确定地面部队是否和树木在同一位置上。到目前为止,甚至还没有确定这一点。因此,接下来需要去处理这个问题,清理和调整相关的部分。

游戏行业太难进入了。为什么潜在的程序员浪费时间?

进入游戏行业确实很困难,可能潜在的程序员会浪费时间在这个领域上,我不太清楚具体原因,也许是因为他们想要编程游戏吧。对于这个问题,我也不太了解,现在的游戏行业和很多年前的情况完全不同了。

我即将开始学习计算机科学学位。我喜欢游戏,创建游戏的概念很有趣,尽管我在数学方面不太擅长。鉴于此,你会建议我远离游戏开发,转向我原本的计划------软件工程吗?

如果你不擅长数学,但对编程感兴趣,特别是游戏开发,建议不要完全放弃游戏开发的方向。不过,游戏开发确实涉及大量数学,如果你对数学本身没有兴趣,可能不太适合从事游戏编程。数学在游戏开发中扮演着非常重要的角色,比如图形学、物理模拟等方面。至于如果你仅仅是在高中时数学不好,但对编程有兴趣,仍然可以尝试,因为实际的编程工作并不完全依赖于高中的数学成绩。

我有 Ruby 和 JS 背景,我想知道你们是否通常在游戏开发中做单元测试/行为测试,还是因为有视觉元素,做起来太难了?

在游戏开发中,确实可以进行单元测试,尤其是对于一些核心组件,比如内存分配器等。对于这些复杂的、独立的系统,单元测试是有意义的,可以帮助发现一些潜在的bug。然而,对于整个游戏本身,进行有效的单元测试是非常困难的。游戏的交互性和视觉效果使得构造能够有效发现bug的单元测试变得非常复杂且成本高昂。因此,游戏开发中通常采取的是更随机化的测试方法。通过这种方式进行一些自动化测试,可以帮助发现一些问题,但这并不是完全依赖单元测试的解决方案。

我们目前知道每帧游戏逻辑的毫秒数吗?目前我们推的是 60 FPS,虽然是硬锁定的,可能我们已经有一些空闲的空间来处理游戏逻辑了?

目前游戏逻辑每帧的时间应该是相当充裕的。虽然游戏的帧率设定为每秒60帧,并且它是锁定的,但在游戏逻辑的计算上应该还有很多空间。主要原因是屏幕上的像素数与游戏中的实体数量差异巨大。举个例子,假设屏幕分辨率是1920x1080,意味着有约200万个像素,而游戏中显示的实体最多只有100个左右。相比之下,游戏逻辑的计算量远低于图形渲染的计算量。因此,游戏逻辑应该不会成为瓶颈。同时,已经为世界其他地方的背景模拟做好了准备,这有助于确保即使在大规模模拟时,也不需要过度担心性能问题。

所有动画都是程序化完成的,还是会由艺术家来做?还是两者都有?

希望一切动画都能通过程序化的方式实现,而不是由艺术家手工制作。这样可以更灵活地控制动画效果,并且更容易进行调整和扩展。

你能展示一段你实现哈希表的代码吗?

这个哈希表的实现方法是,我们首先通过计算世界中某个区域的x、y、z坐标来生成哈希值。然后,我们通过掩码(masking)操作,从哈希值中去除掉不需要的部分,确保哈希值适合存入哈希表。

在哈希表的存储结构中,我们采用了外部链式法(External Chaining),也就是说,在哈希表的每个槽中存储的是一个链表,多个值可能会映射到同一个槽,我们就通过遍历这些链表来查找所需要的元素。简而言之,这就是一个使用链表处理哈希冲突的哈希表。

此外,在代码中,我们通常会看到一个"更好的哈希函数"的笑话,这表明这个哈希函数可能还不是最优的,可能以后还会进行改进。

如果 Vulkan 在你硬件加速渲染器时已经发布,我们可以使用 Vulkan 吗?

如果使用 Vulkan 渲染的话,理论上是可行的。尽管现在渲染速度已经非常快,可能暂时不需要深入探索渲染的优化问题。我们很可能会在最后准备发布前才进行这方面的优化。

你认为微软为什么决定在 Xbox One 上省略 1080i 分辨率选项?你认为他们很难包括它吗?

关于微软决定在 Xbox One 上省略 1080p 分辨率选项的问题,这个话题和当前讨论并不相关,而且也不清楚微软做出这样的决策背后的具体原因。因此,很难给出准确的解释。

今天很多游戏都有延迟,真是太不可思议了。感谢你为这些问题(糟糕的编程和/或语言)提供了一些见解

今天的计算机硬件已经非常强大,尤其是图形处理单元(GPU)和中央处理单元(CPU)都非常快速,因此出现许多存在延迟的游戏显得有些不可理解。尽管有时候可能是操作系统或者显卡驱动的问题,导致游戏出现卡顿、延迟等现象,但很多时候问题并非出在硬件上,而是因为游戏开发者在编程时没有采取优化措施。

在许多情况下,游戏开发者可能因为不懂或者懒得投入时间去写高效的代码,导致游戏出现延迟或者低帧率等问题。虽然这种现象并不一定完全归咎于开发者,但游戏的性能,特别是流畅度,应该是一个重要的开发目标。确保游戏在高性能硬件上也能提供流畅的体验应该是开发者的首要任务。

然而,现实中很多开发者更注重选择自己熟悉的工具,可能会选择像C#这样的语言,但这些语言在性能方面可能会有所限制,导致最终的游戏体验无法达到理想的流畅度。虽然使用这些语言开发游戏可能方便,但如果忽视了性能问题,最终可能会导致游戏中的卡顿、长时间加载等问题,这会影响玩家的游戏体验。

因此,开发者应该始终把良好的性能作为开发游戏的基本要求。只有当确保了良好的性能之后,才能再考虑如何让开发过程更加便捷。为了整个行业的进步,我们应该坚持这一点,只有玩家要求更高的游戏表现,开发工具和引擎才会不断改进。最终,游戏开发者的职责是确保玩家能够获得流畅、稳定的体验,因为计算机硬件本身就有足够的能力支持这一点。

不安全的内存是否是现代游戏高性能优化所需的关键?我听说这是像 Rust 和 Haskell 这样的内存安全语言不能接管低延迟应用或驱动程序的关键原因,但我对这个领域了解不够

关于现代游戏和高性能应用程序中是否需要不安全内存操作的问题,观点认为内存安全可能与高性能存在冲突,但这种看法并不完全成立。实际上,内存安全本身并不必然导致性能问题。问题的关键在于目前的语言,它们在提供内存安全的同时,可能并不适用于高性能的需求。

未来的编程语言有可能在保证内存安全的同时,仍能提供高性能的能力。当前技术尚未达到这一水平。值得注意的是,内存安全和垃圾回收并不完全等同,垃圾回收需要在运行时分析指针并自动释放内存,这种做法会影响性能,因此难以满足高性能需求。然而,内存安全本身并不需要依赖于垃圾回收机制,未来可能会出现不依赖这些机制、依旧保证内存安全的编程语言。

目前来看,内存安全和性能之间的矛盾是由现有的编程语言和技术所造成的,但这并不意味着这两者不能共存。随着技术的发展,未来可能会发现,内存安全和高性能实际上是可以和谐共存的。此时,一种高性能的语言可能也能够提供内存安全,因为这两者可能是相辅相成的。

总的来说,当前我们还处于编程技术发展的初期阶段,很多解决方案尚未成熟。在未来的二十年内,随着技术的不断进步,我们有可能会找到既能保证内存安全又能提供高性能的语言和技术。

相关推荐
lally.22 分钟前
sql深入学习
数据库·sql·学习
虾球xz1 小时前
游戏引擎学习第127天
java·学习·游戏引擎
9命怪猫2 小时前
AI大模型-提示工程学习笔记21-图提示 (Graph Prompting)
人工智能·学习·ai·大模型·prompt
乱次序_Chaos2 小时前
【无监督学习】主成分分析步骤及matlab实现
学习·算法·机器学习·matlab
eqwaak02 小时前
2025年2月28日全球科技信息差:技术革新、市场震荡与认知重构
开发语言·人工智能·科技·学习·重构
MYX_3092 小时前
Python 编程题 第四节:斐波那契数列、列表的复制、暂停后输出、成绩评级、统计字符
开发语言·python·学习
驱动起爆大师x_x4 小时前
STM32F1学习——WDG看门狗
笔记·stm32·单片机·嵌入式硬件·学习
青岛国之信检测4 小时前
【学习】软件测试中的判定表测试方法解析
功能测试·学习·测试用例·安全性测试
万兜鍪:>5 小时前
线程(Thread)
linux·c语言·vscode·学习