通用-–-线性色彩空间
汉化标题:通用-–-线性色彩空间
原文标题:General-–-Linear-color-space
汉化更新时间:2026-01-14 13:58:27 +0800
英文原文最新更新时间:2024-05-14 16:46:41 -0400
CSP 中的线性色彩空间是 0.2.3 版本引入的一个新功能,它允许 WeatherFX 样式改变 Assetto Corsa 处理着色的方式。默认情况下,该功能处于禁用状态,因此旧的 WeatherFX 样式仍然保持相同的外观。
它是什么?
在计算机图形学中,有两种更流行的方式来存储和处理图像数据(即色彩空间)。一种是线性色彩空间,其中存储的亮度值与显示像素的最终亮度成线性比例关系(因此,如果您使用照度计测量屏幕,RGB=127 的亮度将是 RGB=255 的一半)。但问题在于我们的感知是非线性的:当我们打开第二盏灯时,我们不会觉得亮度增加了两倍,而在直射阳光下我们几乎注意不到手电筒的光斑,但在夜间它甚至可能让人眼花缭乱。
正因为如此,存在一种称为 sRGB(或 gamma 色彩空间)的替代色彩空间,它更接近人类感知光线的方式。以这种格式存储数据可以更高效地利用空间,并有助于消除暗区的色带(在 gamma 色彩空间中,RGB=2 与 RGB=1 的区别不大,而在线性色彩空间中差异很大)。因此,这几乎是所有计算机图形学数据的主要标准。JPEG 和 PNG 以 sRGB 格式存储数据,Windows 桌面合成器期望图像返回 sRGB 格式的数据,然后将该 sRGB 数据作为 sRGB 传递给显示器。
遗憾的是,正因如此,游戏开发人员很容易犯这个错误,即实际的 3D 着色也是在 sRGB 中完成的。但是,许多用于 3D 渲染的模型如果不在线性色彩空间中完成,就会惨遭失败。即使是像"遍历附近的光源并求和它们对给定像素的贡献"这样简单的事情:虽然在线性色彩空间中工作时这是一个完全合理的做法,并且它会产生手电筒光斑在直射阳光下逐渐消失的效果,但如果在 gamma 空间中完成,它最终看起来会很糟糕。渲染器的几乎每个其他方面也是如此,包括反射(您是否注意到现实生活中,被照亮的黑色汽车似乎比白色汽车更反光?)、雾(如果在线性色彩空间中使用正确的散射完成,被照亮的物体应该比暗物体可见得更远)、灯光与表面的交互方式等等。
这就是新选项所做的事情。一个简单的配置行,但在内部它切换到一套全新的着色器,并升级了 Assetto Corsa 渲染的许多部分。这就是为什么它取决于 WeatherFX 样式来决定是否激活它:在此之前,样式经过精心调整,以试图解决 AC 中任何与着色相关的计算完全不正确的问题,通过提供各种虚假值,甚至对于像太阳/环境光关系这样基本的东西也是如此。现在有了更准确的渲染器,大多数这些技巧都不再需要了。
当然,不仅仅是 WeatherFX 样式经过微调来解决这个问题的,许多例如材质或 PP 滤镜也是这样配置的。但我预计影响会不那么严重:新的着色器集在后台进行一些转换,以试图保持外观相似(例如,fresnelMaxLevel 会被平方);当然,不可能完美地匹配事物:如果不算其他,现在黑色汽车在 AC 中也会显得更反光。
当前状态
CSP v0.2.3 应该几乎所有的功能都已适应新的色彩空间。一个值得注意的例外是 Lua 着色器:遗憾的是,没有一种真正好的方法来添加兼容层,因此着色可能会有偏差。透明度可能也存在一些问题:正确的线性色彩空间会改变 alpha 混合混合的视觉结果,有时可能会导致与一些例如汽车仪表盘外观的差异。已经有一些调整试图缓解这个问题,但可能还不够。
另一个问题是,线性色彩空间更加强调渲染中的任何一般问题和捷径,但这将在未来得到修复,因为我们继续努力改进 AC 的视觉方面并升级各种视觉效果。
哦,线性色彩空间尚不适用于展厅或预览生成模式。一些原始效果(例如旧烟雾)在某些光照条件下可能会表现异常。
性能影响
新的着色器集确实有一些额外的指令来在色彩空间之间转换事物并调整参数,以及新的双层雾。但是,旧集有两种雾的变体:原始的 Kunos 雾和 CSP 雾。因此,一般而言,性能影响应该接近于可忽略不计。哦,如果您没有使用 YEBIS 替换,它可能会添加一个额外的后处理步骤,但如果您确实关心性能,YEBIS 替换可能是一个很酷的选项供您尝试(默认 WeatherFX 样式中使用的自动曝光在 0.2.3 中已大大改进)。
材质配置提示
现在的主要提示是,除非您使用的是 fuPBR 着色器,否则请考虑暂时以旧的色彩空间为目标,而不是为线性色彩空间重新配置材质,特别是如果您使用的是所有那些 [INCLUDE: common/material…] 文件中的材质模板。材质参数重新映射将来可能会更改以更适合的某些内容,因此您的旧材质可能会改进,而专门适应线性色彩空间的材质可能会损坏。
当然,所有这些都不适用于 fuPBR 着色器。只需为它们使用常规的 PBR 贴图,无需任何调整。如果外观确实有问题,请不要编辑反射率或粗糙度贴图之类的东西,fuPBR 着色器应该更新以尽可能准确。这就是适当的 PBR 的全部意义。
哦,说到材质,需要注意的是不要让东西太暗。对于常规纹理和材质,您的 ksAmbient/ksDiffuse 值应该在 0.4…0.6 左右。而且,当然,请尝试使它们保持相似。这有一些例外:例如,如果您正在森林中制作赛道,并且出于某种原因不想烘焙顶点 AO,您可以将树下的表面的 ksAmbient 设置得低得多,但对于常规材质,请尝试使这些值相似。
Lua 脚本提示
现在编写脚本时需要考虑两个关键点。首先,如果您在主渲染通道中使用着色器绘制任何内容,您可能需要更新着色器。使用 USE_LINEAR_COLOR_SPACE(可以在 #if USE_LINEAR_COLOR_SPACE 和 if (USE_LINEAR_COLOR_SPACE) 中使用)来更改某些逻辑(如果应用了修复)。您还可以使用 toLinearColorSpace() 和 toSrgbColorSpace() 转换为线性色彩空间并返回。例如,如果您在计算中读取漫反射纹理并将其乘以 ksDiffuse 的某种类似物,在线性色彩空间中,您应该将结果通过 toLinearColorSpace() 传递,它应该会很好地工作(无需在那里分支,如果禁用线性色彩空间,toLinearColorSpace() 不会做任何事情,只是返回其输入)。
另一点是,如果您想将包含场景镜头的输入纹理输出到例如汽车仪表盘上的 LDR 纹理中,应该如何工作。一般来说,确保通过 convertHDR() 传递 HDR 场景输入。在线性色彩空间中,WeatherFX 样式可以将整体场景亮度设置为 0.001 左右,以更有效地使用 float16 范围,convertHDR() 将抵消这一点。如果您需要出于某种原因执行相反的操作,请将第二个参数设置为 true。
此外,您可以从 Lua 本身使用 ac.convertHDRToLDR() 函数访问相同的转换。
WeatherFX 开发提示
要启用修复,只需使用 true 参数调用 ac.useLinearColorSpace()。至于其第二个参数,那是第一个注意事项:AC 和 CSP 几乎所有的渲染目标都使用 float16(也称为 half)格式,虽然通常来说很好,但它的范围只有大约 1/65000…65000(以及相同的负值)。对于原始绘制的方式来说并不是那么糟糕,但现在有了像 resultEmissive = pow(ksEmissive * txDiffuse, 2.2) 这样的转换阶段,达到 65k 边界要容易得多(白色纹理和 ksEmissive 为 200 将已经达到限制)。我发现解决这个问题的最佳方法是使用场景亮度乘数并将其设置在 0.001 左右,因为我们在千分之几中有大量未使用的精度。灯光也是如此:为了优化数据交换,它们的颜色在传递给 GPU 时以 float16 格式存储,因此颜色为 200 的灯光最终会被限制。这就是 ac.useLinearColorSpace() 的第二个参数的作用。将其设置为 100 左右,灯光颜色在发送到 GPU 时将除以 100,LightingFX 贡献稍后在 GPU 端乘以 100。
在更新默认 WeatherFX 样式时,我注意到相当多的错误,太多无法通过选项修复,因此除了切换色彩空间外,该函数还应用了一堆修复,改变了一些函数的行为:
- 云考虑天空雾偏移和指数,而之前它们忽略这些值;
- 远距离发光亮度不会受到场景亮度乘数的影响两次;
- 白色参考点受到场景亮度乘数的影响;
- 月球米氏散射与 v2 天空着色器一起工作(之前由于拼写错误,它没有添加到最终结果中);
- 计算吸收的函数不再混淆 v2 天空面向太阳和背向太阳的值(这是一个特别糟糕的错误,抱歉 🤦♂️);
- 体积光现在使用亮度乘数;
ac.setBrightnessMult()缩放其他值(之前,例如,您必须设置亮度乘数,然后设置雾色才能应用;现在您可以设置雾色,更改亮度乘数,雾色将重新缩放);- 天空颜色计算根本没有考虑太阳颜色。
另一个重大变化是在雾函数中。旧的着色器集有两个雾实现,一个是 Kunos 风格,另一个是基于高度的雾,来自这篇帖子,我之前经常在 ShaderToy 上看到它被使用。遗憾的是,有了正确的线性色彩空间,很明显它不是我们所需要的:由于那个基于高度的特性,它总是在某点达到 1(或者至少在我的版本中是这样,也许我错过了什么,但我无法弄清楚如何正确使用它)。正确的雾永远不应该达到 1,这就是为什么明亮的闪亮物体在雾中比暗物体可见得更远的原因。因此,它被一个新的雾所取代,该雾由 ac.setNearbyFog() 控制,它是您可以在场景之上添加的第二层雾,用于实际的雾和薄雾。旧的新雾被简化了,因此可以用于远处的薄雾或简单的大气光吸收,因此现在您可以一起使用两层雾。
当然,线性正确的色彩空间也需要一个后处理步骤,将线性图像转换回 sRGB 图像。默认情况下,CSP 将在使用您可以通过 ac.setHDRToLDRConversionHints() 提供的值将图像发送到 YEBIS 进行后处理之前为您执行此步骤,但如果您正在开发自定义后处理实现,您可能想要使用一些特殊的东西。例如,对于默认 WeatherFX 样式,我使用了不同的线性→sRGB 转换,它应该更准确。也可能在色调映射步骤之前应用线性→sRGB 转换,而不是在开始时。但如果您不想替换 YEBIS,您总是可以只从 ac.onPostProcessing() 返回带有 sRGB 数据的画布(请参阅默认 WeatherFX 样式中的"render_linear.lua"作为示例)。当然,您也可以只使用 YEBIS 中的 gamma 参数,但根据我的经验,它产生的结果要差得多。
还有一件事。与其他一样,WeatherFX Lua 在 AC 已加载(包括着色器)时首次运行。如果脚本要做的第一件事是切换到线性色彩空间着色器集,这可能会导致巨大的延迟,这可能是一个问题。为了防止此类问题,请在您的"manifest.ini"中添加 [CORE] LINEAR_COLOR_SPACE_HINT = … 值。它可以是 0、1,或者如果您的线性色彩空间是可选的并由"settings.ini"中的复选框控制,则可以是节和键名称。