当前位置: 首页 > news >正文

游戏引擎学习第99天

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

黑板:制作一些光场(Light Field)

当前的目标是为游戏添加光照系统,并已完成了法线映射(normal maps)的管道,但还没有创建可以供这些正常映射采样的光场。为了继续推进,需要生成三个光场:上、中、下三个不同的环境贴图(EnvMaps),以便能够从中获取周围环境的光照数据。

具体来说:

  1. 光场的构建

    • 顶部和底部光场:这些光场相对容易构建,可以使用地面和天空的位图来代表。地面图将用作底部光场,天空图作为顶部光场,主要工作是确保渲染器能够保存并从未被遮挡的基础层中提取数据。
    • 中间光场:这个光场稍微复杂一些,需要考虑光的遮挡效应。如果有光源经过,应该能够表现出光线穿过物体表面的效果,类似地,如果有物体阻挡光线,采样到的光线就应该变暗。这个光场的实现会涉及一些更复杂的技术和算法,尤其是在计算遮挡和反射等方面。
  2. 目前的步骤

    • 生成顶部和底部的测试环境光贴图,以确保系统的基本光照采样工作正常。虽然还没有实现复杂的遮挡和反射效果,但这些测试贴图将帮助验证正常映射的实现。
    • 在顶部和底部光场上使用简单的测试模式,以检查是否能够正确采样并应用正常映射。

总的来说,当前工作重点是创建并调试这些基础的光场贴图,以便后续进一步完善光照和正常映射的系统,确保渲染效果能够达到预期。

创建测试环境贴图

在开发过程中,首先需要为环境贴图(即光照场)分配空间。这些环境贴图包括顶部、中部和底部贴图,尽管目前尚不清楚这些贴图的具体分辨率和内容(例如,顶部贴图可能代表天空,但实际上可以是任何其他内容)。因此,将这些环境贴图作为动态数据进行处理,它们会与地面缓冲区(ground buffers)一同管理,因为它们的性质是短暂的,随着渲染器的发展,空间分配会逐步调整。

在实现过程中,需要为这些环境贴图分配内存,并且考虑到光照的处理,这些贴图的分辨率可以选择一个较低的采样率,而不是与其他内容保持相同的高分辨率。因此,应该先定义这些环境贴图的宽度和高度,以便为后续的工作打下基础。

接下来,初始化时,将这些环境贴图的变量(例如顶部、中部和底部贴图,以及它们的宽高)添加到相应的数据结构中,确保它们在渲染过程中能够正确使用。
在这里插入图片描述

初始化环境贴图

首先,设置了一个名为 TranState 的初始化状态,用来初始化环境贴图。接下来,环境贴图的宽度和高度被设置为 512x256,虽然这些值是随机选择的,但它们决定了环境贴图的分辨率。

为了简化初始化过程,使用了一个循环来逐个初始化每个环境贴图。具体来说,在循环中,首先获取环境贴图数组中的每个环境贴图,并为其初始化不同的 LOD(Level of Detail)。这些 LOD 是相同贴图的不同清晰度版本,逐级模糊,分辨率逐渐降低。

通过设置循环,首先处理顶层环境贴图(索引 0),接着是中层和底层环境贴图(索引 1 和 2)。另外,还考虑到了坐标系的方向,决定使用 “Z轴向上” 的方式,这样可以保持与其他坐标系统一致,增加一致性和易于理解。

最后,在每一轮循环中,初始化的环境贴图会根据对应的分辨率大小,逐渐变得更模糊,这样可以生成一系列不同清晰度的环境贴图版本,以供后续使用。
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

设置LOD(细节层次)选择器

接下来,在初始化过程中,加入了另一个循环,用来处理每个 LOD(细节级别)索引。这个内层循环会根据环境贴图的数量(此时设为 4)迭代,通过每一层 LOD。

在每一轮循环中,根据当前 LOD 索引来创建相应的环境贴图。通过调用已定义的 MakeEmptyBitmap 函数,这个函数负责为每个 LOD 分配内存空间,并且将分配的内存推入暂存区(TransientArena)。这样,环境贴图的数据就能够存储在指定的内存区域。

为了支持不同细节级别的效果,首先创建最高分辨率的贴图,然后每次迭代时,宽度和高度都会按比例缩小。具体来说,每次迭代时,宽度和高度会减半,从而生成一个逐渐模糊、分辨率递减的级别(例如,初始分辨率、四分之一大小、再四分之一大小,依此类推)。这种方式会产生一系列从高到低的 LOD 地图,确保每个级别的环境贴图都有合适的分辨率。

通过这种方式,可以实现分层次的细节级别(LOD)管理,确保在渲染时根据需要使用不同清晰度的贴图,以提高性能并保证渲染效果。
在这里插入图片描述

将贴图传递给CoordinateSystem

在此阶段,需要确保能够正确地传递之前创建的环境贴图(环境地图)。之前这些环境贴图并没有被传递过来,而现在需要开始传递它们。这是一个直接的过程,只需要将环境贴图按照顺序传递即可。

首先,传递第一个环境贴图(如顶部贴图),然后是第二个贴图(中间贴图),最后是第三个贴图(底部贴图)。在代码中,环境贴图的顺序需要符合预设的顺序,即先是顶部贴图,然后是中间贴图,再到底部贴图。

在传递这些贴图时,确保与渲染组中的坐标系统一致,因此需要仔细检查环境贴图在传递时的顺序,以确保与渲染设置相匹配。通过这种方式,环境贴图可以正确地被使用在渲染过程中。
在这里插入图片描述

编译并清理

有一些变量被错误地声明为指针,这应该是一个错误,需要去掉指针声明并进行修正。这样修改之后,应该能够顺利编译,并确保代码正常工作。
这些修改有助于代码结构的清晰,确保不同功能和模块能够适当地组织,避免潜在的问题,并且使代码更加易于维护。
在这里插入图片描述

运行但没有发生什么特别的事

目前,地图中的内容只是垃圾数据,实际上它们目前只有零值,因为它们位于临时缓冲区(transient arena)中,而该缓冲区在初始化时已经被清空。因此,当前不应有任何光照贡献,这正是预期的结果。

