How to Save HDR Screenshots in Godot

Godot 4.7 introduces support for HDR output. You may want to take screenshots of your game running in HDR mode and share them on the web. This post describes how to get screenshots like the following that work well in almost any browser that isn’t Firefox. (Firefox doesn’t support HDR at the time of writing this post.)

These screenshots are best viewed on an HDR-compatible smartphone or laptop with display brightness set to 50%:

Step 1: Manually set the output maximum linear value

Before saving an HDR screenshot, you should set your output maximum linear value to be in the range that most displays will be able to show without much tonemapping. I like to use a value of 4.0. This value cannot be set directly, but by forcing your maximum luminance to equal exactly 4 times your reference luminance, you’ll get a maximum linear value of 4.0:

func _process(_delta: float) -> void:
	var window_id: int = get_window().get_window_id()
	var reference_luminance: float = DisplayServer.window_get_hdr_output_current_reference_luminance(window_id)
	DisplayServer.window_set_hdr_output_max_luminance(reference_luminance * 4.0, window_id)

Note: On platforms besides Windows and macOS, you can’t set your maximum luminance in Godot. Instead, you’ll need to set the maximum luminance using an HDR calibration tool, like what is found in the Wayland (Linux) display settings.

You can verify that the maximum linear value is correct using the text in the top left of Godot’s game view:

Step 2: Save your EXR screenshot

Next is to save the EXR screenshot. Make sure to set the color_image parameter to true and pass in the maximum linear value:

var window: Window = get_window()
var image: Image = window.get_texture().get_image()
image.save_exr("screenshot.exr", false, true, window.get_output_max_linear_value())

Step 3: Prepare for the web

Now we’ve got a great, high quality EXR screenshot… But EXR files are not the most shareable file format. To prepare a screenshot for the web, we’ll first convert the EXR file to an HDR PNG file and then convert that to an HDR AVIF file. You can use the HDR PNG file directly on the web, but it is lossless and has quite a large file size. AVIF is probably a more preferable format for most cases where a smaller file size is best.

On macOS, I used the the following terminal commands to create my HDR PNG and HDR AVIF file:

brew install jpeg-xl
brew install libavif

/opt/homebrew/bin/exr_to_pq --luminance 'white=203' screenshot.exr screenshot.png

/opt/homebrew/bin/avifenc screenshot.png --clli 812,0 --cicp 9/16/9 --depth 10 -q 85 screenshot.avif

The --clli 812 argument in the final command is the maximum luminance of the screenshot and should equal the maximum linear value (4.0) multiplied by 203 nits. Instead of calculating the number yourself, you can use the maxiumum luminance value that is provided by the output of the exr_to_pq command to ensure minimal tonemapping will be applied to your image when it is viewed on different displays.

If you’re using a different operating system like Windows or Linux, you’ll need to find a good distribution of these command line tools that have been built with support for EXR and HDR.

Tips

You should provide the same --clli value for all HDR screenshots to give consistent appearance, even if some of the screenshots have a lower maximum value. This way the same tonemapping will be applied to all screenshots, giving a consistent appearance.

In the above SDR example, I generated the SDR screenshot by setting the maximum luminance to equal the current reference luminance to produce a maximum linear value of 1.0. I then used the same --clli value for both the HDR and “SDR” screenshots to give a consistent appearance between the two.

Special Thanks

The whole process for converting an EXR image into an HDR PNG and AVIF file was figured out by dogelition on the HDR Den Discord server. Thanks for the incredible insight! I never would have figured this out on my own 🙂


Posted

in

by

Tags: