游戏引擎学习第98天

devtools/2025/2/12 16:31:47/

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

开始进行一点回顾

今天的目标是继续实现正常贴图的操作,尽管目前我们还没有足够的光照信息来使其完全有用。昨日完成了正常贴图相关的基础工作,接下来将集中精力实现正常贴图的基本操作,并准备好在下周进一步处理如何生成可用的光照字段,供正常贴图使用。

首先,计划实现一个模拟球形正常贴图生成器,然后将其与环境贴图进行采样,最终验证生成的正常贴图是否合理。尽管之前没有正确连接这些部分,但目标是确保正常贴图能正确显示并为接下来的光照处理做好准备。

讨论 MakeSphereNormalMap 和 GPU 与 CPU 渲染器

首先,实现正常贴图时,需要传入一个位图和粗糙度值。粗糙度值作为材质参数,被打包到透明度通道中。正如之前所讨论的那样,并不一定需要在正常贴图中存储所有三个法线值,因为在GPU中,通常只会存储两个值。因此,可能在CPU端也只存储两个值。

主要目标是使用CPU渲染的方式帮助理解整个过程,并展示如何优化,而不是为了实现游戏中高效的性能。毕竟,CPU并非为图形渲染设计,尽管随着AVX-512等技术的发展,CPU的图形处理能力有所提升,但从总体上看,CPU在图形渲染方面无法与GPU相匹敌。因此,CPU渲染的质量和效率不能与GPU相比较,主要还是为了教学和理解过程。
在这里插入图片描述

为 MakeSphereNormalMap 创建 TreeNormal

首先,正常贴图生成器已经准备好了。接下来,在加载时,将生成一个正常贴图供使用。最初使用的是树的位图来进行测试。虽然现在并没有真正的树的正常贴图,但假设我们有一个树的正常贴图,并将粗糙度设为1,即完全平滑和反射的材质,这样有助于调试。

接下来,需要为此分配空间。通过调用 MakeEmptyBitmap 来分配空间。需要传入内存区域、宽度和高度,宽度和高度与树的位图的宽高一致。由于接下来会直接写入数据,因此不需要将其清零。

这些贴图会被存储在临时数组(transient array)中,这与地面缓冲区类似。树的正常贴图将被存放在相同的内存区域中,这样可以确保它们与其他临时状态共享内存。

接下来,将生成一个球形正常贴图,并将其存储在该内存区域。然后,可以将树的正常贴图传递到适当的函数中,确保能够从中进行采样,下一步需要返回到相关的例程,处理和应用该正常贴图。

在这里插入图片描述

让游戏运行,查看 NormalMap 的解包并改变其打包方式

在对法线贴图进行采样时,首先进行了解包操作。法线的值是从0到255以及0到128的范围。这种打包方式存在一定问题,尤其是法线的Z值应该是128加127的范围,而不是直接使用0到128的范围,因为法线的Z值应该始终是正值。为了一致性,可以采用一种更合理的打包方式,即将法线的Z值加1,再除以0.5并进行缩放处理,这样就可以确保所有值都在相同的坐标空间内。

目前的做法是只解包了法线,并进行线性插值,但仍未对解包后的值进行适当的缩放处理,因此无法得知这些值的具体含义。为了正确使用法线,需要将其调整到正确的范围,考虑到正负值的变化,并进一步处理这些值。
在这里插入图片描述

在这里插入图片描述

黑板:考虑问题的 hacky 特性

在处理3D问题时,尤其是在一个本质上是2D的世界中,常常会遇到一些问题,这些问题通常来源于需要将3D的概念压缩到2D环境下进行处理。具体来说,法线(Normals)计算是这种问题的一个例子,尤其是在2D渲染中,如何处理法线方向和深度(Z轴)的问题。

在绘制物体时,例如绘制一个球体,可以观察到法线指向的方向。例如,球体上的法线可能会指向我们,或者指向其他方向(比如上、下、左右等),而且这些法线会随着物体的远离而变得越来越小。需要注意的一点是,球体的法线永远不会指向屏幕后面,也就是说,我们不可能有指向屏幕后方的法线。

