游戏引擎学习第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 昨天的回顾 今天的工作重点是继续进行反射计算的实现。昨天,我们开始了反射和环境贴图的工作,成功地根据法线显示了反射效果。然而,我们还没有实现反射向量的计算,导致反射交点的代…...
新一代SCADA: 宏集Panorama Suite 2025 正式发布,提供更灵活、符合人体工学且安全的应用体验
宏集科技宣布正式推出全新Panorama Suite 2025 SCADA软件!全新版本标志着 Panorama Suite的一个重要里程碑,代表了从 Panorama Suite 2022 开始并跨越三个版本(2022、2023、2025)的开发过程的顶峰。 此次重大发布集中在六个核心主…...
Visual Studio 进行单元测试【入门】
摘要:在软件开发中,单元测试是一种重要的实践,通过验证代码的正确性,帮助开发者提高代码质量。本文将介绍如何在VisualStudio中进行单元测试,包括创建测试项目、编写测试代码、运行测试以及查看结果。 1. 什么是单元测…...
Notepad++ 中删除所有以 “pdf“ 结尾的行
Notepad 中删除所有以 “pdf” 结尾的行 操作步骤 1.打开文件: 在 Notepad 中打开你需要处理的文本文件。 2.打开查找和替换对话框: 按快捷键 Ctrl F,打开“查找和替换”对话框。 3.启用正则表达式模式: 在对话框的底部…...
Java 使用腾讯翻译 API 实现含 HTML 标签文本,json值,精准翻译工具
注意:需搭配标题二的腾讯翻译工具使用 一-1、翻译标签文本工具 package org.springblade.common.utils;import java.util.ArrayList; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern;public class TencentTranslationFor…...
DeepSeek R1+Open WebUI +SearXNG 本地化部署与联网功能
GitHub - searxng/searxng-docker: The docker-compose files for setting up a SearXNG instance with docker....
数据科学之数据管理|NumPy数据管
一、Numpy介绍 (一) 什么是numpy NumPy是Python中科学计算的基础包。它是一个Python库,提供多维数组对象,各种派生对象(如掩码数组和矩阵),以及用于数组快速操作的各种API,有包括数学、逻辑、形状操作、排序、选择、输入输出、离散傅立叶变换、基本线性代数,基本统计运…...
零基础玩转 DeepSeek API实战教程
大家好,我是herosunly。985院校硕士毕业,现担任算法研究员一职,热衷于大模型算法的研究与应用。曾担任百度千帆大模型比赛、BPAA算法大赛评委,编写微软OpenAI考试认证指导手册。曾获得阿里云天池比赛第一名,CCF比赛第二名,科大讯飞比赛第三名。授权多项发明专利。对机器学…...
【GPIO】5.理解保护二极管在GPIO过电压保护中的作用
在电子电路设计中,保护二极管是常见的保护元件,用于防止过电压对敏感电路的损害。本文将探讨当GPIO输入电压大于3.3V时,保护二极管如何工作,并解释为什么大部分过电压引起的电流会通过二极管流向VDD而不是流入内部电路。 1.背景 …...
2.5 模块化迁移策略:从传统项目到模块化系统
模块化迁移策略:从传统项目到模块化系统 将传统 Java 项目迁移至 JDK 9 模块化系统是一项系统性工程,需分阶段实施以降低风险。以下是详细的迁移策略、工具使用和实战示例。 1. 迁移阶段划分 阶段目标关键操作阶段1:兼容性验证确保项目能在…...
Tortoise Git
TortoiseGit 是一个 Windows Shell 与 Git 的接口,它提供了文件状态的覆盖图标,强大的 Git 上下文菜单等。你可以在官方网站 (tortoisegit.org) 轻松使用安装程序进行下载。TortoiseGit 的当前稳定版本是 2.14.0 ,根据你的机器配置࿰…...
Maven Spring框架依赖包
Maven中添加Spring框架依赖包 Spring核心工具包SpringJDBCSpring配置文件头信息 Spring核心工具包 在pom.xml文件中添加 <!-- Spring的核心工具包--><dependencies><dependency><groupId>org.springframework</groupId><artifactId>spr…...
【Cocos TypeScript 零基础 15.1】
目录 见缝插针UI脚本针脚本球脚本心得_旋转心得_更改父节点心得_缓动动画成品展示图 见缝插针 本人只是看了老师的大纲,中途不明白不会的时候再去看的视频 所以代码可能与老师代码有出入 SIKI_学院_点击跳转 UI脚本 import { _decorator, Camera, color, Component, directo…...
Linux库制作与原理:【静态库】【动态库】【目标文件】【ELF文件】【ELF从形成到假造轮廓】【理解链接和加载】
目录 一.什么是库 二.静态库 2.1创建静态库 我们在之前的路径下新建lib使用我们自己的库 2.2 使用makefile生成静态库 三.动态库 3.1动态库生成 3.2动态库使用 3.3库运行搜索路径 四.目标文件 五.ELF文件 六.ELF从形成到加载轮廓 6.1ELF形成可执行 6.2 ELF可执行文…...
中间件-redis-(ubantu)
1、安装依赖包 sudo apt-get update sudo apt-get install redis 一旦安装完成,Redis 服务将会自动启动。想要检查服务的状态,输入下面的命令: rootvims:/etc/redis# sudo systemctl status redis-server ● redis-server.service - Adva…...
ubuntu20.04+ROS+Gazebo+px4+QGC+MAVROS
目录 前言 一、安装ROS 二、安装PX4 编译 三、QGC安装 四、安装MAVROS 命令记得加sudo! 前言 在安装ubuntu20.04ROSGazebopx4QGCMAVROS时,参考了很多网上的资料,总结一个较为顺利的流程。 官方指南PX4 自动驾驶仪用户指南 | PX4 Gui…...
基于 openEuler 构建 LVS-DR 群集(同网段)。
一、LVS相关原理 1.LVS简介 LVS是Linux Virtual Server的简称,也就是Linux虚拟服务器, 是一个由章文嵩博士发起的自由软件项 目,它的官方站点是www.linuxvirtualserver.org。现在LVS已经是 Linux标准内核的一部分,在 Linux2.4内核以前&…...
计算机毕业设计PySpark+Hadoop+Hive机票预测 飞机票航班数据分析可视化大屏 航班预测系统 机票爬虫 飞机票推荐系统 大数据毕业设计
温馨提示:文末有 CSDN 平台官方提供的学长联系方式的名片! 温馨提示:文末有 CSDN 平台官方提供的学长联系方式的名片! 温馨提示:文末有 CSDN 平台官方提供的学长联系方式的名片! 作者简介:Java领…...
【设计模式】【行为型模式】观察者模式(Observer)
👋hi,我不是一名外包公司的员工,也不会偷吃茶水间的零食,我的梦想是能写高端CRUD 🔥 2025本人正在沉淀中… 博客更新速度 📫 欢迎V: flzjcsg2,我们共同讨论Java深渊的奥秘 …...
机器学习: 逻辑回归
概念与定义 逻辑回归是一种用于分类问题的统计方法。它通过计算目标变量的概率来预测类别归属,并假设数据服从伯努利分布(二分类)或多项式分布(多分类)。逻辑回归模型输出的是概率值,通常使用sigmoid函数将线性组合映射到0和1之间。 1. 概念 逻辑回归用于解决分类问题…...
域名解析—互联网世界的导航系统
在互联网的世界里,每个网站都像一座“城市”,而用户要找到这些“城市”,必须依赖一套精准的导航系统——这就是域名解析。无论是浏览网页、发送邮件,还是使用移动应用,域名解析都在背后默默支撑着用户的每一次访问。本…...
PAT乙级真题 — 1080 MOOC期终成绩(java)【测试点3超时】
对于在中国大学MOOC(http://www.icourse163.org/ )学习“数据结构”课程的学生,想要获得一张合格证书,必须首先获得不少于200分的在线编程作业分,然后总评获得不少于60分(满分100)。总评成绩的计…...
【Prometheus】如何通过prometheus监控redis实时运行状态,并实现告警通知
✨✨ 欢迎大家来到景天科技苑✨✨ 🎈🎈 养成好习惯,先赞后看哦~🎈🎈 🏆 作者简介:景天科技苑 🏆《头衔》:大厂架构师,华为云开发者社区专家博主,…...
ARM Cortex-M3/M4 权威指南 笔记【一】技术综述
一、Cortex-M3/M4 处理器的一般信息 1.1 处理器类型 ARM Cortex-M 为 32 位 RISC(精简指令集)处理器,其具有: 32位寄存器32位内部数据通路32位总线接口 除了 32 位数据,Cortex-M 处理器(以及其他任何 A…...
【Qt】定期清理程序
在现有Qt程序中实现可配置日志保存天数的代码示例,分为界面修改、配置存储和核心逻辑三部分: // 1. 在配置文件(如settings.h)中添加保存天数的配置项 class Settings { public:int logRetentionDays() const {return m_settings…...
基于51单片机的门禁刷卡器proteus仿真
地址:https://pan.baidu.com/s/1j0KAmH5pVGWZWRpT6p5hBg 提取码:1234 仿真图: 芯片/模块的特点: AT89C52/AT89C51简介: AT89C52/AT89C51是一款经典的8位单片机,是意法半导体(STMicroelectron…...
二、数据持久化篇(深度增强版)
二、数据持久化篇(深度增强版) 2.1 JDBC Template深度解析 架构设计思想 #mermaid-svg-y2IrKiVu2gzenoCB {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-y2IrKiVu2gzenoCB .error-icon{fil…...
时间敏感和非时间敏感流量的性能保证配置
论文标题 中文标题: 时间敏感和非时间敏感流量的性能保证配置 英文标题: Provisioning of Time-Sensitive and non-Time-Sensitive Flows with Assured Performance 作者信息 Luis Velasco, Gianluca Graziadei, Sima Barzegar, Marc Ruiz Optical Co…...
k8s管理工具之lens
什么是lens Lens 是当前市场上最强大的K8S IDE。它是一个独立的单机应用,可以同时运行在macOS、Windows和Linux上。 作为K8S IDE,该有的它基本都有了! 集群管理 导入已有集群 首先,你需要在 Lens 中添加你的 Kubernetes 集群。点…...
kafka介绍,kafka集群环境搭建,kafka命令测试,C++实现kafka客户端
目录 kafka介绍kafka集群环境搭建zookeeper安装与配置kafka安装与配置 kafka命令测试C实现kafka客户端librdkafka库编译新版本cmake编译cppkafka库编译C实现kafka生产者和消费者客户端 kafka介绍 定义与概述 Apache Kafka 是一个开源的分布式流处理平台,最初由 Lin…...
DeepSeek的蒸馏技术:让模型推理更快
DeepSeek系列模型,如DeepSeek-R1-Distill-Qwen-7B,采用了知识蒸馏(Knowledge Distillation)技术,这是一种强大的模型压缩和优化方法。通过蒸馏,DeepSeek模型在保持甚至提升性能的同时,实现了更快…...
SAP-ABAP:dialog界面中的数据块Event Block详解举例
在SAP的Dialog程序开发中,Event Block(事件块)是屏幕流逻辑(Flow Logic)中的关键部分,用于定义屏幕在特定事件触发时执行的逻辑。Event Block通常与ABAP模块(Module)结合使用&#x…...
微信小程序 - 模版语法
声明和绑定数据 小程序页面中使用的数据均需要在 Page() 方法的 data 对象中进行声明定义 在将数据声明好以后,需要在 WXML 中绑定数据,数据绑定最简单的方式是使用 Mustache 语法(双大括号)将变量包起来。 在 {{ }} 内部可以做…...
mapbox进阶,添加绘图扩展插件,裁剪线
👨⚕️ 主页: gis分享者 👨⚕️ 感谢各位大佬 点赞👍 收藏⭐ 留言📝 加关注✅! 👨⚕️ 收录于专栏:mapbox 从入门到精通 文章目录 一、🍀前言1.1 ☘️mapboxgl.Map 地图对象1.2 ☘️mapboxgl.Map style属性1.3 ☘️MapboxDraw 绘图控件二、🍀添加绘图扩…...
Dav_笔记14:优化程序提示 HINTs -2
优化方法和目标的提示 ALL_ROWS和FIRST_ROWS(n)提示允许您在优化方法和目标之间进行选择。如果SQL语句具有指定优化方法和目标的提示,则优化程序将使用指定的方法,无论是否存在统计信息,OPTIMIZER_MODE初始化参数的值…...
Oracle ORA-00054
ORA-00054: resource busy and acquire with NOWAlT specified or timeout expire 错误 ORA-00054: resource busy and acquire with NOWAIT specified or timeout expired 是 Oracle 数据库中常见的一个错误,通常发生在尝试获取一个已经被其他会话占用的资源时。这…...
ABB能源自动化选用宏集Cogent DataHub避免DCOM问题,实现高效、安全的数据传输
案例概况 ABB能源自动化公司通过宏集Cogent DataHub软件将电厂设施的数据实时传输到公司办公室,实现了OPC隧道/镜像解决方案,在电厂和公司网络之间建立了一个安全、可靠的连接,确保数据传输的高度安全,减少入侵风险。 ࿰…...
IP地址有哪些类型?
IP地址是计算机网络中用来识别和查找设备的唯一标识符。根据其作用和使用范围,IP地址可分为以下几种类型: 1.局域网IP地址 局域网IP地址又称内网IP地址,是局域网内用来识别和查找局域网设备的地址。局域网是一个相对较小的网络,…...
网络安全评估:保障设备与系统安全的关键
保障网络安全离不开对入网设备的安全评估,这种评估运用了多种技术和手段,对网络中的设备与系统进行详尽的检查。它能迅速发现并排除潜在的安全风险,对网络系统的安全稳定运行具有极其关键的作用。 测评目的 确保网络系统的安全与稳定是网络…...
一竞技瓦拉几亚S4预选:YB 2-0击败GG
在2月11号进行的PGL瓦拉几亚S4西欧区预选赛上,留在欧洲训练的YB战队以2-0击败GG战队晋级下一轮。双方对阵第二局:对线期YB就打出了优势,中期依靠卡尔带队进攻不断扩大经济优势,最终轻松碾压拿下比赛胜利,以下是对决战报。 YB战队在天辉。阵容是潮汐、卡尔、沙王、隐刺、发条。G…...
管式超滤膜分离技术在茶澄清浓缩领域的创新应用
管式超滤膜分离技术在茶澄清浓缩领域展现出广阔的前景,其独特优势和应用效果正逐渐改变着茶饮料行业的生产方式。以下是几个关键点,说明了这一技术为何具有如此积极的发展潜力: 1. 高效澄清与保留风味 管式超滤膜具有高精度的过滤能力&#x…...
maven web项目如何定义filter
在 Maven Web 项目中定义一个 Servlet 过滤器(Filter),需要遵循 Java Servlet 规范,并利用 Maven 来管理项目结构和依赖。下面是如何在 Maven Web 项目中定义和配置一个过滤器的基本步骤: 1. 创建过滤器类 首先&…...
如何在MacOS上查看edge/chrome的扩展源码
步骤 进入管理扩展页面点击详细信息复制对应id在命令行键入 open ~/Library/Application Support/Microsoft Edge/Default/Extensions/${你刚刚复制的id} 即可打开访达中对应的更目录 注意 由于原生命令行无法直接处理空格 ,所以需要加转义符\,即:open ~/Librar…...
【学术投稿-2025年计算机视觉研究进展与应用国际学术会议 (ACVRA 2025)】CSS样式解析:行内、内部与外部样式的区别与优先级分析
简介 2025年计算机视觉研究进展与应用(ACVRA 2025)将于2025年2月28-3月2日在中国广州召开,会议将汇聚世界各地的顶尖学者、研究人员和行业专家,聚焦计算机视觉领域的最新研究动态与应用成就。本次会议将探讨前沿技术,…...
redis记录用户在线状态+活跃度
1.记录用户在线状态 redis的Bitmap记录用户在线状态 使用一个大的Bitmap,每个bit位对应一个用户IDbit值1表示在线,0表示离线用户ID与bit位的映射关系: bit位置 用户ID % bitmap容量 具体实现: # 用户上线时,设置对应bit为1 SETBIT online_users {user_id} 1# 用户下线时,设…...
vmware安装win7
1、版本说明 vmware workstation 16 win7 X64 2、安装步骤 安装步骤有点独特,先配置虚拟机,然后再虚拟机的虚拟光驱里添加下载的win7。 配置完了之后,点击要运行的虚拟机,然后一直往下走就可以完成系统的安装。 3、配置系统以解…...
OpenAI推出的Computer Use智能体:Operator是什么
OpenAI推出的Computer Use智能体:Operator是什么 是一款能像人一样与图形用户界面交互来操作计算机的AI智能体。以下是其核心原理及举例说明: 核心原理 感知: 屏幕截图获取:利用高性能屏幕捕获模块,如基于WebRTC的截图技术,以极低延迟获取高清晰度页面图像,为后续分析…...
[FastAdmin] 上传图片并加水印,压缩图片
1.app\common\library\Upload.php 文件 upload方法 /*** 普通上传* return \app\common\model\attachment|\think\Model* throws UploadException*/public function upload($savekey null){if (empty($this->file)) {throw new UploadException(__(No file upload or serv…...
二、k8s项目的生命周期
项目的生命周期 创建-----------》发布-----------》更新--------》回滚----------》删除 kubectl create deployment nginx1 --imagenginx:1.22 --replicas3 基于deployment控制器创建pod 控制器的名称是nginx1 pod使用的镜像:nginx:1.22 --replicas3 pod的数量有多少 3个…...
MySQL数据库入门到大蛇尚硅谷宋红康老师笔记 基础篇 part 10
第10章_创建和管理表 DDL:数据定义语言。CREATE \ALTER\ DROP \RENAME TRUNCATE DML:数据操作语言。INSERT \DELETE \UPDATE \SELECT(重中之重) DCL:数据控制语言。COMMIT \…...