游戏引擎学习第100天
仓库:https://gitee.com/mrxiao_com/2d_game_2
昨天的回顾
今天的工作重点是继续进行反射计算的实现。昨天,我们开始了反射和环境贴图的工作,成功地根据法线显示了反射效果。然而,我们还没有实现反射向量的计算,导致反射交点的代码未能运行。因此,目前显示的只是一个平面的效果,因为我们仅仅是验证了能否从环境贴图中进行查找,而并未实现实际的反射计算。
今天的目标是详细了解反射代码的工作原理,并尽可能实现它。通过这个过程,我们将继续推进法线贴图的代码实现,接下来要做的主要是优化这部分内容。在剩下的光照计算工作中,很多都涉及如何计算和采样环境贴图,因此反射代码的实现将是关键。
接下来的工作将集中在实现反射向量的计算以及相关代码的完成,接着进入到优化和其他细节的处理。
黑板:表面反射率
在今天的工作中,主要关注的是理解反射模型的数学原理,并试图实现这一点。部分内容比较直接,容易理解,另一些则稍显复杂,特别是在处理2D光照时需要做一些近似,因为无法直接实现某些计算。
首先,回顾一下前面的图示,我们讨论了光照和反射的过程。每个反射表面都有一个视点和一个光源,光源和视点形成一个“反射”的路径,类似于光线反射的行为。在高反射表面上,光线的反射会非常直接,反射光线的角度与入射光线的角度是相同的,这种反射被称为纯反射。
当表面反射能力较差时,反射光线会扩展成一个较大的光锥,意味着更多的光线会散射到反射表面。随着表面变得更加光滑,反射的光线会变得更加集中。这个模型并不是基于物理的光照模型,而是一个简化的近似。在实际应用中,大多数表面都可以通过这种近似来实现合理的效果,尤其是在2D图形中。我们通过简单的反射模型计算反射光线,或者通过模糊的纹理贴图来实现更广泛的反射效果。
最终,光照的核心目标是计算直接反射的光线,即从视点出发,反射光线沿着相同的路径返回。这是一个相对简单的反射模型,主要集中在如何计算这种反射光线,并逐步实现。
黑板:直线反射模型和碰撞检测
在碰撞检测中,使用的数学方法和光照模型中的反射计算相似。假设有一个表面,其法线向量为 n,且有一个从眼睛指向该表面的向量 e。为了计算反射向量,我们需要产生一个与向量 e 相似的向量,但方向相反。具体来说,这个反射向量的 y 分量保持不变,而 x 分量则取反。这个过程和碰撞检测中计算反射向量的过程非常相似。
假设我们有一个指向眼睛的向量 e,目标是计算一个采样向量 s。为了得到反射向量,首先需要对 e 进行取反,得到负的向量 -e。然后,可以通过一个几何构造来确定反射向量。首先,计算法线与向量 e 的点积,这样可以知道 e 在法线方向上的分量。接着,通过沿着法线方向加上这个分量的两倍,得到反射向量。
通过这种几何构造,反射向量就可以被有效地计算出来,避免了繁琐的代数推导。虽然可以通过代数方法求解这个问题,但几何方法通常更加直观且效率更高。
查看代码
在实现反射计算时,首先需要定义眼睛的视线方向向量(EyeVector)和表面的法线(Normal)。法线可以从法线贴图中获得,但视线向量在 2D 游戏中尤其难以定义,因为没有一个明确的“眼睛”位置或视线方向。在这种情况下,需要找到一种方法来推算视线向量。
代码中有一个待办事项,任务是基于观察者的视线方向来计算反射向量。为了实现这一点,需要通过代码获得法线向量,并且推算出一个对应的视线向量。这对于 2D 游戏尤为复杂,因为没有直接的视角或摄像头位置定义。因此,在计算反射时,需要额外的工作来确定视线方向。
黑板:正交投影
在 2D 游戏中,常见的问题是游戏画面并不是基于透视的。传统的 3D 游戏中,视角随着物体位置的变化会出现不同的透视效果,比如物体的不同面会根据位置的不同而有所变化。但在 2D 游戏中,通常采用的是正交投影,这意味着即使物体在画面上移动,其外观不会因为透视关系发生变化。举个例子,一棵树在屏幕的左右两侧看起来是一样的,这与透视不同,因为透视下物体的侧面会随着位置变化而不同。
为了处理视线方向,考虑到游戏使用的是正交投影,可以假设视线始终是平行于屏幕的。这意味着,无论屏幕如何滚动,视线的方向都不会改变,从而避免了视线反射随着物体位置变化而变化的情况。这样做的目的是保持视觉一致性,避免在视觉风格上混用透视和正交投影,这样会更符合游戏的整体视觉效果。
将Z轴设为眼睛向量
为了处理视线方向,假设 z 轴指向正上方,垂直于屏幕。这意味着视线(即眼睛的向量)始终朝向屏幕外的某个方向。通过这种假设,视线就会始终指向上方,回到视角的位置,而不受物体移动的影响。这种做法保持了游戏中的视觉一致性,使得所有物体的反射和显示效果不会因为视角的变化而产生不自然的错觉。