具体来说,在这种2D环境中,法线的意义变得难以直观理解。可以通过把法线的方向与世界坐标系的轴进行比较来帮助理解:

  • 法线的 X 轴对应于世界的 X 轴。
  • 法线的 Y 轴更像是世界的 Z 轴,用于描述地面和天空之间的过渡。
  • 法线的 Z 轴则描述了物体的“外向性”,表示物体的指向。

这种映射方式的背后逻辑是:在2D中,物体的法线方向无法指向物体的背面,所有可见的法线方向都指向物体的前面。因此,在2D渲染环境中,法线的 X、Y、Z 坐标实际上映射到了一个类似的坐标系统中。在这个系统中,法线的 XY 轴代表着物体的空间位置,而 Z 轴则决定了物体的相对外向性,即指向屏幕的方向。

通过这种方式,即使在2D世界中处理3D法线的问题,我们也可以通过这种转换方法将其映射到合适的方向和位置,从而在图形渲染中正确地表示法线信息。

黑板:地面的法线

在处理地面和物体的法线时,地面法线的情况与其他物体有所不同。地面通常是平坦的,但如果地面有一些起伏(例如不平整的地形),那么地面的法线就不再是单一方向的,而是可能指向半球的各个方向。因此,地面法线的处理相对复杂,它不像其他物体那样总是指向一个固定方向。

对于这种情况,可以考虑两种类型的法线贴图:

  1. 正面法线贴图:这类贴图的法线主要指向物体的前面,通常用于表示像树等物体的表面。法线的方向是确定的,主要指向物体的外部。
  2. 上方向法线贴图:这类贴图的法线是真正的三维法线,代表物体表面的实际方向。在地面这种场景中,法线方向可能指向不同的方向,以适应地面的起伏和变化。

因此,对于这两种法线贴图,可能需要不同的纹理采样路径。一种用于处理类似树木等物体的法线,另一种则用于处理地面等平坦或起伏的物体。根据绘制的物体类型,可以切换使用不同的法线贴图采样路径,具体取决于物体是地面类型还是其他类型的物体。

虽然大多数情况下法线指向物体的外部,但也可以考虑一些特殊情况。例如,在某些游戏场景中,如俯视角的地牢,墙壁的法线可能会指向不同的方向。墙壁的法线不仅指向外部,还可能指向不同的方向。这表明法线不仅仅限于指向物体的外面,某些特殊情况下,物体(如墙壁、地牢等)的法线也可能指向其他方向。因此,在处理这些场景时,可能需要更多的法线信息,尤其是对于像精灵(sprite)等物体,它们的法线有时会指向不同的方向。

综上所述,法线的方向处理不仅仅依赖于物体表面是否指向一个固定方向,还要根据具体场景的需要来调整法线的处理方式。在不同的纹理类型和物体类型之间,可能需要采用不同的法线贴图和采样路径。

处理前向位图,首先引入 UnscaleAndBiasNormal

目前,我们只处理正面朝向的法线贴图,地面部分会稍后处理,可能会使用不同的路径来处理。假设我们已经将法线贴图正确地加载进来,接下来需要做的就是处理法线值的缩放。

首先,我们需要从法线贴图中提取法线,并对这些法线进行缩放。法线的每个分量(X、Y、Z)都被压缩到了0到255的范围内,因此首先需要将这些值从0到255的范围转换到0到1的范围。这是通过简单的除以255来实现的。

接下来,我们需要将0到1的范围映射到-1到1的范围。这意味着,我们需要对转换后的法线分量进行扩展,使其从-1开始,然后通过将值乘以2来覆盖整个-1到1的范围。这样,法线的每个分量就能正确地反映其在三维空间中的方向。

至于法线的W分量,它表示粗糙度,通常已经是一个从0到1的值,因此无需进行任何额外的处理,只需要像处理颜色值一样进行转换即可。

最终,经过这些处理后,我们就能够获得一个合理的法线值,它已正确缩放并适用于渲染计算。
在这里插入图片描述

在这里插入图片描述

在游戏中查看,并回顾代码

在进行法线处理时,当前的操作是将法线的每个分量从[0, 255]的范围转换到[0, 1],然后再从-1开始,向正负两个方向扩展到[-1, 1]范围。对于法线的处理似乎是正确的,但结果显示的法线效果不如预期。虽然一半的结果看起来正常,但另一半的法线却显得有些奇怪,给人一种不太对劲的感觉。

