游戏引擎学习第94天

news2025/2/11 2:22:13

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

回顾上周的渲染器工作

完成一款游戏的开发,完全不依赖任何库和引擎,这样我们能够全面掌握游戏的开发过程,确保没有任何细节被隐藏。我们将深入探索每一个环节,犹如拿着手电筒翻看床底,驱散那些潜伏的怪物,甚至是最可怕的怪物也不能让我们退缩。我们对这些挑战无所畏惧,因为我们自己也很强大,拥有锋利的牙齿和坚定的信心。

回顾一下我们上周的工作,基本上开始了渲染器的开发,并且完成了一些重要步骤。如大家所见,当前屏幕中有一个旋转的树形物体,虽然我们没有明确解释为什么会有这样的效果,但它是我们的测试地图。我们已经实现了纹理映射,并且通过双线性过滤使得物体的旋转和缩放表现得更加平滑。接下来,我们将继续深入讨论这些内容。

修正伽马校正

今天,我们将讨论一个平时可能不会提到的话题,但应某位观众的要求,我们决定讲解一下——这就是伽马校正(Gamma Correction)。实际上,有人曾在Q&A环节提到过这个问题,虽然没有直接提到伽马校正,但可以推测他们是在间接询问相关内容。至少我们知道有观众对此感兴趣,因此这次讨论不会完全没有意义。

伽马校正对一些人来说非常重要,尤其是在游戏开发中,很多开发者都非常关注这一点,因此今天我们将讲解一下。问题的核心在于,有人曾看到一段视频提到计算机上颜色混合的处理方式通常是不正确的,这位观众表示,因为我们是从零开始做游戏引擎的,所以有机会做得更好。需要澄清的是,提到“计算机上颜色混合不正确”,实际上是指图像编辑软件(如绘图程序)中的做法,而游戏本身的颜色混合通常是做得对的。事实上,游戏已经有很多年采用伽马校正的管线,大约自《半条命2》开始,像《最后的生还者》和《神秘海域2》这些游戏就已经使用了伽马校正的管线,以确保颜色混合的正确性。

所以,游戏在伽马校正方面实际上领先了许多艺术软件和绘图工具,后者有很多仍然没有正确实现伽马校正。特别是像Adobe这样的软件在很多地方并没有处理伽马校正,而视频中提到的正是这一问题。但是,对于现代3D游戏而言,伽马校正是非常重要的,尤其是在涉及光照和高动态范围成像(HDR)时,这些技术如今已经广泛应用于游戏中,因此正确的伽马校正变得尤为重要。

虽然我并不是伽马校正的专家,但会尽量解释伽马校正的基本概念,帮助大家理解,并鼓励大家做进一步的阅读和学习。接下来,我将演示如何在软件光栅化器中实现伽马校正,而在硬件版本中,这个过程实际上是非常简单的,虽然不能说是100%完美,但它非常接近理想的效果。

图表:显示器的伽马曲线

在这部分内容中,讨论了显示器的伽马曲线。首先,展示了一个图表,图表的x轴表示灰度级别,也就是像素值,范围是0到255。y轴表示亮度,单位是坎德拉每平方米(cd/m²),这就是显示器实际显示出来的亮度。

图表显示的是一个曲线,而不是一条直线,这意味着当向显示器发送不同的像素值时,显示器的亮度变化并不是线性的。在低端区域,像素值的微小变化(比如增加1或2)对亮度的提升几乎没有影响。而在高端区域,增加相同的像素值,亮度变化则会显著增大。

这也正是显示器的工作原理:它们并不是线性地响应输入的像素值。这个曲线反映了显示器的亮度响应特性,也就是“伽马曲线”。虽然有很多历史原因解释为什么显示器是这样工作的,但这些并不是今天讨论的重点。接下来,进入了新的编程日程,准备开始新的绘图工作。

例子:
在这里插入图片描述

import numpy as np
import matplotlib.pyplot as plt

# 定义多个伽马值
gamma_values = [1/25.0, 1/10.0, 1/5.0, 1/2.5, 1/1.5, 1, 1.5, 2.5, 5.0, 10.0, 25.0]  # 增加了更多的伽马值

# 生成灰度值,范围从0到255,共256个点
x = np.linspace(0, 255, 256)

# 创建绘图
plt.figure(figsize=(10, 6))

# 对每个伽马值绘制伽马校正曲线
for gamma in gamma_values:
    # 计算对应的亮度(伽马曲线)
    y = (x / 255) ** (1 / gamma) * 255  # 伽马校正公式
    
    # 使用格式化的字符串显示伽马值
    if gamma < 1:
        label = f'Gamma = 1/{1/gamma:.2f}' if gamma != 1 else 'Gamma = 1'
    else:
        label = f'Gamma = {gamma:.2f}' if gamma != 1 else 'Gamma = 1'

    plt.plot(x, y, label=label)

# 添加标题和标签
plt.title('Gamma Correction Curves for Different Gamma Values')
plt.xlabel('Input Pixel Value (0-255)')
plt.ylabel('Output Brightness (cd/m²)')

# 设置网格
plt.grid(True)

# 设置自定义的x轴和y轴刻度
plt.xticks(np.arange(0, 256, 20))  # 设置x轴刻度从0到255,每隔20显示一个刻度
plt.yticks(np.arange(0, 256, 20))  # 设置y轴刻度从0到255,每隔20显示一个刻度

# 添加图例
plt.legend()

# 显示图形
plt.show()

黑板:伽马

在图像处理和显示技术中,伽马校正曲线是用于调整亮度和对比度的关键工具。它展示了输入像素值与输出亮度之间的关系,通常以坎德拉每平方米(cd/m²)作为单位,这表示显示器的亮度。伽马校正曲线呈现出一种非线性的形式,通常低亮度区域(暗区)具有更多的分辨率,意味着即使小的输入变化也能引起显著的亮度变化;而高亮度区域(亮区)则相反,变化较小,控制较少。