黑板:一个“过早的优化”
通过假设视线向量(眼睛向量)仅在 z 轴方向上有影响,可以进行一定的优化。因为在进行计算时,只有 z 坐标需要参与运算,这简化了运算的复杂度。例如,当计算点积时,由于 x 和 y 坐标的影响可以被忽略,最终只需要关注 z 坐标。这使得计算变得更加高效。
进一步地,可以考虑在法线贴图中直接存储 z 坐标的值,而不是存储完整的法线。这是因为已经知道法线的其他部分(x 和 y)对计算的贡献为零。因此,可以将法线贴图优化为只存储 z 坐标。尽管这样做可能有一些局限性,例如无法利用平方根技巧减少计算量,但这种做法仍然值得考虑。
假设眼睛向量始终为[0, 0, 1]来计算反射
在计算反射方向时,眼睛向量( I I I 向量)始终被假设为 (0, 0, 1),即指向屏幕外的 z 轴方向。为了计算反射,首先需要使用法线向量和公式进行计算。反射方向的 z 分量由法线的 z 分量乘以 2 得到,这个值与法线的 x、y、z 分量一起构成反射计算的一部分。
反射方向的计算过程可以简化为:通过法线向量的 z 分量进行加权,得到新的反射方向。然后,可以将这个反射方向传递到后续计算中。接下来,还需要处理屏幕空间的 UV 坐标,并计算与表面的交点。这些步骤涉及到反射的采样方向以及如何利用屏幕坐标信息来确定反射位置。


编译并查看结果
可以快速编译并查看结果,尽管这可能会导致非法结果,因此在实际运行时可能会出现断言错误,但还是决定尝试一下。结果没有出现断言错误,程序成功运行。可以看到,反射效果开始显现,但仍然没有完成正确的采样部分,距离最终效果还差一点。总体来看,反射效果开始有些圆润,表明数学运算没有出现大问题。

黑板:调整因子α
第二部分涉及一个“补偿因子”(fudge factor),这是一个相对麻烦的部分,因为它通常看起来似乎很合理,但实际上并不理想。补偿因子听起来很不错,特别是如果不是从字面上理解,而是指代某些旅游小镇出售的糖果那种“补偿因子”,因为糖果很好吃,但实际上它并不健康。真正应该考虑的是数学或物理中的“补偿因子”,而不是糖果。
补偿因子之所以不好,是因为它们没有明确的标准,你不知道它应该设定为多少,也不了解它的行为。使用真实世界的物理或数值时,通常知道它们应该是什么样子,如果出错就能定位到问题。然而,补偿因子通常是任意设置的,可能是设错了,导致调试变得困难。因此,虽然补偿因子是现实中不可避免的,甚至在最复杂的物理文献中也会出现,但它们的使用要尽量避免,尽量减少代码中的任意常量。
总之,虽然补偿因子在物理模拟中不可避免,但要尽量控制它们的使用,因为它们可能会带来调试和理解上的困难。
黑板:从环境贴图中采样
问题的核心在于屏幕上的一个点和该点在世界中的映射关系。假设屏幕是一个平面,我们有一个目标点,目标是通过从该点沿着某个方向投射来采样世界中的地图。现在的问题是,我们知道方向,但不知道应该走多远才能在这个方向上与地图相交。这个问题类似于碰撞检测中的光线与平面的交点问题,虽然之前已经做过类似的计算,但这里的困难在于没有明确的距离信息。
尽管我们可以通过设置一个大概的高度(比如设定天空在地面上的某个高度),但这种做法可能会在地图的边缘导致问题,因为边缘处的采样结果可能不准确。这个过程涉及到一定的"调节因子"(fudge factor),即如何处理这些不确定的变量,比如预设的天空高度等。
为了处理这个问题,首先假设我们知道Z轴上的位移距离(即光线在Z轴上的投射距离),然后通过计算在此方向上的位移量,得到X和Y方向上需要多少的位移。通过构建相似的三角形,我们可以计算出需要多少Z轴的位移才能到达目标位置,然后根据这个比例来确定X和Y轴的位移量。
当然,还需要考虑视角问题,比如是朝上看还是朝下看,这会影响Z轴的方向,因此需要根据不同情况调整计算。
总之,这个过程相对复杂,因为涉及到多个不确定的因素(比如调节因子),并且可能需要额外的调整,以确保在不同的屏幕对齐方式下能够正确采样。
假设反射方向始终与其尝试从中采样的地图方向一致
在处理反射方向时,目标是确保反射方向始终指向要从中采样的地图方向。假设所有的法线都是朝上的,我们将反射方向用作新的方向,而不是继续使用原来的法线。这时,反射方向将考虑当前实际观察到的方向。
具体来说,Y轴的值决定了反射是向上还是向下,因此反射方向的Y轴会告诉我们是朝上还是朝下采样。Z轴的值将与Y轴相对应,确定采样的方向。如果法线已经正确存储在适当的空间中,那么这部分的逻辑应该能够正常工作。
关键是要确保法线是正确配置的,并且反射方向能够正确指向目标地图。如果一切配置正确,反射方向的计算应该没有问题。
对底部地图取反反弹方向
在进行采样时,地图的法线图将基于反弹方向,而不是原来的法线,因为现在采样的方向才是关键。为了避免处理法线指向的方向(正Y或负Y),采样环境映射函数应该不需要关心法线的具体方向,而是始终假设Y轴是正值。这样,不论是在采样地面还是天空时,代码看起来应该一致,像是总是朝上采样。
具体来说,如果在采样时是从地面地图采样,法线的Y值应该为负;而从天空地图采样时,法线的Y值为正。因此,我们只需要调整反弹方向的Y值,确保其总是指向上方,这样可以避免反弹方向需要自己处理法线的朝向。
调整反弹方向的Y值后,它的方向会发生变化,但大小不受影响。此时,反弹方向就可以正确传递给环境映射采样函数,开始进行下一步的处理。