接下来需要完成的部分是对法线进行插值(lerp),然后将其重新调整到正常范围。虽然法线从[0, 255]编码的方式可能会引起一些后续的影响,但这个步骤可能依然是可行的。理论上,先进行插值,再将法线解压并调整回正常的范围,应该是可行的。

在此之后,还有一个关于环境采样的操作,预计是要通过法线来作为颜色进行显示。然而,这一部分可能并没有按预期的效果展示。

仅绘制法线并将 Texel.a 强制为 1.0f

首先,打算直接绘制法线,将其作为纹素(Texel)显示。为了避免颜色闪烁,将纹素的透明度(Texel.a)强制设置为1.0,以确保没有不必要的视觉效果。然而,显示的结果却是完全黑色,说明出现了某种问题。

由于最近在代码处理上有所不确定,决定先停下来验证所有的逻辑,确保每一步都按照预期执行。尤其是对于昨天编写的代码,虽然今天继续处理,但仍然缺乏完全的信心,因此需要仔细检查一下各个环节的工作情况。
在这里插入图片描述

步进 NormalMap 代码

为了验证从法线贴图中获取的数据,打算直接查看样本值。发现解包后,法线的各个分量(A、B、C、D)都为零,这显然不合理。正常情况下,法线贴图的值不应该为零,除非某些特殊的区域,比如边缘区域。感到这种情况不太可能出现,因此决定进一步调查这个问题。计划直接查看法线贴图,以了解为什么会出现这种异常情况。

领悟时刻:我们仍然在从纹理中采样

发现了第一个问题:昨天的代码复制粘贴时没有修改法线贴图,仍然从错误的纹理中进行采样。这显然没有任何帮助,导致了当前的问题。
在这里插入图片描述

继续步进代码

发现了法线贴图中的问题,法线 B 和 D 显示为零,其他法线的值也看起来完全错误。例如,1, 1, 0 不是一个有效的法线。于是决定查看法线贴图的实际内容,查看内存中的值,并比较这些值是否合理。同时,通过查看生成球体位图的函数,进一步验证问题的来源。
在这里插入图片描述

在这里插入图片描述

领悟时刻:通常写一个像素时,你想移动到下一个像素

对昨天的代码产生了怀疑,指出在一个像素上写入多次是没有意义的。通常,在写入一个像素后,应该移动到下一个像素。
在这里插入图片描述

快速检查数学

检查了数学公式,发现之前的计算存在问题。通过正确生成位图 UV,将 X 除以宽度来获得正确的值,并使用该值生成适当的 X 和 Y 坐标。
在这里插入图片描述

黑板:Nz 应该通过毕达哥拉斯定理解决

通过使用勾股定理来计算球体的 Z 坐标。已知 X 和 Y 坐标,以及球体的半径为 1,可以使用公式 x 2 + y 2 + z 2 = 1 x^2 + y^2 + z^2 = 1 x2+y2+z2=1 来计算 Z 坐标。通过将 X 和 Y 的平方移到方程的右边,并对其进行平方根运算,得到 Z 坐标的值: Z = 1 − x 2 − y 2 Z = \sqrt{1 - x^2 - y^2} Z=1x2y2

计算 Nz

通过使用勾股定理计算 Z 坐标,公式为 Z = 1 − x 2 − y 2 Z = \sqrt{1 - x^2 - y^2} Z=1x2y2 ,可以得到球体上的 Z 坐标。

设置法线并查看像素是如何打包的

在处理法线时,首先将其范围从 -1 到 1 转换到 0 到 1,然后乘以 255 来进行打包,最后将粗糙度值放入 ALF 位置。完成打包后,进行截断和位移操作。这样就能将法线打包到适当的颜色通道中。需要确保解包操作能正确恢复原始顺序,以保证法线数据的正确性。
在这里插入图片描述

步进代码

正在检查计算出来的法线,期望能够得到实际可用的法线。尝试查看法线值,但发现值仍然是非常不合理的,如 2.5 或 250。对于这些值感到困惑,认为它们没有意义。
在这里插入图片描述

在这里插入图片描述

黑板:处理球体外的区域