这种设计的目的是为了适应人眼对比度的敏感度。人眼对暗部细节的敏感度比亮部更强,即当灰度值较暗时,微小的差异更容易被察觉。而在亮部,较大的灰度差异才能被区分。因此,通过这种非线性的伽马曲线,更多的控制被分配到暗区,从而使得暗部的细节调整更加精确。这样设计的目的是优化显示器在各个亮度区间的表现,特别是在低亮度范围内,增加暗部灰度的细腻度,而不会过度影响亮部区域的呈现。

黑板:为什么在乎?

关于为什么需要关注伽马校正曲线,问题的根本原因在于数学的应用。当进行颜色空间操作时,尤其是像颜色混合这种操作,涉及到的很多计算实际上是在亮度空间中进行的。举例来说,当进行双线性混合时,期望的行为是如果两个像素值的亮度分别为 25% 和 75%,混合结果应该是 50%。换句话说,如果两个像素值分别是 0% 和 100%,混合结果也应该是 50%。

然而,问题出现在我们执行这种操作时,并没有考虑到伽马校正。简单地在像素值空间(也就是未经伽马校正的颜色值)进行线性混合,会导致得到的结果并不符合预期。例如,当将黑色和白色(亮度分别为最低和最高)进行线性混合时,直接得到的像素值可能是中间值(128),然而这个值对应的亮度并不会是预期的“半亮度”,而是接近灰色,这与实际的亮度并不匹配。

这种情况的根本原因是没有在亮度的正确空间内进行计算。数学本身在像素空间中是正确的,但我们误解了这个空间的意义,认为它是亮度空间。实际上,这个空间与亮度无关,当映射回亮度时,结果就完全错误了。

因此,解决这一问题的关键是在进行颜色混合或其他相关操作时,必须考虑伽马校正,以确保计算的结果反映的是实际的亮度,而不是仅仅在像素值空间中进行的线性运算。

在这里插入图片描述

黑板:我们需要撤销或更准确地说,考虑到显示器伽马曲线的影响

为了使数学运算正确地进行颜色混合,必须考虑如何撤销伽马曲线的影响。然而,撤销这种曲线的影响实际上是非常困难的,因为显示器本身就输出了这个曲线,而用户已经购买了显示器,无法对显示器进行修改。因此,我们需要考虑到显示器输出的伽马曲线,并在此基础上进行补偿。

不仅如此,涉及到的还有艺术家和玩家。艺术家在制作图像时,实际上是在伽马曲线的空间中进行创作。通常,艺术家使用的是sRGB标准,这个标准实际上就是伽马曲线的一个变体。艺术家在创作时会根据显示器的伽马曲线调整图像,因此他们制作的图像本身已经是在这种空间下完成的。如果不对这些图像进行处理,它们会直接在显示器上正确显示。

然而,一旦进行任何需要线性计算的操作,比如双线性插值(在纹理映射时需要使用),就会遇到问题。在这些情况下,我们需要将颜色值从sRGB空间转换到线性空间进行计算,然后再将结果转换回sRGB空间,以确保输出的图像在显示器上正确显示。

这一过程就是伽马校正管道,通常由高质量的游戏引擎执行。例如,Unreal 4引擎就实现了这一过程。虽然Unity在早期可能没有做到这一点,但随着其光照和渲染技术的更新,现在很可能也已加入了这一功能。实际上,现代的图形硬件已经内建了这个功能,只需要开启即可。因此,任何了解图形学的开发者都会使用这一方法来确保正确的渲染结果。

黑板:在软件光栅化器中如何处理?

在处理图像时,需要对伽马曲线进行操作,以确保数学运算的正确性。为了实现这一点,首先需要找到一种方法,将伽马曲线“拉直”,也就是将其从伽马空间转换到线性空间进行运算。完成运算后,还需要将其转换回原来的伽马曲线,即将结果“压平”,以确保输出图像在显示器上正确显示。

这一过程可以理解为在进行图像处理时,先将图像从伽马空间转换到线性空间,在那里进行操作,再将结果转换回伽马空间。这种方法可以确保图像在进行颜色混合、纹理映射等运算时,得到准确的结果,避免因为伽马曲线导致的显示错误。

网络:sRGB定义1

sRGB曲线有明确的定义,可以查阅相关资料。sRGB的曲线定义了颜色空间的变换及其gamma编码过程,虽然在软件光栅化器中可能无法完全精确使用这个曲线,但可以通过查阅其定义来尝试找到一种近似方法。为了在软件中实现这种转换,通常需要使用查找表(lookup table)来映射gamma值。

如果选择不使用查找表,也可以假设显示器的gamma值为2.2,这种假设简化了处理,可以在软件中直接实现。这种处理方法比完全按照sRGB规范进行复杂的颜色空间变换要简单。

以下是相关的 URL 资源链接:
wiki:

  • SRGB
  • Gamma_correction

网络:伽马校正2

通常涉及到gamma校正的方程式会在相关资料中写出,尽管一些网站的数学展示方式可能不如预期,通常推荐通过访问其他更详细的资源来深入了解。可以参考一些具体的文献和研究,如 Linear-Space Lighting 这类资料,了解更准确的gamma校正实现方法。这些资料会提供更清晰的公式和理论,帮助理解如何处理和实现gamma校正渲染。

  • Linear-Space Lighting

网络:Filmic Games: ‘线性空间光照(即伽马)’,John Hable3

在讲解gamma校正渲染的过程中,展示了Uncharted游戏在GDC上的一场演讲,讨论了如何处理gamma曲线并应用于渲染。该演讲的重点是说明色彩值如何根据2.2次方来处理,同时提到了对应的反向操作,即通过将值的2.2次方反转得到对应的gamma校正。具体的公式包括2.2次方的处理和0.45的反转,后者是2.2的倒数。虽然讲解中提供了这些数学公式的背景,但没有直接给出所有公式的完整表达,尽管这份幻灯片展示了一些重要的信息。