开始设置采样方向
在这里,Y 分量决定了在采样过程中走多远,这是因为我们在编码这些地图时的方式。我们希望假设 Y 不可能为零。我们之所以知道 Y 不能为零,是因为在之前的代码中已经做了相应的处理,如果 Y 为零,意味着它指向了平面,我们会避免这种情况。只有当 Y 不为零时,才会沿着正确的方向进行采样。
我们可以断言 Y 的值大于零,并且不应该出现 Y 为零的情况。由于之前的代码已经处理了这些情况,因此不会出现 Y 为零的情况,这是我们希望确保的。
接下来,我们需要做的就是基于这些前提来继续执行采样操作。

计算从调整因子和系数的偏移量
首先,需要计算系数,该系数由以下几部分组成:
-
fudge factor(调节因子)
f,它的作用是控制距离的大小。假设我们初步设定为1米,这个值可能不是最佳的,但可以作为一个初始值。该因子用于计算后续的偏移量。 -
计算系数:该系数等于调节因子
f除以SampleDirection.y,这是为了确保按 y 方向的分量来调整偏移量。通过这种方式,可以计算出正确的偏移量。 -
偏移量:偏移量是系数乘以剩余的向量(即 x 和 z 分量)。根据样本的坐标系统,x 和 z 分量用于定义偏移的方向。对于法线,x 方向用于 x 分量,z 方向用于 z 分量,y 方向与坐标系统有关。
-
待处理问题:当前使用的是一种特定的编码方式,可能会出现需要反转某些方向(例如 z 方向)的问题。为了确保系统正确工作,未来可能需要对坐标系统进行调整,反转方向等操作。
这些步骤的目的是确保样本坐标和偏移量在正确的方向上,以便计算正确的反射或反弹效果。
计算tX和tY并(错误地)限制它们
首先,偏移量提供了从当前点到目标点的距离,这个距离是以米为单位的。接下来,计算时,需要考虑从屏幕空间的 UV 坐标开始,我们会利用这些 tx 和 ty 值进行采样操作。在这里,tx 和 ty 是以像素为单位的,所以我们需要先计算出相关的 RP 值,然后将其乘以实际的纹理大小。
具体步骤如下:
-
计算 U 和 V:需要将当前的 LOD 映射到适当的范围内。这一步涉及到计算 U 和 V 的偏移量,以便根据纹理的大小进行调整。
-
纹理坐标调整:接着,需要将 tx 和 ty 的值调整为纹理的大小。为了确保不越界,tx 和 ty 会被限制在 [0, 1] 范围内,这样就能避免越界采样。
-
边界处理:由于可能会采样到纹理之外,采用了
clamp(0, 1)来保证采样始终在有效范围内,从而避免出现无效或错误的采样结果。
总的来说,这一系列操作确保了偏移量能够正确地应用到纹理采样过程中,同时避免了超出纹理范围的情况发生。