面临的另一个问题是,有时计算结果可能会偏离球面。因为填充整个位图时,可能会得到一些超出球面范围的值。为了解决这个问题,可以考虑将这些值处理为合理的法线方向。例如,可以将这些超出范围的值设定为指向上方的法线(例如,(0, 0, 1)),或者可以选择将它们设置为球面上最近的点所对应的法线方向。

引入 RootTerm

为了处理球面之外的情况,首先计算根式项,确保在计算时根项不会小于零。如果根项大于等于零,则可以正常计算平方根。如果根项变为负数,则意味着将进入复数范围,这显然是不合适的。因此,在这种情况下,可以将其替换为更有用的值。为了简化处理,当前可以假设不在球面上的点直接指向上方((0, 0, 1)),但是未来可能会考虑采用更复杂的方案来处理这些情况。
在这里插入图片描述

检查法线值

通过处理越界的情况,得到了更合理的结果,值为(0, 0, 1),这正是预期的结果。当进行线性插值时,应该能够得到合理的结果。接着,通过对法线进行去偏移和缩放,期望得到的结果是(0, 0, 1),实际上也得到了相似的结果。不过,有些地方可能需要进一步检查,尤其是确保128能够精确映射到预期的值。
在这里插入图片描述

在游戏中查看,发现一些错误的负值

遇到的问题是没有任何合理的值被填充进来,结果总是指向正上方,这显得很奇怪。原本应该通过计算根式值(1.0减去x和y的平方和)来得到一些结果,但实际情况是,这个计算似乎没有正确执行。通过检查,发现即使期望的值为(0, 0),也没有得到合适的计算结果,可能存在某些环节的问题,导致无法得到正确的值。
在这里插入图片描述

查看正确的 NormalMap

现在代码看起来已经回到一个更合理的空间,虽然有些部分可能还需要进一步调整,但至少不再完全荒谬了,算是取得了一些进展。现在的结果符合预期,虽然还存在一些小问题,但总体上已经接近正确的方向。这是逐步前进的过程。
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

重新计算 Texel.rgb 使其在有效颜色范围内

为了绘制效果,可以在渲染过程中调整值的范围,将其从负一到一的范围转换为有效的颜色范围。通过将值乘以0.5并加上0.5,可以将其调整到有效的颜色范围内。这可以帮助观察正常的映射效果,比如随着横向移动,红色的数量增加,纵向移动时绿色增加等。然而,可能存在某些细节上的问题,比如lerp计算不太正确,导致某些地方的效果出现异常。因此,需要更仔细地检查其中的实现,确保所有的计算都正确。如果关闭正常映射后,效果回归正常,可能是与正常映射相关的某些计算出现了偏差。
在这里插入图片描述

关闭 NormalMap,并考虑使用比树位图更明确定义的东西

为了更好地调试问题,考虑用一个非常明确的形状而不是当前的树形进行测试。这可以帮助更清晰地显示出是否存在问题,因为树形可能不如预期那样干净,导致问题不容易察觉。使用一个简单且明确的形状进行测试,可能会提供更直观的反馈,帮助发现并解决潜在的问题。
在这里插入图片描述

在这里插入图片描述

暂时将 TreeNormal 当作纹理图绘制

为了调试法线贴图,考虑将树的法线直接绘制出来,并将其当作一个纹理来观察。这样可以更清楚地了解为什么采样结果看起来如此奇怪。通过观察,发现图像存在sRGB颜色空间的异常,尤其是黄色区域,可能是由于128作为零值导致的。此外,虽然边缘看起来没有明显的畸形,但采样结果仍然显得有些不对劲。因此,下一步需要进一步检查法线贴图的采样过程,确保没有发生任何异常,导致图像出现带状现象。
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

引入 BilinearSample 函数

为了避免出错,可以考虑将代码封装成一个函数,使其更加简洁且不易出错。例如,可以定义一个内联函数来执行双线性采样,该函数接收加载的位图(即纹理)以及采样的 x 和 y 坐标,完成采样后返回一个结构体,其中包含四个采样结果。

接下来,将所有相关的法线贴图采样操作替换为此封装好的双线性采样函数,使得代码更加简洁和易于管理。通过这种方式,可以更轻松地进行法线贴图的处理和调整,同时保持代码的整洁性。

区分一下几个单词 Linear 线性 Nearest 是临近