为了更好地理解该主题,建议阅读演讲内容,并可能从中获得更多有价值的信息。相关演讲的幻灯片包含了关键数学内容,且演讲者使用了有效的讲解方式来帮助理解gamma校正的概念。

黑板:伽马曲线通常应该是什么样

在讨论如何避免完全模仿sRGB曲线时,提到了一种简化方法,即使用一个近似的曲线来代替完整的gamma校正。具体来说,可以通过将色彩值(如红色、绿色或蓝色)提高到2.2次方来模拟显示器的输出。虽然2.2次方的操作本身有点复杂,特别是在软件光栅化器中实现时,使用查找表可能会是一个选择,但为了避免性能开销,尝试使用2次方代替2.2次方。事实上,2次方与2.2次方在0到1的范围内非常接近,尤其在颜色空间中,这是一个可以接受的近似。因此,可以使用2次方而不是2.2次方来简化计算,避免频繁的查找表操作,从而提高效率。

黑板:计算这个伽马曲线

决定使用2次方而不是2.2次方的原因是,平方运算非常简单,只需将数值与自身相乘即可,这在计算上非常高效。而且,反向操作,即计算平方根,虽然过去比较复杂,但如今处理器对平方根的计算已经进行了加速,尤其是像Intel处理器,它们在计算平方根时表现非常高效。平方和平方根的运算广泛应用于向量归一化等计算任务,因此现代处理器对此类运算进行了优化。这样,平方和平方根的计算就变得非常快速。虽然如果需要更精确地逼近2.2次方,可以考虑使用更复杂的数值方法,但在大多数情况下,使用2次方已经足够。

黑板:构想帧缓存管线

在渲染过程中,需要将颜色从sRGB空间转换到线性空间,然后再进行操作。转换的过程包括将sRGB转为线性空间(sRGB to linear),并在操作完成后再将结果转换回sRGB(linear to sRGB)。有两种方式来处理这个管线问题,适用于不同的场景。

一种方式是将帧缓冲区设置为线性空间,这意味着图像内容在被绘制到屏幕上之前,会先进行线性空间的处理,然后才做颜色的转化。另一种方式是将帧缓冲区设置为sRGB空间,在绘制时先将图像从sRGB空间转换到线性空间进行处理,最后在显示前再转换回sRGB。

对于软件渲染器而言,因为其主要操作基于已经完成的位图图像,可能不会涉及太多的3D构建和复杂的渲染过程,因此线性帧缓冲区的优势并不显著。实际操作中,如果选择将图像处理后直接转换为线性空间,并在绘制过程中避免频繁的转换操作,最后再将结果转换回sRGB以显示,可以减少重复的转换过程,节省性能开销。

黑板:关心带宽

在处理颜色时,假设大多数情况下需要关注带宽。为了存储线性帧缓冲区,可能需要每个像素每个通道16位,而不是当前使用的8位每通道,因为8位不足以存储线性色彩而不出现带状伪影。这是因为8位足以避免在曲线的某些区域产生带状伪影,但如果将其拉直,在低位区段的分辨率不足以防止带状伪影。因此,需要更多的比特精度来防止带状伪影,通常需要16位。

然而,使用16位每通道将导致带宽需求翻倍,这对于当前的设计是不希望的。虽然有可能通过结构化光栅化器来避免带宽问题,但并不认为需要线性帧缓冲区,因此更倾向于在处理过程中通过一次转换(在绘制时进行sRGB到线性的转换,并在最终输出时进行线性到sRGB的转换)来节省带宽和缓存开销,避免频繁的转换操作。

这种方法不仅能节省带宽,还能在一定程度上减轻缓存解决方案的负担,并且执行这一步骤的开销相对较小。总体来说,采取这种方式应该能够有效地减少资源开销。

在这里插入图片描述

网络:‘Uncharted 2: HDR Lighting’ 幻灯片,John Hable4

这段内容提到了一些演示文稿的内容,建议查看更完整的幻灯片,因为幻灯片中包含了更多的例子和详细的信息。原始幻灯片只是展示了要点,而完整版本包含了更多关于如何存储和输出的示例和细节。通过浏览这些内容,可以更好地理解相关概念,熟悉操作流程。
神秘海域2:HDR Lighting PPT:

  • Linear-Space Lighting

黑板:我们想要模拟显示器的行为

在这一过程中,目标是模仿显示器的行为,以便将颜色值转化为均匀的亮度。首先,要将颜色值从sRGB空间转换到线性空间。这可以通过对颜色值进行2.2次方的运算(假设红色值为2.2次方)来完成。之后,进行所有必要的数学运算。最后,为了将值转换回sRGB空间,需要对其进行平方根运算。完成这些操作后,将结果写入最终输出。这是一个相对简单的过程,通过这种方式可以实现从sRGB到线性空间的转换以及反向转换。

实现这个sRGB Linear

为了实现从sRGB到线性空间的转换,可以通过创建一个函数来处理颜色值的转换。例如,假设已经提取了颜色值(如RGBA),可以通过一个函数将这些值从sRGB空间转换到线性空间(或线性亮度空间)。具体来说,函数将对每个颜色通道进行gamma校正,将其从sRGB值转换为线性空间的值。由于颜色值通常是0到255的范围,因此需要将其归一化到0到1之间,进行转换后再进行其他运算。

在完成数学运算后,再将计算结果转换回sRGB空间。这时,需要再次将颜色值从线性空间转换回sRGB空间,且在写回到输出时要进行校正处理。为了确保正确的转换,所有的操作需要确保在归一化的0到1的空间内进行,而不是在0到255的范围内。