接下来,目标是开始向这些地图中填充实际内容,从而能够开始从中进行采样。首先,计划将开始填充这些地图,确保能够正确地从中获取数据并进行进一步的处理。
在这里插入图片描述

绘制环境贴图

首先,需要在渲染过程中绘制这些环境图,以确保它们确实包含预期的内容,而不是盲目地进行采样。为了验证这一点,首先在渲染调用之前,添加一些代码以绘制至少是环境图的顶级图层。这样可以确保已经正确填充了环境图,并能够正确地查看它们的内容。

由于粗糙度(roughness)用于决定选择哪个LOD(细节层次),因此需要确认实际填入图层的粗糙度值。在当前实现中,粗糙度值是用来决定选择哪一层LOD的。例如,当粗糙度为零时,默认选择的是顶层LOD。

为了绘制图像,可以通过遍历环境图的每个地图来绘制不同LOD的图层。需要确保每个LOD图层在屏幕上正确显示,图像的位置也需要通过重新定义X轴和Y轴的比例来确定。为了避免图像重叠,可以根据实际LOD的宽高来调整显示大小,确保它们能够正确地显示在屏幕上。

最后,绘制的图层可能会出现颜色或透明度问题,需要调整颜色值,确保它们显示为全亮度,避免任何颜色的影响。通过调整绘制位置,可以确保每个图层正确显示,并且在屏幕上能够看到完整的环境图。如果图层的透明度不正确,可能会导致图像无法正确显示。

总之,这个过程的目的是确保环境图正确地填充并渲染,以便可以进一步调试和验证它们的使用。
在这里插入图片描述

在这里插入图片描述

准备环境贴图

首先,在每一帧渲染开始时,清除并重新绘制环境图,填充一些可以测试法线贴图的内容。为了简化测试,可以使用渲染组中的 DrawRectangle 调用,通过指定位图并填充不同颜色来验证法线贴图效果。

具体来说,首先清除环境图的顶层LOD,并填充该图层。可以将底部填充为红色,顶部填充为蓝色,这样通过法线贴图的光照计算时,至少可以在物体的顶部和底部看到红色和蓝色的色调。

接下来,清除环境图的顶层LOD,传入相关参数(如 TranStateEnvMapWidthEnvMapHeight)进行清除,确保渲染的图层没有残留内容。可以选择清除为纯红色、纯绿色和纯蓝色,代表不同的层次。清除绿色层可能是多余的,当前代码可能未使用该层,这一点需要检查并修正。

最后,转换色彩格式时,可能需要将颜色从 uint32 转换为 real32,以确保渲染时颜色正确显示。此时的清除操作完成,准备好进行后续的渲染和测试。
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在游戏中查看

已经可以看到正在从环境贴图中采样了,但是出现了一个 bug,看起来是混合的方向被反转了。具体来说,可以看到蓝色区域有较为平滑的过渡效果,虽然可能还不是完全正确,但至少有一定的羽化效果,看起来没有太硬的分界线。然而,红色区域却出现了明显的硬边界,这正是需要修正的问题,不符合预期效果。

注意到一些BUG

发现了两个问题,第一个是混合的方向被反转,导致蓝色区域羽化效果正常,而红色区域出现了硬边界。第二个问题是地面和天空贴图被翻转了,地面贴图应该在下方,但却出现在上方,天空贴图则相反。这个问题也需要解决。

这种问题的发现是通过调试代码时采用的结构化艺术方法。通过用简单的、易于区分的颜色(如红色和蓝色)来填充贴图,能够更清晰地看到哪里出错,而不像直接用真实数据那样难以分辨问题。结构化艺术帮助识别并修复渲染中的细微错误。计划接下来可能用白色调试纹理替换当前的纹理,专注于查看球体法线贴图的效果,而不受其他纹理的干扰。

在这里插入图片描述

查看如何修复底部贴图的采样

发现了一个问题,底部贴图被错误地指定为上部贴图,导致渲染结果不正确。检查了法线图的方向,发现法线的Y值在负方向时,被错误地判断为底部贴图。此问题可能是由于法线贴图的坐标系问题引起的,当前的贴图坐标系是从顶部到底部,而理想情况下应该使用从底部到顶部的坐标系。

考虑到大多数图形API(如OpenGL)通常使用底部到顶部的坐标系,因此决定将贴图的坐标系调整为从底部到顶部。这种调整需要尽快完成,因为在渲染过程中,坐标系问题可能会影响后续的计算和调试,因此需要尽早解决。

优先整理坐标系统

目前,坐标系问题尚未解决,但决定暂时跳过这一部分,先继续进行渲染工作,直到更进一步处理坐标系问题。虽然当前坐标系存在问题,但实际采样的部分并没有错误,因此可以继续进行。计划在优化和进一步处理渲染时,首先对屏幕坐标系、世界坐标系和纹理坐标系进行统一,确保它们在整个系统中保持一致性。这一调整将在进一步推进之前进行,以避免后续出现更复杂的问题。

添加一些注释

在这里插入图片描述

修复底部贴图的采样

在检查底部纹理地图时,发现出现了方向问题。原本的映射范围应该是从负一到负零点五,但当前实现中出现了反向映射的问题。为了解决这个问题,需要调整坐标映射的方向,确保在零的位置应该是1,而不是0。通过对公式进行简化和调整,可以正确地得到所需的结果。最终,通过调整公式,确保了映射的数值正确地从负一到负零点五之间变化,并成功修复了方向问题,达到了预期的渐变效果。
在这里插入图片描述

为环境贴图实现棋盘格图案

现在的目标是更准确地计算采样位置,因为当前的采样方法并不精确。为了测试这个问题,建议使用棋盘格纹理进行调试,这样可以通过清晰的图案帮助识别问题。计划使用16像素的棋盘格模式,通过在每个LOD层上绘制这个棋盘格,来检查纹理的采样。

为了实现这一点,需要在代码中定义棋盘格的宽度和高度,并通过遍历每个LOD的x和y坐标,依次绘制每个格子。每个格子的最大和最小坐标由宽度和高度决定。为了简化颜色的设置,将绘制矩形的函数修改为接受颜色值,颜色值包括红、绿、蓝和透明度。这样可以直接传递颜色并且避免繁琐的操作。