找到UV值
为了计算 UV 坐标,首先需要确定屏幕空间中的偏移量。然后,通过将偏移量与当前屏幕空间的位置结合,得到最终的 UV 坐标。需要特别注意的是,偏移量的单位是米,因此必须将米转换为 UV 坐标。这就需要一个“米到 UV”的转换因子,即每米多少个 UV 单位。
在实现时,可以通过将转换因子与偏移量相乘来完成这一步骤。这个因子通常需要根据纹理的大小进行调整。如果转换因子设置得过高,会导致 UV 坐标计算错误,因此需要通过调试来确定正确的因子值。
此外,UV 坐标需要进行限制(clamp),确保它们位于 [0, 1] 范围内,以防止超出纹理的有效区域。这一过程可能会涉及调整其他参数,直到 UV 坐标能够正确映射到纹理上。
为了进一步清晰理解计算过程,建议将“米到 UV”的转换因子命名为更具描述性的名称,如“DistanceFromMapInz”,以便更好地理解和调试。


在游戏中查看并引入一些运动
目前,反射效果已经初步实现,尽管还不完美,但能看到一些反射效果。接下来,将添加一些运动效果,以便更好地观察反射的动态变化。当前,反射看起来有些问题,尤其是在底部反射部分,可能是因为Y轴的翻转处理不正确,需要重新检查这一点。反射效果的优化还没有完成,目前的实现非常昂贵。
此外,反射效果的调试版本比较慢,编译器优化的提升可以改善性能,虽然在调试版本中,性能问题尤为明显。目前,反射已经开始动态变化,可以看到光线的反射效果,但底部的反射应当向另一个方向弯曲,这表明底部的翻转处理存在问题,需要进一步修正。
此外,采样时遇到的问题可能是因为边缘点的法线处理不当,可能是法线设置为直向后方,这可能导致了一些不正常的采样表现。在这些位置,可能没有正确处理球体外部的法线信息,这也需要修正。
反射的法线可能指向错误的方向,应该看到一些棋盘状的效果,但目前没有看到,这可能是一个 bug。反射的法线应当直接指向观察者,而不是指向天空。这样的问题源自于光照系统的处理,因为这是第一次进行此类光照处理,需要考虑如何正确编码光照计算和反射效果。

黑板:我们编码事物的方式
在光照计算中,法线的方向需要特别注意。当前,法线的设置为(0, 0, 1),表示它指向屏幕的z轴正方向,而这并不指向天空,而是指向一个特殊的采样平面。如果想要让法线指向“直上”,应该将法线的值设置为(0, 1, 0),而不是(0, 0, 1)。这意味着,在光照系统中,法线的方向设置不正确,可能导致反射效果的方向出现问题,需要进行修正。
更改MakeSphereNormalMap中的默认法线
在生成法线贴图时,默认法线方向的设置需要调整。目前的法线方向是指向屏幕的z轴正方向,而不是指向天空。为了使法线指向天空,应该将其设置为指向y轴的正方向,即(0, 1, 0)。这样可以更好地控制反射效果,提供更准确的光照模拟。调整默认法线后,重新生成纹理贴图,查看是否有改善。

在游戏中查看
目前的反射效果没有达到预期,尤其是在需要显示棋盘图案的区域。反射效果仍然不准确,可能是因为在模拟镜面反射时存在一些问题。具体来说,可能是反射表面上的球形着色没有正确处理,导致不符合预期的反射效果。为了找出问题的根源,需要进一步检查是否正确地使用了球体的alpha值,或者检查其他与反射和着色相关的部分,以找出为何没有显示预期的棋盘图案。

进入调试器β
当不确定问题的原因时,通常意味着需要进一步调试以学习更多的内容。为了理解当前反射效果的问题,首先查看了渲染调用,并通过绘制矩形来逐步进行调试。在采样法线贴图时,检查了计算出的法线,确认其确实指向天空,这是预期的结果。接下来,检查了反射方向的计算,以进一步了解计算过程是否正确。这些调试步骤有助于发现并解决潜在的问题。
黑板:我们仍然需要一些镜面的倾斜度
通过进一步思考,反射的方向解释变得更加清晰。如果法线完全指向天空,那么反射会呈现出一种与镜面平行的效果,相当于将镜子横着拿着观察,不会看到反射的效果。如果希望看到向上的反射,镜子仍然需要有一定的倾斜角度。因此,经过思考,这个现象其实是合乎逻辑的。尽管如此,仍然需要进一步调试其他部分以确保一切正常。
调整法线
通过将法线调整为半y半z的方向,经过标准化处理后,得到了期望的反射效果。具体而言,计算了 1 1 + 1 \frac{1}{\sqrt{1+1}} 1+11 的结果,并得到了指向上方并具有一定倾斜角度的标准化法线。这种修改符合预期,反射效果看起来更符合需求。