这些转换操作的核心是从sRGB到线性空间的转换,以及从线性空间返回sRGB的转换。为了避免误差,所有的输入和输出值都需要在适当的空间内进行转换和处理。需要实现相应的转换函数,以便在每个操作步骤中对颜色值进行正确的处理。
在这里插入图片描述

创建SRGB255ToLinear1和Linear1ToSRGB255函数

在这个过程中,我们需要实现一个函数,将颜色值从sRGB转换为线性亮度空间,并且再从线性亮度空间转换回sRGB空间。具体来说,目标是模仿显示器如何处理色彩曲线,即将颜色值平方处理。Alpha值不会受到影响,因为它并不是亮度值,而是表示混合度的百分比,通常是线性编码的。

在进行这些操作时,首先要将输入的颜色值从sRGB空间转换为线性空间,这个转换会涉及到将每个色彩通道的值除以255,使其变成0到1之间的数值。然后,色彩值将被平方,以模拟显示器的处理方式。当我们需要将这些值转换回sRGB空间时,就要对结果进行平方根运算,将它们从线性空间转换回来。

对于Alpha值,它会直接传递,无需处理。最后,整个过程完成后,结果会被返回。

在实现时,需要确保在平方之前将色彩值从0-255范围转化为0-1范围,这个步骤会在代码中集成,确保计算正确。

在这里插入图片描述

在这里插入图片描述

编译,查看游戏中的效果,仔细检查

在进行修改后,可能会出现一些问题,因此需要仔细检查代码,确保所有操作都符合预期。以下是整个过程的总结:

  1. 输入颜色的处理

    • 颜色值以0到255的范围传入,需要先转换为线性空间。
    • 在处理颜色值时,必须确保Alpha通道不被修改。原本不打算修改Alpha,但代码中不小心对Alpha进行了平方操作,已修复此问题。
    • 对颜色通道(红色、绿色、蓝色)执行平方操作,确保它们转换到线性空间,且所有操作都发生在0到1的范围内。
  2. 255值的处理

    • 在转换和运算过程中,避免将255值硬编码到计算中。任何需要恢复到0到255范围的值应在操作完成后再进行缩放。
    • 在计算时,采用了线性空间操作,最后通过乘以255将值转换回0到255的范围。
  3. 纹理采样与颜色处理

    • 通过纹理采样获取纹理的texel值(A、B、C、D),并将这些值从0到255的空间映射到线性空间。
    • 对纹理的值进行双线性插值,确保计算得到的颜色值是正确的。
  4. Alpha混合计算

    • 根据源Alpha和目标Alpha计算反向Alpha(逆Alpha),并使用这种逆Alpha来进行混合操作。
    • 对每个颜色通道(红色、绿色、蓝色)应用源Alpha与目标Alpha进行混合,得到最终的颜色值。
  5. 最终结果的回转换

    • 在混合完成后,将线性空间中的颜色值转换回sRGB空间。通过乘以255恢复颜色值到0到255的范围。
    • 将最终的颜色值和Alpha通道的值一起打包,形成最终的像素值。
  6. 问题解决

    • 在整个过程中,确保不对Alpha进行不必要的操作,避免将255值直接硬编码进计算中。
    • 对所有的操作进行细致检查,确保没有多余的错误,并确保颜色值正确处理。

在检查过程中,遇到了一些小的细节问题,比如不小心将Alpha通道处理了,或者在恢复255值时没有正确地处理。已逐一解决这些问题,并确认代码逻辑的正确性。
在这里插入图片描述

在这里插入图片描述

设置断点并单步调试

在调试过程中,使用了断点来查看每个步骤的计算结果,并通过观察变量值来定位问题。以下是调试的详细步骤和发现:

  1. Texel值检查

    • 检查到读取的Texel值已正确地在0到1的范围内,这是预期的行为。Texel A、B、C、D的值都在正确范围内,说明纹理采样操作没有问题。
  2. 源Alpha值(RSA)

    • 在源Alpha值(RSA)的计算中,发现源Alpha值为1,这看起来有些奇怪。原本预期应该在某些情况下,特别是在边缘像素上,源Alpha值应为0。
    • 进一步调试发现,虽然最开始的像素应具有Alpha值0,但可能是因为正在处理的并不是第一个像素,而是从其他像素开始的。最终发现第一个像素确实是Alpha为0的。
  3. 帧缓冲读取

    • 在读取帧缓冲中的目标像素时,发现目标像素的颜色为紫色,这不应该发生,因为此时不应在无效的区域绘制。
    • 这个紫色的目标颜色是由于没有填充地面补丁,导致绘制过程中产生了无效的像素颜色。对于这一点,初步认为是正常的,之后会填充地面补丁。
  4. sRGB到线性空间的转换

    • 将Texel值从sRGB空间转换为线性空间后,源Alpha值的反转(逆Alpha)是1,这应该使得最终计算的颜色值与目标像素值一致。
    • 结果显示,混合后的值和目标像素值完全一致,这是正确的,说明混合操作没有问题。
  5. 线性空间到sRGB的回转换

    • 在回转换时,发现最终结果没有转换到预期的0到255的范围。预期是在这个步骤中将颜色值转换到正确的范围,但实际没有发生。
    • 经过分析,发现问题出在没有正确地执行该转换,导致值没有正确更新。
  6. 调试结论

    • 整个调试过程中,除了sRGB到线性空间的回转换步骤有问题外,其他步骤的操作如Texel读取、Alpha值计算和混合过程都正常。
    • 需要检查线性到sRGB的回转换部分,修复这一问题,确保颜色值在转换后能够正确地恢复到预期的范围。

通过这些步骤,发现了转换过程中出现的错误,并确认了其他操作的正确性。

顿悟时刻:Linear1ToSRGB255在开方之前乘以了255