在插值方法中,“临近” 和 “双线性” 通常是用来描述不同的插值算法。以下是这些词语的英文翻译以及简要说明:

  1. 临近插值 - Nearest Neighbor Interpolation
    这种方法选择离目标点最近的像素值,简单快速,但效果较粗糙。

  2. 双线性插值 - Bilinear Interpolation
    该方法使用目标点周围四个像素点的加权平均来计算目标点的值。相比于临近插值,双线性插值生成的结果更加平滑。

  3. 单线性插值 - Linear Interpolation
    这种插值方法通常用于一维数据中,通过两个相邻数据点的线性插值来估算目标点的值。在二维空间中,可以将其视为沿着两个轴进行线性插值,常用于图像的一维变化。

在这里插入图片描述

在这里插入图片描述

在游戏中查看,并切换到法线图调节纹理的路径

目前的状态看起来相对合理,虽然仍然有些不对劲,可能存在轻微的剪切问题,需要进一步检查。总体上,当切换回正常路径时,可以看到正常映射的颜色与树的纹理相互作用,表明两者的采样已经相对正确。现在可以开始进一步进行操作,继续朝着目标推进。
在这里插入图片描述

用 NormalMap 照亮纹理

当前的问题是如何处理环境映射。需要传入环境映射以进行采样,并决定如何调节这些映射。可以考虑将它们直接相加,这可能是一个合理的选择,但也需要确保不会发生溢出,因此需要对结果进行限制,确保所有值都在0到1之间。此外,还存在一个问题,即光照颜色是预乘 alpha 的,因此从光照颜色中获取的值需要乘以纹理的 alpha 值,因为纹理的 alpha 值是我们预期的值,这也是需要额外处理的部分。
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在游戏中查看

当前的情况是,虽然已经使用法线贴图进行照明,但法线贴图本身并没有包含任何光照值,实际上只使用了法线贴图中的值来进行照明,这样的效果非常有限。因此,下一步的目标是从某个来源获取光照值,以便真正实现照明效果。
在这里插入图片描述

处理 environment_map 的内容

在处理法线贴图时,最初的代码中使用了错误的法线轴(Z轴),实际上应该使用法线的Y轴来确定方向,因为Y轴值决定了位置的高低。法线的Y值范围从-1到1,因此,之前的测试方法不太适用,应该调整为更合理的范围测试。根据法线Y值的大小,决定使用哪个地图:如果Y值小于-0.5,则使用底部地图,如果大于0.5,则使用顶部地图。

为了混合顶部和底部地图的光照值,需要根据法线Y值计算权重。如果法线Y值小于-0.5(即最小值),则混合底部地图的光照,如果法线Y值接近0.5,则混合顶部地图的光照。计算时,首先将法线Y值从-1范围内的数值映射到0到0.5的区间,再进行适当的处理以得到正确的结果。

接下来,在选择了顶部或底部地图后,需要进行采样,获取相应的光照值。对于采样过程,传递法线的X、Y、Z值有些不清楚是否完全符合要求,因此需要进一步思考这些值在采样中的具体意义,并决定是否需要调整。
在这里插入图片描述

黑板:从环境映射中选择

在处理这个问题时,首先需要理解的是,当前场景中的点和法线信息可以视为类似于重心坐标。通过屏幕空间中的UV坐标,可以确定环境贴图中对应的位置。UV坐标告诉我们在环境贴图中的相对位置,例如,某个点可能对应UV值(0.2, 0.8)。此外,法线会指示观察方向,帮助确定视角和环境贴图之间的交点。

接下来,需要计算的是如何根据法线与环境贴图的关系来确定需要采样的贴图位置。假设法线与Z轴的偏离角度不超过45度(即0.5),这样可以简化计算过程。这个角度范围可以视作一个锥形区域,向上和向下各有一个锥形视野。我们需要确定每个法线值对应的环境贴图上的像素。

在面对水平区域(即正中央平面)时,计算变得更加复杂,因为我们没有明确的标准来处理这一部分。为此,需要更细致地思考如何处理这个区域,尤其是在定义环境类型时,可能会有不同的处理方式。总的来说,目标是根据法线的方向,从环境贴图中选出合适的像素进行采样。

使中间采样为空,并让 SampleEnvironmentMap 生成我们可以使用的值