在游戏中查看反射球
反射效果已经逐渐接近目标,但仍然存在一些问题,反射代码还没有完全正确工作。目前,可以看到反射的效果已经开始出现,但仍需要一些调整才能完善。尽管离最终完成还有一定距离,但进展已经比较接近。接下来的工作将继续检查代码,特别是检查蓝色的代码部分,目的是搞清楚当前反射实现中的问题。如果今天无法解决这个问题,可能会把这个问题作为明天的工作重点。
考虑调查顶部地图的问题
当前问题在于顶图(蓝色的天空图)采样出现异常,导致反射效果不正确。反射代码似乎需要一些调整,特别是在计算反射路径时,可能需要做一些取反操作,但目前不确定为何需要这样处理。怀疑可能是反射计算过程中某个环节不够严谨,因此决定将问题的排查留到明天,仔细检查每个步骤,确保所有计算都是正确的。尽管如此,进展已经相当接近,问题很可能会很快得到解决。
你可以尝试创建一个具有更可预测反射的法线贴图,而不是使用球形的
当前反射效果仍存在问题,考虑到调试反射时,使用球形表面通常可以比较清晰地展示反射的全过程,因此决定尝试使用更具可预测性的法线图。可能考虑使用像波浪形的表面(上下起伏)作为替代方案,因为它能提供不同的反射模式,帮助更好地调试。另一个发现是反射衰减的现象,可能与环境图的混合系数有关,但由于法线是指向上的,理论上不应该发生这种情况,因此需要进一步思考并排查问题。总结来说,当前存在一些bug,计划在明天深入调试并解决这些问题。
法线贴图中的Y轴设置正确吗?
当前法线图的Y轴设置是反转的,意味着球体的顶部在底部,底部在顶部。之所以这样设置,是因为计划在坐标系统中进行翻转,因此在更改之前没有做调整。尽管从技术上来说,这与正常的坐标系统是反向的,但这种翻转只会影响反射的呈现方式,而不会影响其他反射的行为。换句话说,这只是导致蓝色出现在底部、红色出现在顶部,但除非有误,否则这不会对反射效果产生根本性影响。
一些人建议使用金字塔形
有提议考虑使用金字塔形状,金字塔的法线图是比较容易生成的。可以通过创建一个金字塔的法线图来实现,基本的思路是通过选择哪一部分设置为特定的法线值(例如0.777),金字塔的形状可以通过判断X是否大于Y来决定。也可以考虑其他形状,比如菱形,但金字塔形状相对直接。

黑板:金字塔
想要创建一个金字塔形状,首先需要定义金字塔的轮廓。金字塔的底部可以通过 x = y 的线来表示,而其顶部则是 1 - x = y 的线。在这种情况下,可以通过判断点是否位于这些线的哪一侧来确定其位置。如果 x 小于 y,那么这个点要么位于金字塔的一侧,要么位于另一侧。通过这种方式,可以简单地分类并确定每个点的归属,从而绘制出金字塔形状。这是一个比较直接的方法,完全可以实现。
制作金字塔
如果 x 小于 y,那么就意味着点位于金字塔的两个象限之一,否则就不在这些象限中。接下来,可以通过判断点是否满足 1 - x = y 的条件来确定其位置,虽然这个表达方式可能并不是最简洁的,但可以按这种方式处理。至于坐标系统,目前的坐标系统是反向的,但为了方便起见,仍然按正常方式处理,忽略这一点。
黑板:弄清楚金字塔的设置
实际上,考虑到金字塔的形状,x = y 线和 1 - x = y 线分别是确定各个区域的关键。如果 x 小于 y,那么就意味着点位于金字塔的某个象限,具体位置还需要判断是否满足 x 小于 y 的条件。对于每个区域,Z 坐标都等于 0.7,而 X、Y 方向的分量则会根据象限的不同而取不同的值:在 X 方向指向负值,在 Y 方向指向负值,反之亦然。这个逻辑看起来比较直接,可以按此处理。
设置金字塔
在计算金字塔形状的法线时,不需要使用 X 或 Y,也不需要其他复杂的计算,只需要直接根据条件确定法线的方向。通过判断 x < y 和 index < y,可以确定法线的方向。如果条件符合,法线的 x 分量为负值,表示该面朝向负方向;如果不符合,则根据位置调整法线的 y 或 x 分量,以确定正确的方向。这种方法简化了代码,并减少了不必要的计算。

尝试一下
在讨论中提到需要调用 MakePyramidNormalMap 来生成一个金字塔形状。通过使用金字塔形状,发现 Y 轴上的两个点没有反射,可能是因为它们指向的方向没有指向任何物体,或者是其他未知的原因。这一问题需要进一步思考和调查。