此外,还需要确保颜色值正确传递给每个矩形绘制函数,并更新了存储颜色信息的结构,使得颜色数据传递更加简洁和一致。最终,代码将更高效且易于维护,因为减少了冗余的计算和数据传递。
在这里插入图片描述

在这里插入图片描述

实现ToV4

通常定义数学库是为了快速地从一个向量类型转换到另一个类型。比如,对于一个 v4 向量,定义一个 twoBefore 函数,可以接受一个 v3(即 XYZ)和一个 W 分量。这个函数非常基础,它的作用是将 v3 的 XYZ 分量和 W 分量直接传递给结果。这样就能得到一个新的 v4 向量,结果的 XYZ 保持不变,而 W 则是传入的 W 值。通过这种方式,可以快速在不同维度的向量之间进行转换。

对于其他维度的向量,比如 v3,也可以使用类似的方法进行定义和转换。基本上,每个维度的向量都会有类似的函数来进行构造和转换,这样的实现非常简洁且高效。
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

继续棋盘格例程

在绘制棋盘格的过程中,首先需要为每个纹理地图设置颜色。为了做到这一点,通过 LOD 获取当前的颜色,并将其分配给地图颜色。然后,为了绘制棋盘格,定义了一个 MapColor,通过这个颜色来决定当前区域的颜色。在遍历棋盘格时,通过判断当前所在的棋盘格位置来决定是否将颜色设置为黑色。

棋盘格的最小点(MinP)和最大点(MaxP)可以通过 XY 坐标来确定,因为在绘制时,宽度和高度已经确定。为了正确地交替棋盘格的颜色,设置了一个 CheckerOn 变量,用来判断当前方块的颜色是地图颜色还是黑色,并通过切换 CheckerOn 的状态来实现这一效果。为了在每一行交替改变颜色,定义了一个 RowCheckerOn 变量,它决定了每一行的起始颜色。

同时,需要设置棋盘格的宽度和高度,这里暂时设定为 16x16,不过也考虑到高度可能小于宽度,决定暂时调整为 8。这个设置还没有完全确定,最终效果会根据实际需求进行调整。
在这里插入图片描述

在这里插入图片描述

按alt+enter全屏好像不能固定全屏

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

在游戏中查看棋盘格

棋盘格已经绘制完成,看起来很棒。可以看到双线性滤波正在生效,尤其是当样本之间的纹理采样时,产生了中间值,也就是在两个采样点之间的过渡效果。现在棋盘格已经被处理完了。

有趣的是,观察到现在整个图像没有任何光照,这很奇怪,特别是因为推测这意味着纹理采样的位置可能只选择了一些黑色的像素。这个现象可能是因为没有正确处理纹理坐标的计算,导致采样结果不正确。因此,应该开始着手解决纹理坐标问题,确保能够正确地从纹理图中获取像素值。
在这里插入图片描述

引入TestDiffuse和TestNormal

首先,决定不再使用树的正常贴图,而是创建一个测试用的位图和正常贴图。创建了 TestDiffuseTestNormal,其中 TestDiffuse 是一个颜色位图,TestNormal 是一个球形正常贴图。为了生成正常贴图,选择了一个球形正常贴图,并设定了零粗糙度。接下来,创建了一个 256x256 的空位图 TestDiffuse,作为测试使用。

接着,使用 DrawRectangle 方法绘制矩形填充该位图,并将颜色设置为中性灰色,这样就可以观察正常贴图的效果了。

在代码中,TreeNormal 不是 GameState 的成员,因此需要调整代码中的变量名称,改为 TestDiffuseTestNormal。此外,由于改变了位图的大小,动态重载并不支持这一变化,因此需要处理这个问题。
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

其他修改是添加注释

在游戏中查看并改变背景颜色

在开发过程中,原本使用的灰色与背景色相同,导致无法清晰地判断图形是否正确绘制。经过反思,这个灰色选择显然是一个疏忽。为了能更好地确认是否有绘制效果,决定调整颜色,以便能够更容易地看到图形。现在,当图形正确绘制之后,可以观察到其是否反映了预期的光照贡献,尤其是在处理实际的反射和光照传递时。虽然时间紧迫,接下来的工作可能无法完成,但这一调整为后续的调试和开发提供了更清晰的视图。
在这里插入图片描述

在这里插入图片描述

查看我们在SampleEnvironmentMap中的工作

在开发过程中,当前的实现并没有处理如何正确地查找采样位置,因此没有得到预期的光照贡献。当前系统只是简单地选择了地图的上角,而没有实际执行从地图中查找和计算具体值的工作。为了解决这个问题,需要进行必要的操作,从地图中获取实际的值,并准确地进行计算,确保能够正确反映光照的贡献。这项工作将在后续完成。
在这里插入图片描述

使用法线查找环境贴图

可以想象在地图中选择位置时,使用法线来确定位置。具体来说,可以从宽度开始,然后根据宽度选择法线的 x 坐标。虽然这种方法并不完美,但它能提供一个简单的思路,至少可以从中选择出一些元素。这种方法并不是目标方案,但它提供了一个直观的、易于理解的步骤,便于从中观察和调试。

在游戏中查看

当前反射效果还没有完全实现,实际上它只是在作为一种延迟纹理查找来使用。但至少可以确认,尝试的方向是有效的。对于双线性采样的效果,经过重新思考,可能是合理的,因此目前这种方法应该是可行的。虽然目前的效果已经不错,但暂时不打算深入研究如何正确进行采样,因为现在并没有足够的时间来做这些细节工作。因此,可能需要暂时停止当前的进展。

接下来,计划在稍后继续处理实际的数学运算部分,尤其是采样部分,可以留到第二天再继续。同时,还需要考虑如何处理绿色范围的相关问题,虽然目前还不清楚具体怎么做,但这部分工作肯定会需要大量的规划和思考,因此要提前计划好如何将这部分内容融入到整体中。

在MoveEntity函数中,你有两个未使用的变量:NewPlayerP和OldPlayerP。你会删除它们吗?

