Attention: Here be dragons
This is the latest
(unstable) version of this documentation, which may document features
not available in or compatible with released stable versions of Godot.
Checking the stable version of the documentation...
HDR 输出
HDR 输出是一项功能,可在支持 HDR 的屏幕上呈现高动态范围(HDR)视觉效果。请注意,HDR 输出 不应与 Godot 用于标准动态范围(SDR)输出和 HDR 输出模式的内部 HDR 渲染相混淆。
HDR 输出支持 iOS、Linux (Wayland)、macOS、visionOS 和 Windows。不支持 Android、Linux (X11) 及网页端。
在你的项目中启用 HDR 输出
你可以通过以下步骤在任何新项目或现有项目中启用 HDR 输出:
确保 所有 Environment 资源都 不 使用仅限 SDR 的功能:
色调映射模式:Filmic 或 ACES
辉光混合模式:柔光(Soft Light)
调整:颜色校正
配置 Renderer 项目设置,将其值设为
mobile或forward_plus。配置 Rendering Device Driver 高级项目设置,将其值针对 iOS 设为
metal,针对 Windows 设为d3d12。配置 Display Server Driver.linuxbsd 高级项目设置,将其值设为
wayland,并启用 Prefer Wayland 编辑器设置。启用 HDR 2D 项目设置,并为所有需要支持 HDR 输出的 SubViewports 和 Windows 启用 use_hdr_2d。
启用 Request HDR Output 项目设置,并为所有需要支持 HDR 输出的其他 Windows 启用 hdr_output_requested。
[可选] 通过将 HDR output demo project 中的示例复制到你的项目中,以此提供游戏内的 HDR 设置。
备注
你的项目中部分设置可能已为 HDR 输出配置妥当。例如,在 Godot 4.6 及更高版本创建的项目里,Windows 渲染设备驱动默认设为 d3d12,但如果项目是用旧版本 Godot 创建的,则需要手动修改此项设置。
在 Godot 中使用 HDR 输出
将 HDR output demo project 作为在 Godot 中使用 HDR 输出的第一步。该演示包含了本页所述概念的示例,将帮助你确保开发环境已为 HDR 输出正确配置。
Godot 编辑器中的 HDR 输出
当启用 Request HDR Output 项目设置后,Godot 编辑器将在其主窗口中使用 HDR 输出。你可以根据游戏视图工具栏右侧的文本判断游戏是运行在 SDR 还是 HDR 输出模式:当窗口尺寸旁边出现 "HDR" 文本时,说明你的游戏正在 "HDR" 模式下运行。
游戏视图工具栏中“HDR”文本旁括号内的数值,即当前的 output max linear value,下文将对此进行详细说明。你可以在游戏窗口的选项菜单中切换 Window.hdr_output_requested 属性:
HDR 输出基础原理
Godot 在 HDR 输出中采用的是 Extended Dynamic Range (EDR) 模式。标准动态范围(SDR)输出仅允许显示颜色分量值在 0.0 到 1.0 之间的色彩,而 HDR 输出则允许出现高于 1.0 的数值。可显示的最大数值由 Window.get_output_max_linear_value() 提供,且该方法在使用 SDR 或 HDR 模式时均有效。
备注
这些图表以不含任何 HDR 色彩的 SDR 图像形式呈现。为了弥补这一限制,各坐标轴上的灰度条应用了辉光效果,用以表示超出 SDR 范围的数值。该图表中的“output max value”(输出最大值)代表了由 Window.get_output_max_linear_value() 返回的最大线性色彩分量值。
面向 HDR 输出的设计
充分利用 HDR 输出主要有两种方法:使用 output max linear value 和使用色调映射。
虽然这两种方法可以在同一项目中使用,但对于使用了超出 SDR 屏幕能力的光照、间接光照、全局光照、自发光材质、后期处理效果,或任何其他利用了场景中色彩值的技术的 Viewport,应使用色调映射来生成 HDR 输出。
output max linear value 仅应用于在不经过色调映射、且不影响光照、后期处理效果或周围色彩的情况下,直接向屏幕呈现色彩。这使得 output max linear value 非常适用于场景中无光照或基础光照(且未超出 SDR 屏幕能力)的 CanvasItems 或无阴影材质。
Viewport.own_world_3d 属性可用于区分哪些 Viewports 受到色调映射(tonemapping)及其他 WorldEnvironment 效果的影响。
使用输出最大线性值
在传统的纯 SDR(标准动态范围)游戏中,色彩的最亮表现受限于该色彩的红色、绿色或蓝色分量达到 1.0 的最大值。而在使用现代 HDR 屏幕时,此限制不再适用,高于 1.0 的色彩分量也能被准确呈现。Godot 通过 output max linear value 提供了屏幕所能呈现的最大色彩分量值。该值在 SDR 和 HDR 模式下均可使用,这使得为两种输出模式构建游戏变得轻而易举,无需根据 HDR 输出是否启用而更改行为逻辑。
由于玩家在调整设备亮度、在设备上启用或禁用 HDR 输出,或者在不同屏幕间移动游戏窗口时,output max linear value 都可能频繁发生变化,因此每一帧都重新获取该值,或使用 output max linear value changed 信号至关重要。在 SDR 模式下,该值始终等于 1.0;而在启用 HDR 输出且玩家将屏幕亮度调至最高时,该值也可能等于 1.0。
建议将此 output max linear value 仅用于“高光”效果或持续时间短暂、仅占屏幕一小部分的特殊视觉效果;若屏幕大部分区域长时间以此最大亮度显示,会导致游戏画面亮得令人不适,仿佛游戏忽略了设备的亮度设置。你可能会发现,某些效果在限制为大于 1.0 但小于 output max linear value 的最大线性值时,视觉效果最佳。关于为何有时需要限制 HDR 最大值的更多讨论,可参阅 Android 开发者博客中的文章 HDR and User Interfaces 。
将一种颜色转换为屏幕所能呈现的最亮色彩,可以通过脚本来实现。在处理 CanvasItem 时,一种便捷的做法是将计算后得到的修改版颜色应用到 modulate 或 self_modulate 属性上,同时将该 CanvasItem 的基础颜色设置为 white。以下脚本演示了这一过程:
extends CanvasItem
# Set this to your desired color when the CanvasItem's base color is white.
@export var sdr_self_modulate: Color = Color.WHITE
# Set this to -1.0 to disable limiting the maximum color value.
@export_range(0, 20, 0.1, "or_less", "or_greater") var max_linear_value_limit: float = -1.0
func _enter_tree() -> void:
var window: Window = get_window()
window.output_max_linear_value_changed.connect(_on_output_max_linear_value_changed)
_on_output_max_linear_value_changed(window.get_output_max_linear_value())
func _exit_tree() -> void:
get_window().output_max_linear_value_changed.disconnect(_on_output_max_linear_value_changed)
func _on_output_max_linear_value_changed(output_max_linear_value: float) -> void:
# Adjust the brightness of color to be the brightest possible, regardless
# of SDR or HDR output, but no brighter than max_linear_value_limit.
if max_linear_value_limit >= 0.0:
output_max_linear_value = minf(output_max_linear_value, max_linear_value_limit)
self_modulate = normalize_color(sdr_self_modulate, output_max_linear_value)
func normalize_color(srgb_color, output_max_linear_value = 1.0):
# Color must be linear-encoded to use math operations.
var linear_color = srgb_color.srgb_to_linear()
var max_rgb_value = maxf(linear_color.r, maxf(linear_color.g, linear_color.b))
var brightness_scale = output_max_linear_value / max_rgb_value
linear_color *= brightness_scale
# Undo changes to the alpha channel, which should not be modified.
linear_color.a = srgb_color.a
# Convert back to nonlinear sRGB encoding, which is required for Color in
# Godot unless stated otherwise.
return linear_color.linear_to_srgb()
HDR 输出演示项目 <https://github.com/godotengine/godot-demo-projects/tree/master/misc/hdr_output>`__ 里包含了这个脚本更高级的版本,并且展示了如何在你的项目中运用这种方法的示例。
使用色调映射
要想在不使用 output max linear value 的情况下生成 HDR 输出,你的场景就需要包含那些超出 SDR(标准动态范围)屏幕所能呈现范围的颜色值。因此,使用像 Reinhard 或 AgX 这样的色调映射器就显得尤为重要,它们能够妥善处理这些超高亮度的场景数值,确保其在 SDR 和 HDR 屏幕上都能被正确显示。
色调映射与 HDR
色调映射器(Tonemapper)的核心作用,就是将自然界场景中极高的亮度动态范围,压缩到一个较小的动态范围内,以便能够在屏幕上呈现出来。Godot 中的色调映射器会利用 output max linear value 来确定屏幕能够呈现的输出范围。例如,在 Godot 使用 Reinhard 色调映射器时,场景里从 0.0 到 tonemap white 的线性数值,会被映射到从 0.0 到 output max linear value 的输出范围内。
通过这种方法,你可以调整 tonemap white,从而确保任何低于 tonemap white 的线性场景数值都不会被裁切(clipping)掉。这能保证在动态范围比原始场景更低的屏幕上显示图像时,画面细节不会丢失。
虽然在 SDR(标准动态范围)模式下,这种行为非常稳定,因为 output max linear value 被固定在了 1.0 ;但在 HDR(高动态范围)模式下,这种行为则是动态变化的,它完全取决于当前屏幕(显示器)的实际性能:
正如上方的图表所示,当 output max linear value 等于或高于 class_Environment_property_tonemap_white 时,Reinhard 色调映射器的表现将与 Linear 色调映射器完全一致。这使得在能够还原原始场景更高亮度的 HDR 屏幕上,可以实现精准的色彩再现。当 output max linear value 进一步升高并超过 tonemap white 时,色调映射白点会自动调整以匹配这个输出最大线性值。
在这方面,AgX 色调映射器的表现与 Reinhard 类似,但它的 tonemap white 会始终乘以 output max linear value。而 Linear 色调映射器则完全不进行任何色调映射处理;在任何情况下,它的 tonemap white 都直接等于 output max linear value。至于 Filmic 和 ACES 色调映射器,它们则会完全忽略 output max linear value,始终输出标准动态范围(SDR)的图像。
为什么不把输出最大线性值(output max linear value)和其他技术手段结合起来用呢?
色调映射、间接光照、全局光照以及后处理特效,这些技术都需要依赖稳定的场景色彩数值,才能在 SDR(标准动态范围)和 HDR(高动态范围)模式下,都产生一致且可预测的画面效果。如果开发者在使用这些技术时,场景里的色彩数值会随着 output max linear value 的变化而改变,那么最终的画面效果在不同性能的屏幕上,看起来就会大相径庭,不再保持一致了。
举个例子,辉光(Glow)特效的强度会直接受到场景亮度的影响。如果场景的亮度会随着 output max linear value 的变化而发生改变,那么辉光特效的强度也会跟着变:output max linear value ,产生的辉光效果就会越强——而这通常是一种我们不希望看到的糟糕表现。
绝对亮度值
当使用 HDR 输出时,output max linear value 是根据‘参考白亮度’以及屏幕的‘最大亮度’共同计算得出的。
参考白亮度
参考白亮度(reference white luminance),简称参考亮度(reference luminance),代表的是标准动态范围(SDR)下所能呈现的最亮白色值。当用户调整产生视频信号的设备(比如台式电脑、笔记本电脑或智能手机)的亮度设置时,他们其实就是在调节自己的参考亮度。在智能手机上,这种变化可能会通过手机的‘自动屏幕亮度’功能自动发生,也可能在用户手动调节屏幕亮度时发生。而在台式或笔记本电脑上,根据操作系统的不同,有多种不同的方式来调节这个参考亮度。
这个数值通常在 100 到 300 尼特(nits)之间,并且总是由一个精确等于 1.0 的 output max linear value 来表示。这个数值有时也被称为 "纸张白(paper white)" 或者 "SDR 白阶(SDR white level)" 。
备注
在 Windows 系统上使用外接显示器时,HDR 显示设置中的 SDR 内容亮度(SDR content brightness) 会直接控制参考亮度值,这也是调节 Windows 桌面和 Godot 引擎亮度的主要方式。而当在 Windows 上使用设备自带的 HDR 屏幕(比如笔记本电脑的内置屏)时,更改 HDR 内容亮度(HDR content brightness) 虽然也会直接控制参考亮度,但它对 Windows 桌面或 Godot 的亮度却没有任何影响,因为系统采用了另一套独立的亮度实现方案,直接抵消了改变参考亮度所带来的任何效果。
最大亮度
最大亮度是 HDR(高动态范围)屏幕的一项属性。这个数值通常在 250 到 2000 尼特(nits)甚至更高之间。有些外接屏幕报告的最大亮度值,往往会高于它实际的物理能力,这就会导致屏幕本身去进行可见的色调映射(tonemapping)处理。不过,一些台式机或笔记本电脑的操作系统,都提供了校准每台外接屏幕最大亮度值的方法。
备注
在 Windows 系统上使用笔记本自带屏幕时,当用户调节屏幕亮度,系统报告的‘最大亮度’数值会随之改变,而报告的‘参考亮度’则会保持恒定。这种行为不仅与在 Windows 上外接显示器并调整 SDR 内容亮度 HDR 显示设置时的表现完全相反,也与其他操作系统的表现相反。
实际输出中的最大线性值
在 HDR 模式下,当用户降低他们的参考亮度(reference luminance)时,output max linear value 会随之增加,因为此时可用的 HDR 动态余量(HDR headroom)变多了。同理,当用户提高他们的参考亮度时,可用的 HDR 动态余量就会变少,output max linear value 也会随之降低。在某些情况下,如果在 HDR 模式下将参考亮度调到最高,output max linear value 将会等于 1.0 ,变得和 SDR 模式的表现完全一样,因为此时已经没有多余的 HDR 动态余量可用了。
屏幕与屏幕之间是不一样的
SDR 标准的设计初衷,是为了适配当时全球普遍使用的现有屏幕的性能。而 HDR 标准则是有意采用了截然相反的思路:它们是为了一种‘理想屏幕’的性能而设计的,尽管这种屏幕目前尚未广泛普及。
实际上,这意味着普通的 HDR 屏幕可能会自行执行内部的色调映射、色域映射或动态色调映射(DTM),以便能够呈现那些色域和亮度范围超出了其物理硬件极限的内容。有些屏幕无法在超过一小部分(1% 到 10%)屏幕区域的情况下呈现极高亮度的色彩,当这种情况发生时,它们会暂时调暗整个画面或部分画面。这些功能可能会导致呈现出的颜色与其他屏幕不一致,因此在开发 HDR 游戏时,如果可能的话,最好将它们禁用。你或许可以通过开启屏幕上的 HGiG 模式,或者将屏幕模式设置为“裁剪(clip)”和/或“稳定(stable)”,来禁用其中部分或全部功能。有些 HDR 屏幕在呈现深色或饱和色彩时的效果会与其他屏幕不同;这种外观上的差异通常是由屏幕技术本身的特性导致的。