在处理环境贴图的采样时,首先需要解决的一个问题是如何去除粗糙度的影响。通过去除粗糙度的计算,可以简化处理过程,将其暂时从方程中移除。这样,粗糙度值不会直接影响我们选择的 LOD(细节层次)地图。接下来,需要将粗糙度量化到一个合适的桶中,以决定选择哪个 LOD 索引。

通过将粗糙度值乘以数组的计数(即细节层次的数量),我们可以得到一个在范围内的整数,这个整数表示粗糙度的层次。然后,这个索引值将用于选择对应的 LOD 图像。接下来,使用双线性插值采样从相应的位图中获取颜色值。

在完成粗糙度的计算和位图选择后,进一步的处理是进行双线性插值的计算。通过获得的纹理坐标 (FX, FY),可以进行 sRGB 双线性插值,得到需要的颜色样本。

当完成这些步骤后,接下来需要处理的是如何在环境贴图中进行交点计算。通过法线的方向与环境贴图中的对应关系,可以计算出正确的采样位置,获取合适的颜色样本。然而,这一部分的交点数学计算尚未完全完成,预期在下次工作时继续进行。

为了避免错误和不一致,程序会检查环境贴图是否有效,确保不会因为无效的贴图而导致程序崩溃。在确认有效的环境贴图后,程序将从指定的 LOD 中获取相应的颜色值,并继续后续的处理。
在这里插入图片描述

展望接下来的内容

目前,已经准备好继续进行的步骤包括生成环境贴图和处理交点数学计算。交点计算是为了确定应该在哪里进行采样,这一步骤非常关键,需要计算出正确的采样位置。完成这些后,还需要确保地面和天空等元素能够参与到采样中,这意味着需要进一步处理与这些元素相关的内容。

回顾今天的清理工作

今天主要是进行了一些清理工作,修正了昨天输入的错误。还进行了代码的简化,将一些代码抽取成函数,这样可以避免因拼写错误而带来的麻烦。在开发的初期,抽象出可以正常工作的函数是非常有帮助的,虽然在优化阶段可能需要将这些函数展开,但在确保功能正确的过程中,这种做法可以减少错误的发生。

当你插值两个法线时,你可能得不到一个标准化的向量。你对这个问题有什么看法?请简要评论一下。

在讨论关于法线的插值时,提到了一些可能会导致非标准化向量的问题。首先,插值(lerp)时可能会得到一个不正常的法线,这就是为什么需要特别注意插值过程。虽然现在的插值方法可能产生不标准化的法线,但在做交集计算时,可能可以通过在除法步骤中进行标准化来解决这个问题,这样可以避免手动标准化向量。不过,如果在实际计算中需要一个真正标准化的法线,就会遇到问题。

目前,代码中的法线选择处理不完全正确,因为插值时使用了法线的Y分量,导致插值权重不准确。为了确保正确性,虽然可以选择在计算过程中始终对输入的法线进行标准化,这样可以确保正确性,尤其是在硬件上,特别是桌面PC上,通常会有快速的逆平方根运算,避免存储Z分量,而是通过计算X和Y来得到Z值。因此,可以在着色器中直接计算Z分量,而不需要在CPU端进行这一操作,这也是一个高效的做法。

问:我们如何在计算每个精灵的像素值时进行光源采样?会有一个所有光源的列表吗?还是这会是某种参数?

在计算每个像素的亮度值时,光源的采样方法是通过环境贴图(environment maps)来实现的。所有的光源将会被写入到环境贴图中,然后通过从贴图中提取来进行采样。因此,不需要维护一个光源列表。只需要一个纹理,就能够处理任意数量的光源。无论光源的数量有多少,这种方法的开销是固定的,不会因为光源数量的增加而增加额外的成本。这是一种简化处理的方式,可以高效地处理多个光源。

会为艺术制作法线图吗?

目前计划是将法线贴图(normal maps)包含在艺术素材集中,但具体如何生成这些贴图还不确定。可能会使用离线处理的方式生成法线贴图,而不是由艺术家直接制作。因此,虽然法线贴图会作为艺术集的一部分,但它们的创建过程尚未确定。

你会讲解更高级的 BRDF 吗?还是只会讲像 Phong 这样的简单光照方程?