提问:我认为底部的反射也需要翻转Y轴,因为这是输入向量,对吗?
可能需要翻转y轴的反射,但这并不完全正确。因为已经在之前的计算中考虑了入射向量的影响,特别是通过处理负的E向量和相关的计算,已经部分解决了这一问题。
你说过可能需要使天空贴图比地面更大,这样就不会错过它。能否从相邻的天空地图中采样(假设它们没有被遮挡)?
无法从相邻的天空贴图进行采样,因为在游戏中并不存在相邻的天空贴图。目标是生成一个照明效果,覆盖所需的采样范围。进一步提到,天空实际上类似于室内的天花板,因此需要保持动态性,以便适应不同的环境。
边缘衰减是正确的,因为从边缘反射的向量的Y值将比从中心附近反射的向量小
边缘衰减是正确的,因为反射向量位于边缘时,其y值会比靠近中心的反射向量更小。随着平面变得更加倾斜,反射向量的y分量会减少。当反射法线朝向y轴时,y分量将最大,尤其是在反射角度接近45度的位置。这是因为当反射法线正对着观察者时,y分量最大,而当反射变得越来越倾斜时,y分量逐渐减小。
提问:是否可以旋转“输入”采样,使得球体看起来在旋转?
可以通过为环境贴图添加偏移量来旋转进入的采样,从而使球体看起来像在旋转。事实上,不仅仅是旋转,也可以通过移动来实现。然而,这样做就不再是反射了,因为一个旋转的反射球体看起来和一个静止的反射球体一样。因此,需要小心处理这个效果,因为它不再保持原本的反射特性。
圆柱可能会有用
使用圆柱体代替当前的实现可能会更有效,圆柱体的使用是一个非常好的建议。为了调试一个问题,考虑添加圆柱体的法线图,其中X值始终存在,而Y值则可以被忽略。这种方法可能通过设置不同的参数来生成圆柱体,避免考虑Y分量,从而实现期望的效果。通过这种方式,生成的图形看起来像一个滚动的圆柱体。这也意味着原本生成金字塔的代码可能存在问题,需要进一步调试和修正。


你是使用左手坐标系还是右手坐标系?
在讨论中提到,屏幕空间采用的是右手坐标系,但目前仍使用左上角作为参考点。计划先完成法线图的相关工作,再处理剩余的任务。
法线贴图的反转版本会有用吗?(反转的球体)
提到法线图的反向版本,反向的球形法线图可能不适用于调试,但对于游戏玩法而言,使用反向法线图是可以的,当真正应用于游戏时可以考虑。
根据X/Y轴旋转法线!
目前没有进行法线的变换,这是接下来需要处理的一个问题。比如,观察到在旋转时,法线并没有正确地指向不同的方向,而是保持原来的指向。这意味着法线没有被正确地转换,因此需要确保在旋转过程中正确变换法线方向。明天的任务之一就是解决这个问题,并在代码中加入相应的注释。