在调试过程中,发现了一个错误,问题出在对颜色值的处理顺序上。在原本的逻辑中,预期应该先对颜色值进行平方根运算,再将其乘以255。然而,实际上代码中错误地先将颜色值乘以了255,导致颜色值的计算结果不正确。

这个问题可能是由于在输入时不小心将乘以25的操作写入了代码,而忽略了应该先进行平方根运算的步骤。通过识别这一错误,可以修正计算顺序,重新调整颜色值的处理流程,从而避免出现不符合预期的结果。

这个错误可能是唯一的问题,因此通过修正这个部分,应该能够解决当前的显示问题。
在这里插入图片描述

在这里插入图片描述

享受我们在伽马校正下呈现的树木

在调试过程中,完成了对伽玛校正的实现,树木的渲染现在已经能够正确地进行伽玛校正,并呈现出期望的效果。然而,目前还没有合适的测试方法来验证伽玛校正是否完全正确,因此虽然管道已正确实现,仍然无法确定其是否完全符合预期。尽管如此,经过修正后,至少能够确认流程没有问题,并且在正确的空间内进行数学运算并转换。

对于2D精灵合成,伽玛校正的影响并不像在3D渲染中那样明显。对于大多数2D合成,许多人并未看到伽玛校正带来显著的区别,因此对效果的期望并不高。在进行颜色混合时,可能会看到不同,但这并不是常见的场景。因此,即便没有完美实现,影响也许并不大。

为了测试伽玛校正的效果,可以加入一个切换按钮,允许开启或关闭伽玛校正,看看是否会产生显著的变化,但总体来说,期望并不高。不过,伽玛校正对更复杂的效果是有益的,尤其是在3D渲染中,了解这一点是很重要的。现在,伽玛校正已成功集成到系统中,后续可以在优化时进一步处理,尽管现在计算平方根的性能影响较小,不会成为瓶颈。

总结来说,主要的工作已经完成,只需做一些细微调整,伽玛校正就能正常工作,接下来的优化步骤可以进一步提升性能。

开始使用Color值来为纹理图添加色调

决定进行一些调整,计划使用传入的颜色值,并将其实际应用到绘制的矩形上。目标是在调用绘制函数时,能够利用传入的颜色值进行调节。具体的做法是将传入的颜色值与读取的颜色值相乘,从而对所绘制的内容进行着色调整。例如,可以通过这种方式调节颜色,实现颜色的调制效果。

黑板:色调

如果有一个 Texel 的 r、g 和 b 值,可以通过引入系数来调节颜色,例如使用 CR、CG 和 CB 作为系数来乘以这些值。如果这些系数在0到1之间,就可以控制从纹理图中读取多少蓝色、绿色和红色的分量。这样,就能灵活地调整颜色的渲染效果。

实现色调

可以通过简单的方式来实现对纹理图进行颜色调制。方法是将输入的颜色值与从纹理图中读取的颜色值相乘,从而达到调节颜色的效果。由于在现有的处理流程中,已经在计算过程中对颜色进行了预乘处理,因此只需要在读取 Texel 颜色值后,将其与输入的颜色值相乘,就能实现颜色调节或着色。这个过程非常直接且容易实现。

实时色调

可以通过调整颜色来实现纹理的调色和透明度变化。将颜色从纹理中读取后,可以通过修改其 RGB 值来调节颜色,甚至通过增加频率,使效果更加明显。通过这种方式,可以在不影响性能的情况下实现更多的艺术效果,尤其适用于特殊效果和精灵调色等场景。例如,可以通过简单的调整 RGB 值或透明度来实现子弹或其他特殊效果的颜色变化。这种方式也能很好地与硬件兼容,如 OpenGL,能够高效地处理这类调色需求。
在这里插入图片描述

正确地预乘alpha

在处理透明度时,需要确保正确地进行预乘 Alpha 操作。通过将颜色值与 Alpha 值相乘,可以确保正确的混合效果,尤其是在涉及到透明度变化时。原本的代码没有对颜色进行预乘 Alpha,因此在合成时会出现问题。修正后,将颜色与 Alpha 进行预乘,这样可以正确地计算出混合结果,使得透明度从 0 到 1 的变化过程更加平滑且准确。这种处理方式保证了色彩与透明度的正确结合,尤其适用于精灵和其他需要透明度变化的效果。
在这里插入图片描述

黑板:预乘alpha

在处理透明度时,使用了预乘 Alpha(Premultiplied Alpha)的概念。预乘 Alpha 是在进行线性混合时必须使用的,因为只有预乘 Alpha 才能确保正确的混合效果,而非预乘的 Alpha(即普通 Alpha)则无法正常工作。具体来说,预乘 Alpha 的计算方式是将源颜色与其 Alpha 值相乘,而目标颜色则按标准的线性混合公式进行处理。混合公式为 ( 1 − α d ) ∗ D + α s ∗ S (1 - α_d) * D + α_s * S (1αd)D+αsS,其中 S 已经被预乘 Alpha。出现的问题是在处理颜色时,第二个颜色未进行 Alpha 预乘,导致混合结果不正确。修正的方法是确保在计算源颜色时,将其 Alpha 值与颜色值相乘,确保所有颜色值在管线中都经过预乘处理,这样就能正确计算混合效果。

问:使用2而不是2.2进行伽马校正会产生什么视觉伪影?

使用 gamma 值 2.0 而不是 2.2 并没有带来可视的伪影。理论上,2.0 和 2.2 的 gamma 曲线略有不同,其中 2.0 曲线比 2.2 曲线略平缓。两条曲线在亮度 1 时相交,但如果使用 2.0 而不是 2.2,会导致颜色比正常值稍微暗一些,因为在将颜色转换为线性空间时,2.0 曲线没有完全恢复到线性空间,只有接近线性空间。尽管如此,这种差异非常微小,通常情况下甚至难以察觉,因此并不会造成显著的视觉问题。
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

