Skip to content

Commit

Permalink
Add a Preview Bake button for quick iteration with LightmapGI
Browse files Browse the repository at this point in the history
When using the Preview Bake button instead of Bake Lightmaps,
low quality settings will be used instead of settings from the
LightmapGI node. A node configuration warning will be displayed
besides the LightmapGI node to inform the user that the bake
was done in preview mode. This preview bake warning is persisted
to storage to make teamwork easier.

This helps iterate quicker on a scene's lighting since you don't
need to wait as much time to see results.

On a simple test scene, Preview Bake is about 3 times faster
than a "final" bake with the default LightmapGI settings
(if denoising is not the bottleneck). This metric will vary depending
on CPU and GPU speed, as the baking process is performed on the GPU
but denoising is performedon the CPU.

The preview bake settings can be adjusted in the Project Settings.

This also prints a message with the effective quality settings
before beginning the lightmapping process.
  • Loading branch information
Calinou committed Dec 5, 2024
1 parent 1f47e4c commit 27f792e
Show file tree
Hide file tree
Showing 9 changed files with 171 additions and 15 deletions.
1 change: 1 addition & 0 deletions doc/classes/LightmapGI.xml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
The [LightmapGI] node is used to compute and store baked lightmaps. Lightmaps are used to provide high-quality indirect lighting with very little light leaking. [LightmapGI] can also provide rough reflections using spherical harmonics if [member directional] is enabled. Dynamic objects can receive indirect lighting thanks to [i]light probes[/i], which can be automatically placed by setting [member generate_probes_subdiv] to a value other than [constant GENERATE_PROBES_DISABLED]. Additional lightmap probes can also be added by creating [LightmapProbe] nodes. The downside is that lightmaps are fully static and cannot be baked in an exported project. Baking a [LightmapGI] node is also slower compared to [VoxelGI].
[b]Procedural generation:[/b] Lightmap baking functionality is only available in the editor. This means [LightmapGI] is not suited to procedurally generated or user-built levels. For procedurally generated or user-built levels, use [VoxelGI] or SDFGI instead (see [member Environment.sdfgi_enabled]).
[b]Performance:[/b] [LightmapGI] provides the best possible run-time performance for global illumination. It is suitable for low-end hardware including integrated graphics and mobile devices.
[b]Bake modes:[/b] In the editor, after selecting a [LightmapGI] node, you can choose between performing a [b]Preview Bake[/b] or a "final" bake using the [b]Bake Lightmaps[/b] button. Preview bakes use lower quality settings but are significantly faster to bake (run-time performance is identical). Using preview bakes during development allows you to iterate on your 3D scene's lighting faster. You can change the preview bake quality settings by adjusting the [ProjectSettings]' [code]rendering/cpu_lightmapper/*[/code] properties.
[b]Note:[/b] Due to how lightmaps work, most properties only have a visible effect once lightmaps are baked again.
[b]Note:[/b] Lightmap baking on [CSGShape3D]s and [PrimitiveMesh]es is not supported, as these cannot store UV2 data required for baking.
[b]Note:[/b] If no custom lightmappers are installed, [LightmapGI] can only be baked from devices that support the Forward+ or Mobile renderers.
Expand Down
15 changes: 15 additions & 0 deletions doc/classes/LightmapGIData.xml
Original file line number Diff line number Diff line change
Expand Up @@ -38,12 +38,27 @@
Returns the [NodePath] of the baked object at index [param user_idx].
</description>
</method>
<method name="is_preview_bake" qualifiers="const">
<return type="bool" />
<description>
Returns [code]true[/code] if this baked lightmap is the result of a [i]preview bake[/i], [code]false[/code] otherwise. See also [method set_preview_bake].
See the [LightmapGI] class description for more information.
</description>
</method>
<method name="is_using_spherical_harmonics" qualifiers="const">
<return type="bool" />
<description>
If [code]true[/code], lightmaps were baked with directional information. See also [member LightmapGI.directional].
</description>
</method>
<method name="set_preview_bake">
<return type="void" />
<param index="0" name="preview_bake" type="bool" />
<description>
If [code]true[/code], the baked lightmap is considered to be a [i]preview bake[/i]. This is set to [code]true[/code] by the editor when using the [b]Preview Bake[/b] button at the top of the 3D editor, and [code]false[/code] when using the [b]Bake Lightmaps[/b] button. This method can also be used by plugins. See also [method is_preview_bake].
See the [LightmapGI] class description for more information.
</description>
</method>
<method name="set_uses_spherical_harmonics">
<return type="void" />
<param index="0" name="uses_spherical_harmonics" type="bool" />
Expand Down
14 changes: 13 additions & 1 deletion doc/classes/ProjectSettings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2632,12 +2632,24 @@
- Intel GPUs: SYCL libraries
If no GPU acceleration is configured on the system, multi-threaded CPU-based denoising will be performed instead. This CPU-based denoising is significantly slower than the JNLM denoiser in most cases.
</member>
<member name="rendering/lightmapping/preview_bake/generate_probes_max_subdiv" type="int" setter="" getter="" default="0">
Clamps the maximum value of [member LightmapGI.generate_probes_subdiv] when using the [b]Preview Bake[/b] button after selecting a [LightmapGI] node in the editor.
</member>
<member name="rendering/lightmapping/preview_bake/max_bounces" type="int" setter="" getter="" default="2">
Clamps the maximum value of [member LightmapGI.bounces] when using the [b]Preview Bake[/b] button after selecting a [LightmapGI] node in the editor. If the number of bounces defined in the [LightmapGI] node is lower than this setting, the number of bounces defined in the [LightmapGI] will be used instead.
</member>
<member name="rendering/lightmapping/preview_bake/max_quality" type="int" setter="" getter="" default="0">
Clamps the maximum value of [member LightmapGI.quality] when using the [b]Preview Bake[/b] button after selecting a [LightmapGI] node in the editor.
</member>
<member name="rendering/lightmapping/preview_bake/texel_scale_factor" type="float" setter="" getter="" default="0.5">
Multiplier for the [member LightmapGI.texel_scale] when using the [b]Preview Bake[/b] button after selecting a [LightmapGI] node in the editor. Lower values result in significantly faster baking and smaller file sizes at the cost of blurrier and less precise lightmaps. The default value of [code]0.5[/code] halves the resolution on each axis, resulting in 4× fewer pixels needing to be baked on average.
</member>
<member name="rendering/lightmapping/lightmap_gi/use_bicubic_filter" type="bool" setter="" getter="" default="true">
If [code]true[/code], applies a bicubic filter during lightmap sampling. This makes lightmaps look much smoother, at a moderate performance cost.
[b]Note:[/b] The bicubic filter exaggerates the 'bleeding' effect that occurs when a lightmap's resolution is low enough.
</member>
<member name="rendering/lightmapping/primitive_meshes/texel_size" type="float" setter="" getter="" default="0.2">
The texel_size that is used to calculate the [member Mesh.lightmap_size_hint] on [PrimitiveMesh] resources if [member PrimitiveMesh.add_uv2] is enabled.
The texel size that is used to calculate the [member Mesh.lightmap_size_hint] on [PrimitiveMesh] resources if [member PrimitiveMesh.add_uv2] is enabled. Lower values result in more precise lightmaps on primitive meshes, at the cost of longer bake times and larger file sizes.
</member>
<member name="rendering/lightmapping/probe_capture/update_speed" type="float" setter="" getter="" default="15">
The framerate-independent update speed when representing dynamic object lighting from [LightmapProbe]s. Higher values make dynamic object lighting update faster. Higher values can prevent fast-moving objects from having "outdated" indirect lighting displayed on them, at the cost of possible flickering when an object moves from a bright area to a shaded area.
Expand Down
1 change: 1 addition & 0 deletions editor/icons/BakePreview.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
27 changes: 20 additions & 7 deletions editor/plugins/lightmap_gi_editor_plugin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -66,9 +66,9 @@ void LightmapGIEditorPlugin::_bake_select_file(const String &p_file) {

if (err == LightmapGI::BAKE_ERROR_OK) {
if (get_tree()->get_edited_scene_root() == lightmap) {
err = lightmap->bake(lightmap, p_file, bake_func_step);
err = lightmap->bake(lightmap, p_file, bake_func_step, nullptr, preview_mode);
} else {
err = lightmap->bake(lightmap->get_parent(), p_file, bake_func_step);
err = lightmap->bake(lightmap->get_parent(), p_file, bake_func_step, nullptr, preview_mode);
}
}
} else {
Expand Down Expand Up @@ -123,7 +123,8 @@ void LightmapGIEditorPlugin::_bake_select_file(const String &p_file) {
}
}

void LightmapGIEditorPlugin::_bake() {
void LightmapGIEditorPlugin::_bake(bool p_preview_mode) {
preview_mode = p_preview_mode;
_bake_select_file("");
}

Expand All @@ -142,8 +143,10 @@ bool LightmapGIEditorPlugin::handles(Object *p_object) const {

void LightmapGIEditorPlugin::make_visible(bool p_visible) {
if (p_visible) {
bake_preview->show();
bake->show();
} else {
bake_preview->hide();
bake->hide();
}
}
Expand Down Expand Up @@ -173,15 +176,25 @@ void LightmapGIEditorPlugin::bake_func_end(uint64_t p_time_started) {
}

void LightmapGIEditorPlugin::_bind_methods() {
ClassDB::bind_method("_bake", &LightmapGIEditorPlugin::_bake);
ClassDB::bind_method("_bake", &LightmapGIEditorPlugin::_bake, DEFVAL(false));
}

LightmapGIEditorPlugin::LightmapGIEditorPlugin() {
bake = memnew(Button);
bake->set_theme_type_variation("FlatButton");
// TODO: Rework this as a dedicated toolbar control so we can hook into theme changes and update it
// when the editor theme updates.
bake_preview = memnew(Button);
bake_preview->set_theme_type_variation("FlatButton");
bake_preview->set_button_icon(EditorNode::get_singleton()->get_editor_theme()->get_icon(SNAME("BakePreview"), EditorStringName(EditorIcons)));
bake_preview->set_tooltip_text(TTR("Bakes lightmaps with low-quality settings for quick iteration.\nPreview bake quality can be changed in the Rendering > Lightmapping > Preview Bake section of the Project Settings."));
bake_preview->set_text(TTR("Preview Bake"));
bake_preview->hide();
bake_preview->connect("pressed", callable_mp(this, &LightmapGIEditorPlugin::_bake).bind(true));
add_control_to_container(CONTAINER_SPATIAL_EDITOR_MENU, bake_preview);

bake = memnew(Button);
bake->set_theme_type_variation("FlatButton");
bake->set_button_icon(EditorNode::get_singleton()->get_editor_theme()->get_icon(SNAME("Bake"), EditorStringName(EditorIcons)));
bake->set_tooltip_text(TTR("Bakes lightmaps with the settings specified in the LightmapGI node."));
bake->set_text(TTR("Bake Lightmaps"));

#ifdef MODULE_LIGHTMAPPER_RD_ENABLED
Expand All @@ -201,7 +214,7 @@ LightmapGIEditorPlugin::LightmapGIEditorPlugin() {
#endif // MODULE_LIGHTMAPPER_RD_ENABLED

bake->hide();
bake->connect(SceneStringName(pressed), Callable(this, "_bake"));
bake->connect("pressed", callable_mp(this, &LightmapGIEditorPlugin::_bake).bind(false));
add_control_to_container(CONTAINER_SPATIAL_EDITOR_MENU, bake);
lightmap = nullptr;

Expand Down
6 changes: 5 additions & 1 deletion editor/plugins/lightmap_gi_editor_plugin.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,15 +43,19 @@ class LightmapGIEditorPlugin : public EditorPlugin {

LightmapGI *lightmap = nullptr;

Button *bake_preview = nullptr;
Button *bake = nullptr;

// If `true`, low-quality bake settings will be used for the next bake.
bool preview_mode = false;

EditorFileDialog *file_dialog = nullptr;
static EditorProgress *tmp_progress;
static bool bake_func_step(float p_progress, const String &p_description, void *, bool p_refresh);
static void bake_func_end(uint64_t p_time_started);

void _bake_select_file(const String &p_file);
void _bake();
void _bake(bool p_preview_mode = false);

protected:
static void _bind_methods();
Expand Down
6 changes: 6 additions & 0 deletions modules/lightmapper_rd/register_types.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,12 @@ void initialize_lightmapper_rd_module(ModuleInitializationLevel p_level) {
GLOBAL_DEF(PropertyInfo(Variant::INT, "rendering/lightmapping/bake_performance/max_rays_per_probe_pass", PROPERTY_HINT_RANGE, "1,256,1,or_greater"), 64);

GLOBAL_DEF(PropertyInfo(Variant::INT, "rendering/lightmapping/denoising/denoiser", PROPERTY_HINT_ENUM, "JNLM,OIDN"), 0);

GLOBAL_DEF(PropertyInfo(Variant::INT, "rendering/lightmapping/preview_bake/max_quality", PROPERTY_HINT_ENUM, "Low,Medium,High,Ultra"), 0);
GLOBAL_DEF(PropertyInfo(Variant::INT, "rendering/lightmapping/preview_bake/max_bounces", PROPERTY_HINT_RANGE, "0,16,1"), 2);
GLOBAL_DEF(PropertyInfo(Variant::FLOAT, "rendering/lightmapping/preview_bake/texel_scale_factor", PROPERTY_HINT_RANGE, "0.05,1.0,0.001"), 0.5);
GLOBAL_DEF(PropertyInfo(Variant::INT, "rendering/lightmapping/preview_bake/generate_probes_max_subdiv", PROPERTY_HINT_ENUM, "Disabled,4,8,16,32"), 0);

#ifndef _3D_DISABLED
GDREGISTER_CLASS(LightmapperRD);
Lightmapper::create_gpu = create_lightmapper_rd;
Expand Down
Loading

0 comments on commit 27f792e

Please sign in to comment.