提问:就此结束!
目前,正常贴图的调试工作已经接近尾声。接下来的任务是处理中间贴图,这将是一个更具挑战性的部分。虽然目前尚不确定解决方案,但希望能够找到一个巧妙的办法。明天将继续调试正常贴图的代码,直到完全解决,然后开始研究如何处理中间贴图。
相关文章:
游戏引擎学习第100天
仓库:https://gitee.com/mrxiao_com/2d_game_2 昨天的回顾 今天的工作重点是继续进行反射计算的实现。昨天,我们开始了反射和环境贴图的工作,成功地根据法线显示了反射效果。然而,我们还没有实现反射向量的计算,导致反射交点的代…...
Leetcode:学习记录
一、滑动窗口 1. 找出数组中元素和大于给定值的子数组的最小长度 右指针从左到右遍历,在每个右指针下,如果去掉左边元素的元素和大于等于给定值则左指针右移一次,直到小于给定值,右指针右移一个。 2.找到乘积小于给定值的子数组…...
AT32系列微控制器低压电机控制开发板
参考:《UM0014_AT32_LV_Motor_Control_EVB_V20_User_Manual_V1.0.1_ZH.pdf》 开发板介绍 此电机开发板是一个泛用型的低压三相电机驱动器,应用雅特力科技AT32系列微控制器搭配雅特力电机函数库,可驱动直流无刷电机、交流同步电机࿰…...
如何保持 mysql 和 redis 中数据的一致性?PegaDB 给出答案
MySQL 与 Redis 数据保持一致性是一个常见且复杂的问题,一般来说需要结合多种策略来平衡性能与一致性。 传统的解决策略是先读缓存,未命中则读数据库并回填缓存,但方式这种维护成本较高。 随着云数据库技术的发展,目前国内云厂商…...
Vue3(3)
一.具体业务功能实现 (1)登录注册页面 [element-plus 表单 & 表单校验] 功能需求说明: 1.注册登录 静态结构 & 基本切换 2.注册功能 (校验 注册) 3.登录功能 (校验 登录 存token) import request from /utils/request// 注册接…...
2025 西湖论剑wp
web Rank-l 打开题目环境: 发现一个输入框,看一下他是用上面语言写的 发现是python,很容易想到ssti 密码随便输,发现没有回显 但是输入其他字符会报错 确定为ssti注入 开始构造payload, {{(lipsum|attr(‘global…...
Spring Cloud + Nacos + K8S 零影响发布方案
问题描述 在生产环境中使用 springcloud 框架,由于服务更新过程中,容器服务会被直接停止,部分请求仍被分发到终止的容器,导致服务出现500错误,这部分错误请求数据占用比较少,因为Pod滚动更新都是一对一。因…...
Git命令摘录
使用 Git 升级软件通常是指通过 Git 仓库获取软件的最新版本或更新代码。以下是详细的步骤和方法: 1. 克隆软件仓库 如果这是你第一次获取软件代码,可以使用 git clone 命令将远程仓库克隆到本地。 git clone <仓库地址> 例如: git cl…...
2024年博客之星年度评选—创作影响力评审+主题文章创作评审目前排名(2024博客之星陪跑小分队助力2024博客之星创作者成长)
2024年博客之星年度评选—创作影响力评审主题文章创作评审目前排名 2024年博客之星主题文章创作评审文章得分公布!2024年博客之星创作影响力评审2024年博客之星主题文章创作评审目前排名公布! 【2024博客之星】恭喜完成✅主题创作的226位博主࿰…...
unity 0基础自学2.1:unity 中button的各类状态
文章目录 1、Button的状态2、脚本中获取button的状态2.1 分析状态获取2.2 通过实现接口获取button的状态2.2.1 鼠标点击与释放2.2.2 高亮模式2.2.3 退出选中模式(高亮状态)2.2.4 选择模式selected2.2.5 退出选择模式 3、射线与UI交互设置3.1 Canvas中组件…...
《C++ Primer》学习笔记(一)
第一部分:C基础 在C和C编程语言中,main函数必须返回int类型的值。这一要求自C标准的第一次规范(C89,也叫ANSI C)开始就已经明确规定了。std::endl和\n都用于插入换行符。std::endl除了换行,还会强制刷新输…...
DedeBIZ系统审计小结
之前简单审计过DedeBIZ系统,网上还没有对这个系统的漏洞有过详尽的分析,于是重新审计并总结文章,记录下自己审计的过程。 https://github.com/DedeBIZ/DedeV6/archive/refs/tags/6.2.10.zip 📌DedeBIZ 系统并非基于 MVC 框架&…...
基于 Python(Flask)、JavaScript、HTML 和 CSS 实现前后端交互的详细开发过程
以下是一个基于 Python(Flask)、JavaScript、HTML 和 CSS 实现前后端交互的详细开发过程: --- ### 一、技术选型 1. **后端**:Python Flask(轻量级Web框架) 2. **前端**:HTML/CSS JavaScript&…...
作业。。。。。
顺序表按元素删除 参数:删除元素,顺序表 1.调用元素查找的函数 4.根据下表删除 delete_sub(list,sub); //删除元素 void delete_element(int element, Sqlist *list) …...
C#快速排序QuickSort将递归算法修改为堆栈Stack非递归方式
我们知道,方法的调用是采用Stack的方式[后进先出:LIFO], 在DeepSeek中快速搜索C#快速排序, 搜索结果如图: 我们会发现是采用递归的方式 . 递归的优点: 简单粗暴,类似于直接写数学公式,因代码量较少,易于理解.递归与循环迭代的运行次数都是一致的 递归的缺点: 占用大量的内…...
15.最大二叉树、合并二叉树、二叉搜索树
最大二叉树 就是一个提供了额外信息的中序遍历 class Solution { public:TreeNode* sol(vector<int>& nums,int start,int end){if(startend)return nullptr;int maxnums[start],indexstart;for(int istart;i<end;i){if(nums[i]>max){maxnums[i];indexi;}}Tr…...
【DeepSeek × Postman】请求回复
新建一个集合 在 Postman 中创建一个测试集合 DeepSeek API Test,并创建一个关联的测试环境 DeepSeek API Env,同时定义两个变量 base_url 和 api_key 的步骤如下: 1. 创建测试集合 DeepSeek API Test 打开 Postman。点击左侧导航栏中的 Co…...
Repo命令使用
repo 命令与 git 类似,但它主要用于管理多个 Git 仓库的操作。以下是等效的 repo 命令: 1. 获取新仓库代码 克隆仓库 repo init -u <manifest_url> -b <branch_name> repo sync repo init:初始化 repo,指定远程清单…...
npm install 失败
考虑原因: node版本不符代理镜像连接失败权限不足 症状1: 卡住 尝试降低nodejs版本 症状2:报错 报错1:permission not permitted 报错2: 超时 应对方法: node版本不符 降版本 镜像失败 – 切换镜像 …...
排序算法整理(冒泡排序、选择排序、插入排序、希尔排序、快速排序、堆排序、计数排序、桶排序、基数排序)
排序算法是计算机科学中用于将数据元素按照特定顺序进行排列的算法,常见的排序算法有以下几类: 比较排序 冒泡排序:通过重复地走访要排序的数列,一次比较两个元素,如果它们的顺序错误就把它们交换过来。走访数列的工作…...
Kimi实战1/100 - 读接口文档,编写接口
文章目录 Kimi实战1/100 - 读接口文档,编写接口接口调用requests 调用代码说明注意事项 接口提供FastAPI 接口代码代码说明测试方法 Kimi实战1/100 - 读接口文档,编写接口 接口调用 User: 根据 接口文档 https://www.eiisys.com/home/apiDetails?id00…...
Spring Cache @Cacheable:提升应用性能的利器
在构建企业级应用时,性能优化至关重要。Spring Cache 提供了一种简便而强大的方式来缓存方法调用的结果,从而减少数据库访问、提高响应速度。其中,Cacheable 注解是 Spring Cache 的核心,本文将深入剖析 Cacheable 注解࿰…...
css块级元素和行内元素区别
在CSS中,元素可以分为两大类:块级元素(Block-level elements)和行内元素(Inline elements)。这两种元素在网页布局中起着不同的作用,主要体现在它们的显示方式、尺寸控制、以及与其他元素的交互…...
AWTK fscript 中的 TCP/UDP 客户端扩展函数
fscript 是 AWTK 内置的脚本引擎,开发者可以在 UI XML 文件中直接嵌入 fscript 脚本,提高开发效率。本文介绍一下 fscript 中的 TCP/UDP 客户端扩展函数。 1.iostream_tcp_create 创建 TCP 客户端输入输出流对象。 原型 iostream_tcp_create(host, por…...
[免费]Springboot+Vue医疗(医院)挂号管理系统【论文+源码+SQL脚本】
大家好,我是java1234_小锋老师,看到一个不错的SpringbootVue医疗(医院)挂号管理系统,分享下哈。 项目视频演示 【免费】SpringBootVue医疗(医院)挂号管理系统 Java毕业设计_哔哩哔哩_bilibili 项目介绍 在如今社会上,关于信息上…...
计算机毕业设计PySpark+hive招聘推荐系统 职位用户画像推荐系统 招聘数据分析 招聘爬虫 数据仓库 Django Vue.js Hadoop
温馨提示:文末有 CSDN 平台官方提供的学长联系方式的名片! 温馨提示:文末有 CSDN 平台官方提供的学长联系方式的名片! 温馨提示:文末有 CSDN 平台官方提供的学长联系方式的名片! 作者简介:Java领…...
Jmeter+Influxdb+Grafana平台监控性能测试过程
一、Jmeter自带插件监控 下载地址:https://jmeter-plugins.org/install/Install/ 安装:下载后文件为jmeter-plugins-manager-1.3.jar,将其放入jmeter安装目录下的lib/ext目录,然后重启jmeter,即可。 启动Jmeter&…...
fatal: unable to access ‘https://github.com/xxx/‘: SSL peer certificat
从github上clone代码时报错 F:\Projects>git clone https://github.com/xxx into xxx... fatal: unable to access https://github.com/xxx/: SSL peer certificate or SSH remote key was not OK **可能的原因****解决方法****1. 检查系统时间****2. 禁用 SSL 验证…...
Prompt通用技巧
Prompt 的典型构成 角色:给 AI定义一个最匹配任务的角色,比如:「你是一位软件工程师」「你是一位小学老师」指示:对任务进行描述上下文: 给出与任务相关的其它背景信息(尤其在多轮交互中)。例子 : 必要时给出举例,学术中称为 one-shot learning,few-sho…...
ROACH
End-to-End Urban Driving by Imitating a Reinforcement Learning Coach CARLA-Roach ICCV‘21论文:模仿一个强化学习教练的端到端城市驾驶 文章目录 Roach输入BEV语义分割图像测量向量 Roach输出训练策略网络价值网络 具体实现由 Roach 监督的模仿学习(…...