有两个变量 new playerold player 从未被使用,是否需要删除它们呢?有些疑问是否将来可能会用到这两个变量,特别是它们是否会在之后的工作中派上用场。不过,考虑到它们并未发挥作用,这部分代码可能需要进行大幅删除。如果确实需要删除它们,完全可以这样做。
在这里插入图片描述

这个方案能让你看到水面反射星空吗?

这个方案是否能让一池水反射星空?关键在于如何处理这些地图的分辨率。显然,技术本身是能实现这一效果的,但在采样过程中需要考虑许多因素。如果仔细考虑一下,可能会发现保持效果良好的过程中会有一些挑战。例如,问题在于采样时会遇到的一些困难,这可能会影响最终的视觉效果。

黑板:水面反射星空

当进行反射采样时,尤其是水面反射星空时,问题在于反射的方向和法线。每次反射都会在不同的方向上产生不同的反弹,这些反弹会去采样纹理图,而这种采样通常是稀疏的。因此,无法保证不会出现明显的伪影。比如一个反弹可能会采样到一个位置,而下一个反弹则可能跳跃到非常远的地方,这就无法获得期望的各向异性过滤效果,这对于水面反射的星空来说会导致不良效果。

在这种情况下,为了避免过于复杂的采样问题,可能会选择使用较低分辨率的纹理图,这样就不会用来进行高质量的反射,而更多是用于处理光照或光泽效果,而不是高质量的反射。

解决这种问题的常见做法是在3D中,通过从反射表面视角渲染场景,模拟类似的过程,但是否有其他复杂的技巧来避免这种糟糕的采样结果并不明确。可能会使用各向异性过滤来改善纹理采样,但它可能不足以完全解决问题。反射在3D中的问题可能也会面临类似的困境,有时可能无法完全避免,导致反射效果中出现闪烁的伪影。因此,这种问题需要注意。

如果你想看到光照根据法线贴图的球形形状而扭曲,这是否只是修复从环境贴图采样时的数学部分,还是还有更多内容?

如果希望看到光照根据法线贴图的球形形状发生畸变,那么这实际上只是修正采样部分数学计算的问题。明天进行修正时,应该能够实现这种弯曲效果,直到那时,当前并没有进行这一计算。因此,当前的效果并不表现为反射,原因是没有在计算反射,这一点应该不会让人感到意外。

拥有三个环境贴图(顶部、中部、底部)相较于使用一个整体的环境贴图有什么好处?

使用顶部、中部和底部等多个环境贴图的好处在于,它们可以帮助处理空间的不同层次,尤其是与高度相关的内容。如果只使用一个环境贴图,可能很难将高度信息融入其中,难以做到空间上的细节区分。因此,使用多个贴图可以更好地表示不同的空间位置和高度。如果能够找到一种方法将所有信息都从一个贴图中提取出来,可能会更简单,但目前并没有想到这样的方案。

黑板:预览环境贴图的工作方式

目前的思路是使用多个贴图来处理不同的环境元素。首先,已经制作好了地面纹理,会将其放入场景中进行绘制。接着,为场景中的实体添加阴影,可以将阴影和地面纹理分成两个不同的位图来处理。然后,使用一个中间贴图(包含光照信息)并将其中的光照信息添加到地面贴图中,这样就得到了一个包含光照和阴影的地面贴图。接着,保留一个仅包含光照的中间贴图和一个仅包含天空的顶部贴图。

通过这种方法,地面贴图会包含地面纹理、光照和阴影,中间贴图仅包含光照信息,而顶部贴图则包含天空元素。在进行采样时,期望能够通过这种方式获取到需要的3D信息。通过这种多贴图方式,可以实现对不同环境层次(如地面、光照、天空等)的处理,而单一的环境贴图似乎很难做到这一点。

我不太明白底部贴图的意义。地面是否会对地面上方的物体贡献光照?

底部贴图会将光照反射到地面上的物体上,这是因为现实中也是如此,地面反射的光线对位于地面上的物体外观有重要影响。目标是尝试将这种效果实现到游戏中。首先,计划是添加所有三种贴图,观察它们的效果,如果发现其中某一个贴图的效果不明显,可以去掉,从而节省计算资源。因此,目标是先尝试使用三种贴图,如果效果不理想,可以删除不必要的部分。如果最终只需要中间的贴图,也可以只保留它,如果其他贴图带来良好的效果,则继续保留。

对于每种环境贴图,需要为每个物体设置对应的环境贴图。

你会需要为每个“Z层”准备一组环境贴图吗?

在 Z 轴层级的处理上,计划是让这些层次以某种方式级联。首先,从最低的地面层开始渲染,这将生成底部的环境贴图。接着,我们需要处理天空贴图,它本质上是上方地面层的反转。接下来,通过使用地面贴图的 Alpha 通道来确定位置,再将其与上层中间层的光照合成,得到上方光源照射到下层的区域。

在层次叠加的过程中,可以通过这种方式模拟光照的变化,使得从上方照射下来的光照能够通过不同层次的合成展示出来。这是一种逐层渲染的方法,从底部开始渲染,然后逐步处理更高的层级。

此外,还可以考虑对过亮的部分进行处理,具体的做法可能会包括调整亮度,以避免场景中过亮的部分影响整体效果。

有没有计划对灯光进行过曝处理?(甚至是HDR?)

对于高动态范围(HDR)效果,并不确定是否会实现。考虑到软件环境中,可能无法有效地实现 HDR,因为使用浮点数(如 float16)可能难以处理。在考虑过亮效果时,可以通过使用中间缓冲区来模拟类似 bloom 的效果,但不太可能在软件管线中实现真正的 HDR。

可能的解决方案包括尝试使用一个额外的模糊缓冲区来增加一些 bloom 效果,但在当前的设置下,实际上并不认为能够实现 HDR,尤其是在软件管道中。

你能将所有灯光信息包含在一个5字节的位图中吗:0-255的吸收/反射比例,RGB的Alpha和散射?我想试试,但因为你警告过不要使用非32位对齐的结构,我感到有些犹豫。