目前的照明模型尚未采用BRDF(双向反射分布函数),因为在当前的简化照明模型中,使用BRDF并不会带来显著的效果。BRDF通常需要复杂的照明环境才能看到其效果,而目前的模型较为简单,尚未考虑视角角度的影响。虽然可以通过简单的线性组合来进行照明计算,但目前的做法并不包括视角衰减或光照反弹等计算。

尽管如此,如果未来需要,可以考虑在现有模型中加入视角依赖的光照衰减计算,但目前这些功能不在优先考虑之内。此外,由于整体模型仍然是为适应2D方案进行简化的,加入更复杂的计算可能会造成效率上的浪费,因此目前并不计划进一步增加这些计算。

如果我不想让我的游戏看起来“很普通”?有没有其他类型的图可以让我游戏更独特?

可以使用一种叫做“异常法线贴图”(Abnormal Maps)来让游戏的外观更独特。异常法线贴图的概念来源于将两个不同的纹理(A和B)进行操作,通常通过加法、乘法或卷积等方式进行组合。这种方法会破坏原本法线贴图中的计算,从而产生全新的效果。

例如,可以将两个法线贴图进行乘法操作,生成一个全新的异常法线贴图。通过这种方式,虽然会失去原有的法线计算精度,但可以得到一个独特且不同的视觉效果,从而使游戏的画面显得更加与众不同。不过,这样的效果往往会显得不太自然,因此使用时要小心平衡。

问:有些游戏会使用“凹凸贴图”作为法线图的补充或替代。它们之间有什么区别?各自的优缺点是什么?

现代游戏中,通常不再使用凹凸贴图(Bump Map)替代法线贴图(Normal Map)进行光照计算。凹凸贴图的作用通常是为了压缩法线贴图的大小,或者通过凹凸贴图中的值来近似法线,从而减少法线贴图的存储需求。

另外,凹凸贴图还可以用来实现其他效果,例如位移或遮蔽效果,而不仅仅是光照计算。这些效果可能会用来增强视觉表现,但相比法线贴图,凹凸贴图在真实光照的模拟上并不如法线贴图准确和高效。因此,凹凸贴图在现代游戏中更多地用于非光照效果的实现。

黑板:凹凸贴图与法线贴图

凹凸贴图(Bump Map)和法线贴图(Normal Map)之间有着明显的区别。凹凸贴图主要存储的是表面点的高度值,即每个像素的高度信息,而法线贴图则存储每个表面点的法向量,即表面在该点的方向。法线贴图提供了更精确的信息,因为它直接给出了表面的法线方向,能够更好地用于光照计算。

凹凸贴图通过高度差来计算法线,通常无法准确表达表面细节,特别是硬边等特征,因为它只存储高度变化信息,不能明确表示表面的方向。相比之下,法线贴图能够直接表达表面法线,可以捕捉到更多的细节和更精确的光照效果。

法线贴图的存储效率比凹凸贴图高,因为它直接给出法线的方向,不需要通过计算周围像素的差异来推测。而凹凸贴图通常用于需要节省存储空间的情况,或者在某些情况下,凹凸贴图可以用来生成其他效果,比如遮蔽效应。

在技术上,法线贴图实际上可以被视为凹凸贴图的导数,或者更准确地说,是凹凸贴图的梯度,即凹凸贴图的高度变化方向。总之,法线贴图比凹凸贴图更为强大和直观,可以更好地表达复杂的表面形态和光照效果。

问:这个效果最终会像凹凸贴图的效果吗?

法线贴图(Normal Map)实际上是凹凸贴图(Bump Map)的更强大版本。虽然凹凸贴图可以产生类似的效果,但它的表现远不如法线贴图精确。简单来说,凹凸贴图可以看作是法线贴图的简化版。

我们能否通过在绘制位图时使用帧缓冲区作为环境映射来实现反射?

通过将帧缓冲区作为环境贴图来实现反射是目前的计划。这样,物体的反射效果,比如地面上的反射,就可以通过这种方式实现。虽然还不确定最终效果如何,但这是目前的尝试方向。

如果你把表面看作一个隐式函数 f(x,y,z) = 0,那么它就是该函数的梯度。它与表面切线的叉积相同。