问:还需要做加法色调以实现完整的表现力

需要进行加法颜色调色,以实现更全面的表现力。加法调色意味着可以在原本没有红色的地方加入红色。对此,提出者对加法调色的想法表示犹豫,并表示会再考虑是否进行实现。

我有计算机科学背景,但落后了两个月。只有在不试图输入时,跟进代码才更容易。 如果我落后了很远,停下来只是看视频对理解有多大影响?有时你移动代码时,我必须看2到3遍

如果只是观看而不编写代码,可能会对理解造成一些影响。虽然观看内容有助于理解,但不动手实践的话,学习的效果会大打折扣。编写代码是帮助记忆和深入理解概念的关键步骤。如果感觉落后,不必强迫自己赶进度,可以按自己的节奏学习,也不需要每次都保持最新进度。

问:是否值得只对alpha不等于0或1的纹素进行伽马校正?

对于具有非零或非一 alpha 值的 Texels,实际上应该对这些 Texels 进行 gamma 校正,特别是在做双线性混合时。因为双线性混合要求所有的 Texels 都经过 gamma 校正,才能保证效果正常。所以,目前在处理时会确保这些 Texels 都进行 gamma 校正。虽然如果通过性能分析发现对速度有显著影响,可以考虑让它在编译时选择是否启用 gamma 校正,但目前它还是被保留在系统中。

问:我们可以在sRGB空间进行计算吗?

如果在 sRGB 空间中进行数学运算,虽然是可行的,但如果执行线性混合,它们会变成一堆幂函数。因为在线性混合中,混合的定义就变成了幂函数的形式。

黑板:在sRGB空间内进行Lerp

当前的线性插值公式是 ( 1 − t ) ⋅ A + t ⋅ B (1 - t) \cdot A + t \cdot B (1t)A+tB 。如果想要在 sRGB 空间中进行线性插值,需要使用更复杂的公式: ( 1 − T ) ⋅ A 2 + T ⋅ B 2 \sqrt{(1 - T) \cdot A^2 + T \cdot B^2} (1T)A2+TB2 。尽管可以按照这种方式编写插值,但这样每次都需要平方 A 和 B,计算过程变得更加低效。最优的做法是在顶部进行一次平方处理,最后再进行一次平方根运算。这样可以减少每次计算时的开销。

有趣的是,这个过程有点像一个三角形,插值公式与三角形的边长类似。通过勾股定理可以得到插值结果。虽然这样看起来挺有趣,但不确定是否会有更多深层的影响。对于这个方法的改进,也可以考虑是否能够仅通过调整 T 值来实现,而不需要对 A 或 B 进行平方。

黑板:这个问答已经变成了一次探讨

讨论的核心是关于平方根插值的数学推导过程。首先,假设 F ( T ) F(T) F(T) 表示插值函数,接着对其进行平方操作,得到 F ( T ) 2 = ( 1 − T ) ⋅ A 2 + T ⋅ B 2 F(T)^2 = (1 - T) \cdot A^2 + T \cdot B^2 F(T)2=(1T)A2+TB2。然后,进一步展开表达式,得到 F ( T ) = A 2 − T ⋅ A 2 + T ⋅ B 2 F(T) = A^2 - T \cdot A^2 + T \cdot B^2 F(T)=A2TA2+TB2,这与标准的插值形式相似。

然而,问题出在出现了交叉项,使得无法直接简化。即使尝试将 T T T 移出,也没有有效的办法将其提取出来。虽然开始时考虑通过平方来去除根号,但即使这样, T T T 仍然无法像预期那样被提取出来。

总结来看,尽管通过平方操作和展开,尝试简化表达式,但没有找到有效的方法使得 T T T 可以单独提取出来,因此这一过程仍然没有实质性的进展。

问:你什么时候开始为渲染实现OpenGL?

实现 OpenGL 渲染的计划是在完成软件渲染器的优化后进行,OpenGL 主要是为了提升性能。但考虑到实现 OpenGL 并不会带来太多学习内容,因此当前并不专注于这方面的工作。

问:10_10_10_2格式(硬件上也有)是不是足够避免带状噪声?

使用 10-10-10-2 格式是否足以避免色带问题取决于最终的帧缓冲区处理方式。如果帧缓冲区使用目标 alpha 进行透明度处理(例如 2 位 alpha),那么可以考虑使用 10-10-10-2 或 11-11-11-10 格式,且不在帧缓冲区中使用 alpha。这可能有助于避免色带问题,但也可能会限制未来的扩展,因为这意味着无法处理目标 alpha。因此,这种方法可能不太理想,需谨慎考虑。

问:那使用2.2伽马曲线的意义是什么?为什么不直接使用2作为伽马?

使用 2.2 作为 gamma 值而不是 2 的原因在于,2.2 在色彩准确性上提供了更好的表现。虽然在紧急情况下可以使用 2 来简化计算,但如果追求 100% 的准确性,2.2 是更理想的选择。在硬件实现中,很多时候会使用色彩查找表(LUT)来准确处理 sRGB 配方,这避免了近似值带来的误差。不过,2 作为一种优化方式是被广泛接受的,很多场景下认为它已经足够接近理想值,因此能提供更好的性能。

问:在每次混合时,反复在线性和近似伽马空间之间转换,是否会加重误差?

在进行每次混合时,如果反复在线性空间和近似的 gamma 空间之间转换,会不会积累误差呢?答案是有时候会,也有时候不会。误差的积累取决于具体情况。很多情况下,屏幕上的结果只是最终像素的值,并没有太多透明度混合的操作。只有在处理半透明物体或边缘重叠的地方,才有可能看到误差的累积。