可以将所有光照信息包含在一个五字节位图中,其中包含 RGB 吸收率、反射率、缩放因子、透明度和散射效果。在尝试这种方法时,可能会有一些犹豫,特别是考虑到非 32 位对齐的结构。然而,实际上,非 32 位对齐并不是一个严重的问题,因为这些数据本质上是作为像素值输入的,因此可能能够成功使用五字节格式。如果遇到问题,也可以通过将多个数据打包在一起,减少对齐问题的影响。

然而,关键问题在于如何处理法线信息。在这种方案中,法线的存储似乎是一个需要解决的难题。

甚至只使用0-2的光照范围也能得到很好的效果

可以通过将光照范围从零到 255 映射到更高的亮度范围来获得更好的效果。例如,可以考虑将其映射到双倍亮度等。这需要进一步思考和确认,尚不确定是否完全可行。

关于是否计划进行相机层级的光照处理,目前还不确定。

你是否计划做“相机级别”的光照效果,比如“眩晕手雷”之类的?

例如晕眩手雷这样的效果,可以通过将屏幕覆盖上某些效果或增加亮度来实现,这对系统来说非常容易做到。虽然不确定在当前的游戏中是否需要这些效果,但确实可以实现,而且并不复杂。如果可以不使用 alpha 通道,效果实现会更简单。

如果不需要Alpha,你能否使用RGBE,其中E是所有通道的指数?

使用RGB加上指数(E)来处理每个通道的HDR会使管道处理变得复杂,尤其是在加载和重新打包E值时,这是一个麻烦的过程。虽然实现HDR是可能的,但不一定是可行的,考虑到当前有很多需要关注的问题,可能不值得尝试做HDR。尽管如此,一旦渲染器的架构确定后,进行修改并非不可能,因此可以先实现非HDR版本,之后再考虑是否升级为HDR版本,关键是确保不会做出让未来更改变得困难的决策。

饱和度覆盖起来会很难吗?

饱和度的叠加其实并不难。一个简单的做法是通过拉取平均值来实现。例如,可以通过这种简单的方法来处理饱和度叠加。

引入render_entry_saturation

饱和度的叠加可以通过一个简单的后处理过程来实现。例如,可以添加一个渲染条目来调整饱和度值。通过这种方式,可以在最终的图像上应用饱和度调整。具体做法是,在进行渲染时,添加一个饱和度参数,并在最终渲染输出时循环遍历每个像素并改变它们的饱和度。

为了实现这一点,可以在代码中将目标缓冲区传入并根据给定的饱和度值进行处理。这样就可以在渲染过程中调整图像的饱和度,通过修改像素的饱和度值来达到预期效果。

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

黑板:饱和度

可以通过将颜色从灰色的距离作为一个概念来理解饱和度调整。基本思路是将RGB值归一化到0到1之间,然后计算它们的平均值(例如,R+G+B除以3)。接着,通过计算每个颜色与平均值的差异(即“delta”),根据饱和度值乘以这些差异,将颜色拉远或拉近灰色。这种方法相对简单,不需要进行复杂的颜色空间转换(如HSV),也能达到调整饱和度的效果。

计算平均值和增量

为了实现饱和度调整,可以通过简单的平均值方法来避免复杂的HSV转换。首先,计算RGB值的平均值(例如R + G + B / 3),得到一个灰度值。为了提高灰度估算的准确度,可以考虑根据人的视觉感知,例如绿色比红色和蓝色更敏感,进行调整,但基本思路是通过RGB值计算出一个灰度值。

然后,计算每个颜色与平均值之间的差异(即“delta”),这个差异表示从灰色到原始颜色的距离。接着,可以通过将该差异乘以饱和度水平来调整颜色。如果饱和度水平为1.0,差异将完全恢复,饱和度为0时颜色变为灰色。

这样的方法能够在不进行复杂的颜色空间转换的情况下调整饱和度,同时保留原始的透明度(Alpha通道)。
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在游戏中查看,然后驱动它通过一个动画参数