在讨论时,假设表面是一个隐式函数,实际上是在谈论该函数的梯度。与显式的高度图(如 bump map)不同,隐式函数会在每个位置都有一个值,然后寻找等高线。这时的确会涉及到梯度。然而,bump map 并非隐式函数,它是显式的,因此不能直接通过梯度来处理。为了从 bump map 中得到法线,必须通过计算切线方向,并将它们的叉积作为法线。具体来说,在任何点上,我们有两个切线方向,分别是 U 和 V 方向,然后将它们的叉积得到法线。虽然这类似于梯度,但由于 bump map 是显式的,不能直接像梯度那样操作,因此需要其他方法来处理。

我们已经结束了所有问题

总结来说,讨论结束后,计划继续进行接下来的步骤。主要目标是下周开始构建照明采样缓冲区,并使用正确传递的法线贴图来进行采样。尽管这仍然是一个实验性的过程,因为具体的实现方法还不明确,但会继续努力并尽力实现所需的效果。虽然精确的光照模拟非常复杂,通常需要进行全光照传输计算,但由于时间限制,通常只能进行近似的光照计算,且这种近似方法在3D环境中也属于常见的做法。接下来几周将继续调整这些近似值,直到找到一个能够作为渲染设置基础的方案。


http://www.ppmy.cn/devtools/158256.html

相关文章

CEF132 编译指南 MacOS 篇 - 启程:认识 CEF (一)

1. 引言 在当今的软件开发领域,将 Web 技术融入桌面应用程序已成为一种趋势。开发者们寻求一种方式,既能充分利用原生应用的性能,又能享受 Web 开发带来的高效和灵活性。Chromium Embedded Framework (CEF) 应运而生,它是一个基于…

webpack配置之---output.chunkFilename

output.chunkFilename output.chunkFilename 是 Webpack 中用来配置 异步代码块(动态导入、懒加载等)文件名的选项。它控制的是通过代码拆分生成的那些非入口点(entry)文件的命名规则。 在 Webpack 构建过程中,除了打…

物联网领域的MQTT协议,优势和应用场景

MQTT(Message Queuing Telemetry Transport)作为轻量级发布/订阅协议,凭借其低带宽消耗、低功耗与高扩展性,已成为物联网通信的事实标准。其核心优势包括:基于TCP/IP的异步通信机制、支持QoS(服务质量&…

ubuntu 22.04 安装 cuda sdk 11.8

ubuntu 22.04 安装 cuda sdk 11.8 linux kernel 版本太高的问题 主要思路是先安装 nv 显卡驱动,这会同时安装 kmd driver 然后安装 cuda sdk 11.x 时不安装 kernel driver 下载 display driver 搜索 display driver https://www.nvidia.com/en-us/drivers/ 选择比…

Centos10 Stream 基础配置

NetworkManger 安装 dnf install NetworkManager 查看网络配置 nmcli [rootCentos-S-10 /]# nmcli ens33:已连接 到 ens33"Intel 82545EM"ethernet (e1000), 00:0C:29:08:3E:71, 硬件, mtu 1500ip4 默认inet4 192.168.31.70/24route4 default …

【开源项目】数字孪生武汉~超经典智慧城市CIM/BIM数字孪生可视化项目——开源工程及源码

飞渡科技数字孪生武汉CIM管理平台,基于自研数字孪生引擎,结合数字孪生、物联网IOT、云计算等信息技术,以城市数据资源融合共享为主线,打造感知、联结、计算、运用“四位一体”的城市大脑,赋能经济社会高质量可持续发展…

如何设置Java爬虫的异常处理?

在Java爬虫开发中,异常处理是确保爬虫稳定运行的关键环节。爬虫在执行过程中可能会遇到各种问题,如网络异常、目标网站的反爬机制、数据解析错误等。合理设置异常处理机制可以有效避免程序崩溃,并帮助开发者快速定位问题。以下是设置Java爬虫…

Oracle DBA 诊断及统计工具-1

ORACLE 查看表空间使用情况 1. 基本的表空间使用情况查询 可以通过组合查询 DBA_DATA_FILES 和 DBA_FREE_SPACE 视图来获取表空间的总大小、已使用空间和空闲空间等信息。不过要执行此查询,你需要具有 DBA 权限。 SELECT df.tablespace_name,-- 表空间总大小(MB)ROUND(SU…