不过,即使在这些情况下,误差的累积也不会太显著,除非在很多边缘重叠的地方,每次混合都会有一点舍入误差。对于需要高精度的场景,比如做摄影级别的渲染,确实会选择在浮点数精度下进行计算,以避免这类误差。因为在目前的情况下,所做的工作并不需要每一个细节都达到最高精度,所以这些误差的累积并不会对最终效果造成太大影响。

问:所有显示器都使用相同的2.2伽马曲线吗?

并不是所有的显示器都使用相同的 2.2 曲线,实际上,这也是为什么需要进行显示器色彩校准的原因。并非所有显示器都采用相同的设置,例如对比度的不同,或是其他调整方式,都会导致不同的结果。有时,游戏会要求用户调整一个旋钮,直到屏幕上的灰色颜色看起来一致,这其实是在尝试找出显示器的 gamma 曲线。

因此,没有完美的解决方案,现实中我们无法获取足够的信息来做到完美的校准。使用 2.2 的 gamma 曲线是一种合理的妥协方案,因为它比完全不进行 gamma 调整要好,而且对于大多数情况下,2.2 的近似误差并不会显得太大。当然,这只是一种假设,如果要深入了解,可能需要向更专业的人士请教。

问:将颜色值升到2而不是2.2真的能节省多少处理能力?

将颜色值提高到 2 而不是 2.2 确实能节省很多处理能力。原因在于,平方操作相比于提升到 2.2 次方,计算量要小得多。平方操作只需要进行一次乘法,而将数值提升到 2.2 次方涉及更多的计算步骤。虽然具体的指令数需要具体计算,但可以预见,平方操作可能只需要一条指令,而提高到 2.2 次方可能会需要多条指令,可能高达 5 条甚至更多。

当考虑到每个像素都需要进行这种计算时,这个差距变得非常显著。例如,如果每个像素的处理成本增加了 6 倍,那么总体的性能开销就会大大增加。因此,使用 2 而不是 2.2 是非常有效的优化方式。

另外,2.2 次方操作不仅仅是提高到 2.2 次方,还需要进行反向的 1/2.2 次方操作来还原值,这使得计算更加复杂。所以,即便是通过某种近似算法来加速 2.2 次方的计算,也很难达到单条指令的速度。因此,在需要优化性能的情况下,通常更倾向于使用 2 而不是 2.2。

最终,优化策略往往会取决于具体实现和计算环境,但为了性能考虑,简化计算操作(如使用平方)通常会带来明显的优势。

今天就到此为止

今天讲解了gamma相关的内容,虽然在目前的游戏项目中gamma的影响不大,但对于3D渲染和其他可能涉及gamma的情况,了解它是非常有益的。建议大家多做一些相关的阅读,尤其是对于做图形编程的人来说,gamma的知识是必须掌握的。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2296075.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

win32汇编环境,结构体的使用示例二

;运行效果 ;win32汇编环境,结构体的使用示例二 ;举例说明结构体的定义&#xff0c;如何访问其中的成员&#xff0c;使用assume指令指向某个结构体&#xff0c;计算结构数组所需的偏移量得到某个成员值等 ;直接抄进RadAsm可编译运行。重要部分加备注。 ;下面为asm文件 ;>>…

DeepSeek从入门到精通教程PDF清华大学出版

DeepSeek爆火以来&#xff0c;各种应用方式层出不穷&#xff0c;对于很多人来说&#xff0c;还是特别模糊&#xff0c;有种雾里看花水中望月的感觉。 最近&#xff0c;清华大学新闻与传播学院新媒体研究中心&#xff0c;推出了一篇DeepSeek的使用教程&#xff0c;从最基础的是…

【PDF提取内容】如何批量提取PDF里面的文字内容,把内容到处表格或者批量给PDF文件改名,基于C++的实现方案和步骤

以下分别介绍基于 C 批量提取 PDF 里文字内容并导出到表格&#xff0c;以及批量给 PDF 文件改名的实现方案、步骤和应用场景。 批量提取 PDF 文字内容并导出到表格 应用场景 文档数据整理&#xff1a;在处理大量学术论文、报告等 PDF 文档时&#xff0c;需要提取其中的关键信…

SSA-TCN麻雀算法优化时间卷积神经网络时间序列预测未来Matlab实现

SSA-TCN麻雀算法优化时间卷积神经网络时间序列预测未来Matlab实现 目录 SSA-TCN麻雀算法优化时间卷积神经网络时间序列预测未来Matlab实现预测效果基本介绍程序设计参考资料 预测效果 基本介绍 1.Matlab实现SSA-TCN麻雀算法优化时间卷积神经网络时间序列预测未来&#xff08;优…

大模型推理——MLA实现方案

1.整体流程 先上一张图来整体理解下MLA的计算过程 2.实现代码 import math import torch import torch.nn as nn# rms归一化 class RMSNorm(nn.Module):""""""def __init__(self, hidden_size, eps1e-6):super().__init__()self.weight nn.Pa…

大数据项目2:基于hadoop的电影推荐和分析系统设计和实现

前言 大数据项目源码资料说明&#xff1a; 大数据项目资料来自我多年工作中的开发积累与沉淀。 我分享的每个项目都有完整代码、数据、文档、效果图、部署文档及讲解视频。 可用于毕设、课设、学习、工作或者二次开发等&#xff0c;极大提升效率&#xff01; 1、项目目标 本…

Windows逆向工程入门之汇编环境搭建

公开视频 -> 链接点击跳转公开课程博客首页 -> ​​​链接点击跳转博客主页 Visual Studio逆向工程配置 基础环境搭建 Visual Studio 官方下载地址安装配置选项(后期可随时通过VS调整) 使用C的桌面开发 拓展可选选项 MASM汇编框架 配置MASM汇编项目 创建新项目 选择空…