在上面的讨论中,首先通过简单的方式实现了图像的去饱和化,图像变为黑白。这种效果可以通过一个动画参数来驱动,使得饱和度变化呈现动态效果。通过使用GameState->Time来控制饱和度的变化,可以让饱和度周期性地从全色到无色之间转换。这个效果的计算公式为:GameState->Time * 0.5f + 0.5 * sin(GameState->Time)`,通过调整这个公式,可以实现饱和度随时间的动态变化。

然而,这种实现方式并不是最准确的去饱和化方法。为了提高准确度,可以使用HSV颜色空间的快速转换方法。虽然这种方法可能比当前的实现更精确,但也不一定是完美的,因此如果使用HSV转换并返回到RGB空间,能够达到更好的效果。

尽管当前的帧率非常低,且优化还不充分,但已经能看到饱和度的变化效果。
在这里插入图片描述

在这里插入图片描述

在这种方案中,光会从墙壁等地方水平反射吗?

在这个方案中,讨论了如何通过反射来处理光照,特别是在墙面等物体上实现水平方向的反射。重点是中间层的作用,这一层将用于获取局部的反射效果,尽管目前并不确定这种方式的效果如何。中间层的目标是模拟光的反射,但对于这种方法的实际表现,尚未完全确定。

在2D光照的实现中,通常采用一些“黑科技”方法,效果的好坏取决于这些方法的调优程度。尽管不确定这种方式是否会有效,但会继续尝试看看能否从中得到预期的结果。

太棒了!现在我们可以过度饱和了

可以通过增加饱和度值来实现过度饱和的效果。通过调整该值,可以看到图像的颜色变得更加饱和。此时,饱和度的增大会使颜色偏离正常范围,从而产生过饱和的视觉效果。

相关文章:

游戏引擎学习第99天

仓库:https://gitee.com/mrxiao_com/2d_game_2 黑板:制作一些光场(Light Field) 当前的目标是为游戏添加光照系统,并已完成了法线映射(normal maps)的管道,但还没有创建可以供这些正常映射采样的光场。为了继续推进&…...

NixHomepage - 简单的个人网站

💻 NixHomepage - 简单的个人网站 推荐下个人的开源项目,演示网站,项目链接 https://github.com/nixgnauhcuy/NixHomepage,喜欢的话可以为我的项目点个 Star~ 📷 预览 ⚙️ 功能特性 多平台适配 明亮/暗黑模式切换 W…...

window patch按块分割矩阵

文章目录 1. excel 示意2. pytorch代码3. window mhsa 1. excel 示意 将一个三维矩阵按照window的大小进行拆分成多块2x2窗口矩阵,具体如下图所示 2. pytorch代码 pytorch源码 import torch import torch.nn as nn import torch.nn.functional as Ftorch.set_p…...

Dockerfile 详解:构建自定义镜像

Dockerfile 是一种文本文件,包含了一系列指令,用于描述如何构建一个 Docker 镜像。通过 Dockerfile,我们可以将应用程序及其所有依赖打包成镜像,确保应用在不同环境中运行时保持一致性。掌握 Dockerfile 的写法和最佳实践,能够帮助我们高效地构建和管理容器镜像。 本文将…...

vue2老版本 npm install 安装失败_安装卡主

vue2老版本 npm install 安装失败_安装卡主 特别说明:vue2老版本安装慢、运行慢,建议升级vue3element plus vite 解决方案1: 第一步、修改npm 镜像为国内镜像 使用淘宝镜像: npm config set registry https://registry.npmmir…...

【细看open_r1】精读训练和评估模型以及生成合成数据的脚本(src/open_r1)

src/open_r1 目录下主要包含了一些用于训练和评估模型以及生成合成数据的Python脚本,下面我们对其中几个主要的Python文件进行深度剖析。 configs.py 这个文件定义了两个数据类 GRPOConfig 和 SFTConfig,它们分别继承自 trl.GRPOConfig 和 trl.SFTConf…...

数据库数据恢复—MongoDB丢失_mdb_catalog.wt文件导致报错的数据恢复案例

MongoDB数据库存储模式为文档数据存储库,存储方式是将文档存储在集合之中。 MongoDB数据库是开源数据库,同时提供具有附加功能的商业版本。 MongoDB中的数据是以键值对(key-value pairs)的形式显示的。在模式设计上,数据库受到的约束更少。这…...

Qt 控件整理 —— 按钮类

一、PushButton 1. 介绍 在Qt中最常见的就是按钮,它的继承关系如下: 2. 常用属性 3. 例子 我们之前写过一个例子,根据上下左右的按钮去操控一个按钮,当时只是做了一些比较粗糙的去演示信号和槽是这么连接的,这次我们…...

当 LSTM 遇上 ARIMA!!

大家好,我是小青 ARIMA 和 LSTM 是两种常用于时间序列预测的模型,各有优劣。 ARIMA 擅长捕捉线性关系,而 LSTM 擅长处理非线性和长时间依赖的关系。将ARIMA 和 LSTM 融合,可以充分发挥它们各自的优势,构建更强大的时…...

SpringBoot实战:高效获取视频资源

文章目录 前言技术实现SpringBoot项目构建产品选取配置数据采集 号外号外 前言 在短视频行业高速发展的背景下,海量内容数据日益增长,每天都有新的视频、评论、点赞、分享等数据涌现。如何高效、精准地获取并处理这些庞大的数据,已成为各大平…...

MySQL索引数据结构详解

索引的定义:方便Mysql更高效的获取排好序的数据结构 数据结构分为: 二叉树红黑树hash表B-Tree 二叉树规则:可视化二叉树 从父节点查找数据,每个节点最多有两个子节点,左子节点比父节点小,右子节点比父节…...

Python----PyQt开发(PyQt基础,环境搭建,Pycharm中PyQttools工具配置,第一个PyQt程序)

一、QT与PyQT的概念和特点 1.1、QT QT是一个1991年由The Qt Company开发的跨平台C图形用户界面应用程序开发 框架,可构建高性能的桌面、移动及Web应用程序。也可用于开发非GUI程序,比如 控制台工具和服务器。Qt是面向对象的框架,使用特殊的代…...

C语言——排序(冒泡,选择,插入)

基本概念 排序是对数据进行处理的常见操作,即将数据按某字段规律排列。字段是数据节点的一个属性,比如学生信息中的学号、分数等,可针对这些字段进行排序。同时,排序算法有稳定性之分,若两个待排序字段一致的数据在排序…...

物联网智能语音控制灯光系统设计与实现

背景 随着物联网技术的蓬勃发展,智能家居逐渐成为现代生活的一部分。在众多智能家居应用中,智能灯光控制系统尤为重要。通过语音控制和自动调节灯光,用户可以更便捷地操作家中的照明设备,提高生活的舒适度与便利性。本文将介绍一…...

哪吒闹海!SCI算法+分解组合+四模型原创对比首发!SGMD-FATA-Transformer-LSTM多变量时序预测

哪吒闹海!SCI算法分解组合四模型原创对比首发!SGMD-FATA-Transformer-LSTM多变量时序预测 目录 哪吒闹海!SCI算法分解组合四模型原创对比首发!SGMD-FATA-Transformer-LSTM多变量时序预测效果一览基本介绍程序设计参考资料 效果一览…...

Python实现决策树(Decision Tree)算法

在 Python 中实现一个决策树算法,可以使用 sklearn 库中的 DecisionTreeClassifier 类。这个类实现了分类任务中的决策树算法。下面是一个简单的例子,展示如何使用 DecisionTreeClassifier 来训练决策树并进行预测。 1. 安装 scikit-learn 如果你还没有…...

刷题日记---二叉树递归专题

文章目录 1. 从根到叶的二进制数之和2. 二叉树的坡度3. 总结 1. 从根到叶的二进制数之和 描述: 给出一棵二叉树,其上每个结点的值都是 0 或 1 。每一条从根到叶的路径都代表一个从最高有效位开始的二进制数。 例如,如果路径为 0 -> 1 ->…...

【C++】智能指针的使用及其原理

1. 智能指针的使用场景分析 下⾯程序中我们可以看到,new了以后,我们也delete了,但是因为抛异常导,后⾯的delete没有得到 执⾏,所以就内存泄漏了,所以我们需要new以后捕获异常,捕获到异常后dele…...

Jenkins 安装插件 二

Jenkins 安装插件 二 一. 打开 Dashboard 打开 Jenkins 界面,不管在任何界面,只需要点击左上角 Dashboard 按钮即可 二. 打开 Manage Jenkins 找到 Manage Jenkins -> System Configuration -> Plugins 点击 Plugins 打开界面如下 Updates&a…...

C++自研游戏引擎-碰撞检测组件-八叉树AABB检测算法实现

八叉树碰撞检测是一种在三维空间中高效处理物体碰撞检测的算法,其原理可以类比为一个管理三维空间物体的智能系统。这个示例包含两个部分:八叉树部分用于宏观检测,AABB用于微观检测。AABB可以更换为均值或节点检测来提高检测精度。 八叉树的…...

Java 大视界 -- 云计算时代 Java 大数据的云原生架构与应用实践(86)

💖亲爱的朋友们,热烈欢迎来到 青云交的博客!能与诸位在此相逢,我倍感荣幸。在这飞速更迭的时代,我们都渴望一方心灵净土,而 我的博客 正是这样温暖的所在。这里为你呈上趣味与实用兼具的知识,也…...

学习threejs,使用HemisphereLight半球光

👨‍⚕️ 主页: gis分享者 👨‍⚕️ 感谢各位大佬 点赞👍 收藏⭐ 留言📝 加关注✅! 👨‍⚕️ 收录于专栏:threejs gis工程师 文章目录 一、🍀前言1.1 ☘️THREE.HemisphereLight 二、…...

XML 命名空间

XML 命名空间 引言 XML(可扩展标记语言)是一种用于存储和传输数据的标记语言。在XML中,命名空间(Namespace)是一个非常重要的概念,它主要用于解决XML文档中元素和属性的命名冲突问题。本文将详细介绍XML命…...

kubernetes-cni 框架源码分析

深入探索 Kubernetes 网络模型和网络通信 Kubernetes 定义了一种简单、一致的网络模型,基于扁平网络结构的设计,无需将主机端口与网络端口进行映射便可以进行高效地通讯,也无需其他组件进行转发。该模型也使应用程序很容易从虚拟机或者主机物…...

【嵌入式Linux应用开发基础】ioctl函数

目录 一、概述 1.1. ioctl 的功能 1.2. 函数原型 1.3. 参数说明 1.4. 返回值 1.5. request 命令的定义 二、典型应用场景 2.1. 串口通信控制 2.2. 网络设备配置与管理 2.3. 字符设备控制 2.4. 块设备管理 2.5. 多媒体设备控制 三、关键注意事项 3.1. request 命令…...

开源的轻量级分布式文件系统FastDFS

FastDFS 是一个开源的轻量级分布式文件系统,专为高性能的分布式文件存储设计,主要用于解决海量文件的存储、同步和访问问题。它特别适合以中小文件(如图片、视频等)为载体的在线服务,例如相册网站、视频网站等。 FastD…...

从VGG到Transformer:深度神经网络层级演进对模型性能的深度解析与技术实践指南

一、技术原理(数学公式示意图) 1. 层深与模型容量关系 数学表达:根据Universal Approximation Theorem,深度网络可表达复杂函数: f ( x ) f L ( f L − 1 ( ⋯ f 1 ( x ) ) ) f(x) f_L(f_{L-1}(\cdots f_1(x))) f…...

深入了解 Oracle 正则表达式

目录 深入了解 Oracle 正则表达式一、正则表达式基础概念二、Oracle 正则表达式语法(一)字符类(二)重复限定符(三)边界匹配符(四)分组和捕获 三、Oracle 正则表达式函数(…...

机器学习-监督学习

1. 定义与原理 监督学习依赖于标记数据(即每个输入样本都对应已知的输出标签),模型通过分析这些数据中的规律,建立从输入特征到目标标签的映射函数。例如,在垃圾邮件检测中,输入是邮件内容,输出…...

Leetcode:学习记录

一、滑动窗口 1. 找出数组中元素和大于给定值的子数组的最小长度 右指针从左到右遍历,在每个右指针下,如果去掉左边元素的元素和大于等于给定值则左指针右移一次,直到小于给定值,右指针右移一个。 2.找到乘积小于给定值的子数组…...

探索顶级汽车软件解决方案:驱动行业变革的关键力量

在本文中,将一同探索当今塑造汽车行业的最具影响力的软件解决方案。从设计到制造,软件正彻底改变车辆的制造与维护方式。让我们深入了解这个充满活力领域中的关键技术。 设计软件:创新车型的孕育摇篮 车辆设计软件对于创造创新型汽车模型至…...

AI前端开发:解放创造力,而非取代它

近年来,人工智能技术飞速发展,深刻地改变着各行各业,前端开发领域也不例外。越来越多的AI写代码工具涌现,为开发者带来了前所未有的效率提升。很多人担心AI会取代程序员的创造力,但事实并非如此。本文将探讨AI辅助前端…...

探讨使用ISVA代替“Open Liberty使用指南及微服务开发示例”中日志审计功能

在Open Liberty使用指南及开发示例(四)一文开始日志审计功能占有了一定的开发工作量,那么是否可以使用IBM Security Verify Access(ISVA)代替以节省开发工作?如果可行,那么以后各类应用的日志审…...

log4j2日志配置文件

log4j2配置文件每个项目都会用到,记录一个比较好用的配置文件,方便以后使用时调取,日志输出级别为debug,也可以修改 <?xml version"1.0" encoding"UTF-8"?> <Configuration monitorInterval"180" packages""><prope…...

python专栏导读

由于本人非python工程师&#xff0c;是在自学python&#xff0c;所以本专栏的内容会显得很基础&#xff0c;甚至有些内容在python工程师看来实在太过于简单&#xff0c;在此清楚嘲笑&#xff0c;因为毕竟每个人都是从不懂、从基础开始的。 本篇作为导读和目录形式存在&#xf…...

Ollama与Vllm使用对比与优劣

Ollama和vLLM是两个用于优化大型语言模型&#xff08;LLM&#xff09;推理的框架&#xff0c;它们在性能、资源利用率、部署复杂性等方面各有优劣。以下是对这两个框架的详细介绍&#xff1a; 1. Ollama Ollama是一个轻量级且易于使用的框架&#xff0c;旨在简化大型语言模型…...

K8s之存储卷

一、容忍、crodon和drain 1.容忍 即使节点上有污点&#xff0c;依然可以部署pod。 字段&#xff1a;tolerations 实例 当node01上有标签test11&#xff0c;污点类型为NoSchedule&#xff0c;而node02没有标签和污点&#xff0c;此时pod可以在node01 node02上都部署&#xff0c…...

luoguP8764 [蓝桥杯 2021 国 BC] 二进制问题

luogu题目传送门 题目描述 小蓝最近在学习二进制。他想知道 1 到 N 中有多少个数满足其二进制表示中恰好有 K 个 1。你能帮助他吗? 输入格式 输入一行包含两个整数 N 和 K。 输出格式 输出一个整数表示答案。 输入输出样例 输入 #1 7 2 输出 #1 3 说明/提示 对于…...

本地部署DeepSeek后的调用与删除全攻略

在本地成功部署DeepSeek模型后&#xff0c;如何高效调用它发挥其强大功能&#xff0c;以及在不需要时妥善删除&#xff0c;是很多用户关注的重点。我也在后台接到了很多粉丝的留言&#xff0c;询问 DeepSeek 本地部署之后的一些调用和删除的问题&#xff0c;于是我在网上找了现…...

Qt Designer菜鸟使用教程(实现一个本地英文翻译软件)

1 安装Qt Designer 安装这个包的时候会自带安装 Qt Designer, 安装目录为python的安装根目录的 Lib/site-packages/qt5_applications/Qt/bin 目录下。 pip install pyqt5-tools2 新建窗体 2.1 新建主窗体 创建之后如下图&#xff1a; 设置主窗口大小&#xff1a; 设置窗…...

C++ 洗牌函数std::shuffle的用法

目录 1.简介 2.工作原理 3.std::shuffle 与 std::random_shuffle 的区别 4.rand 和 srand 5.std::shuffle 的使用方法 6.随机数生成器和分布器 7.注意事项 1.简介 std::shuffle 是 C 标准库中用于对序列进行随机重排&#xff08;洗牌&#xff09;的一种算法。它可以将容…...

MySQL InnoDB引擎 MVCC

MVCC&#xff08;Multi-Version Concurrency Control&#xff09;即多版本并发控制&#xff0c;是 MySQL 的 InnoDB 存储引擎实现并发控制的一种重要技术。它在很多情况下避免了加锁操作&#xff0c;从而提高了数据库的并发性能。 一、原理 MVCC 的核心思想是通过保存数据在某…...

【Elasticsearch】simple_query_string

Elasticsearch 的simple_query_string查询是一种灵活且容错性较强的查询方式&#xff0c;它允许用户通过简单的语法构造查询字符串&#xff0c;以实现对文档的搜索。以下是关于simple_query_string查询的详细说明&#xff1a; 1.基本概念 simple_query_string查询是一种基于字…...

数据结构 04

4. 栈 4.2. 链式栈 4.2.1. 特性 逻辑结构&#xff1a;线性结构 存储结构&#xff1a;链式存储结构 操作&#xff1a;创建&#xff0c;入栈&#xff0c;出栈&#xff0c;清空&#xff0c;获取 4.2.2. 代码实现 头文件 LinkStack.h #ifndef __LINKSTACK_H__ #define __LINKST…...

Java并发中的上下文切换、死锁、资源限制

在Java并发编程中&#xff0c;上下文切换、死锁和资源限制是开发者经常需要面对的问题。这些问题不仅会影响程序的性能&#xff0c;还可能导致程序无法正常运行。本文将深入探讨这些问题的原理、影响以及如何在实际开发中避免或解决它们。 目录 1. 上下文切换&#xff08;Con…...

DeepSeek教unity------MessagePack-01

MessagePack是C# 的极速 MessagePack 序列化器。它比 MsgPack-Cli 快 10 倍&#xff0c;并且性能超过其他 C# 序列化器。MessagePack for C# 还内置支持 LZ4 压缩——一种极其快速的压缩算法。性能在诸如游戏、分布式计算、微服务或数据缓存等应用中尤为重要。 这个库通过 NuGe…...

【大语言模型】最新ChatGPT、DeepSeek等大语言模型助力高效办公、论文与项目撰写、数据分析、机器学习与深度学习建模等科研应用

ChatGPT、DeepSeek等大语言模型助力科研应用 随着人工智能技术的快速发展&#xff0c;大语言模型如ChatGPT和DeepSeek在科研领域的应用正在为科研人员提供强大的支持。这些模型通过深度学习和大规模语料库训练&#xff0c;能够帮助科研人员高效地筛选文献、生成论文内容、进行数…...

泰勒公式推导以及常用展开式与近似计算

泰勒公式的基本思想是通过函数在某点的导数来逐渐构建一个多项式&#xff0c;该多项式能够近似函数在该点附近的值。我们通过一次次引入导数来改进近似&#xff0c;从而得到一个无限级数的展开。 准备工作&#xff1a;函数的定义和导数 假设我们有一个函数 f ( x ) f(x) f(x)…...

深入解析A2DP v1.4协议:蓝牙高质量音频传输的技术与实现

1. A2DP概述 A2DP&#xff08;Advanced Audio Distribution Profile&#xff09;是一种高质量音频流媒体协议&#xff0c;旨在实现高质量音频内容的分发&#xff0c;通常用于通过蓝牙设备传输音频数据&#xff0c;例如将音乐从便携式播放器传输到耳机或扬声器。与传统的蓝牙语…...

STM32引脚VBAT和RTC的关系

一、RTC简介 1、RTC (Real Time Clock)&#xff1a;实时时钟。RTC是个独立的定时器。RTC模块拥有一个连续计数的计数器&#xff0c;在相应的软件配置下&#xff0c;可以提供时钟日历的功能。修改计数器的值可以重新设置当前时间和日期。RTC还包含用于管理低功耗模式的自动唤醒单…...