gc buffer busy acquire导致的重大数据库性能故障

&#x1f4e2;&#x1f4e2;&#x1f4e2;&#x1f4e3;&#x1f4e3;&#x1f4e3; 作者&#xff1a;IT邦德 中国DBA联盟(ACDU)成员&#xff0c;10余年DBA工作经验 Oracle、PostgreSQL ACE CSDN博客专家及B站知名UP主&#xff0c;全网粉丝10万 擅长主流Oracle、MySQL、PG、高斯…

Formily 如何进行表单验证

&#x1f90d; 前端开发工程师、技术日更博主、已过CET6 &#x1f368; 阿珊和她的猫_CSDN博客专家、23年度博客之星前端领域TOP1 &#x1f560; 牛客高级专题作者、打造专栏《前端面试必备》 、《2024面试高频手撕题》 &#x1f35a; 蓝桥云课签约作者、上架课程《Vue.js 和 E…

安宝特方案 | AR眼镜:远程医疗的“时空折叠者”,如何为生命争夺每一分钟?

行业痛点&#xff1a;当“千里求医”遇上“资源鸿沟” 20世纪50年代&#xff0c;远程会诊的诞生曾让医疗界为之一振——患者不必跨越山河&#xff0c;专家无需舟车劳顿&#xff0c;一根电话线、一张传真纸便能架起问诊的桥梁。然而&#xff0c;传统远程医疗的局限也日益凸显&a…

使用git commit时‘“node“‘ 不是内部或外部命令,也不是可运行的程序

第一种&#xff1a; 使用git commit -m "xxx"时会报错&#xff0c;我看网上的方法是在命令行后面添加--no-verify&#xff1a;git commit -m "主题更新" --no-verify&#xff0c;但是不可能每次都添加。 最后解决办法是&#xff1a;使用git config --lis…

nodejs - vue 视频切片上传,本地正常,线上环境导致磁盘爆满bug

nodejs 视频切片上传&#xff0c;本地正常&#xff0c;线上环境导致磁盘爆满bug 原因&#xff1a; 然后在每隔一分钟执行du -sh ls &#xff0c;发现文件变得越来越大&#xff0c;即文件下的mp4文件越来越大 最后导致磁盘直接爆满 排查原因 1、尝试将m3u8文件夹下的所有视…

【MySQL — 数据库基础】深入解析MySQL的聚合查询

1. 聚合查询 1.1 聚合函数 函数说明COUNT ( [DISTINCT] expr)返回查询到的数据的数量( 行数 )SUM ( [DISTINCT] expr)返回查询到的数据的总和&#xff0c;不是数字没有意义AVG ( [DISTINCT] expr)返回查询到的数据的平均值&#xff0c;不是数字没有意义MAX( [DISTINCT] expr)…

windows平台本地部署DeepSeek大模型+Open WebUI网页界面(可以离线使用)

环境准备: 确定部署方案请参考:DeepSeek-R1系列(1.5b/7b/8b/32b/70b/761b)大模型部署需要什么硬件条件-CSDN博客 根据本人电脑配置:windows11 + i9-13900HX+RTX4060+DDR5 5600 32G内存 确定部署方案:DeepSeek-R1:7b + Ollama + Open WebUI 1. 安装 Ollama Ollama 是一…

港中文腾讯提出可穿戴3D资产生成方法BAG,可自动生成服装和配饰等3D资产如,并适应特定的人体模型。

今天给大家介绍一种名为BAG&#xff08;Body-Aligned 3D Wearable Asset Generation&#xff09;的新方法&#xff0c;可以自动生成可穿戴的3D资产&#xff0c;如服装和配饰&#xff0c;以适应特定的人体模型。BAG方法通过构建一个多视图图像扩散模型&#xff0c;生成与人体对齐…

数据库 绪论

目录 数据库基本概念 一.基本概念 1.信息 2.数据 3.数据库&#xff08;DB&#xff09; 4.数据库管理系统&#xff08;DBMS&#xff09; 5.数据库系统&#xff08;DBS&#xff09; 二.数据管理技术的发展 1.人工管理阶段 2.文件系统阶段 3.数据库系统阶段 4.数据库管…

跨越边界,大模型如何助推科技与社会的完美结合?

点击蓝字 关注我们 AI TIME欢迎每一位AI爱好者的加入&#xff01; 概述 2024年&#xff0c;大模型技术已成为人工智能领域的焦点。这不仅仅是一项技术进步&#xff0c;更是一次可能深刻影响社会发展方方面面的变革。大模型的交叉能否推动技术与社会的真正融合&#xff1f;2025年…

kafka生产端之架构及工作原理

文章目录 整体架构元数据更新 整体架构 消息在真正发往Kafka之前&#xff0c;有可能需要经历拦截器&#xff08;Interceptor&#xff09;、序列化器&#xff08;Serializer&#xff09;和分区器&#xff08;Partitioner&#xff09;等一系列的作用&#xff0c;那么在此之后又会…

在 Windows 上使用 ZIP 包安装 MySQL 的详细步骤

以下是使用官方 ZIP 包在 Windows 上安装 MySQL 的详细步骤&#xff0c;确保能通过 mysql -uroot -p 成功连接。 步骤 1&#xff1a;下载 MySQL ZIP 包 访问 MySQL 官方下载页面&#xff1a; https://dev.mysql.com/downloads/mysql/选择 Windows (x86, 64-bit), ZIP Archive&…

记录 | WPF创建和基本的页面布局

目录 前言一、创建新项目注意注意点1注意点2 解决方案名称和项目名称 二、布局2.1 Grid2.1.1 RowDefinitions 行分割2.1.2 Row & Column 行列定位区分 2.1.3 ColumnDefinitions 列分割 2.2 StackPanel2.2.1 Orientation 修改方向 三、模板水平布局【Grid中套StackPanel】中…