Rewrite the GPU Lightmapper's indirect lighting logic to match Godot 3.5's CPU Lightmapper.

Port over the logic from Godot 3.5 for indirect lighting. This should fix many issues about indirect bounces causing more energy and improve the overall quality of the result.
This commit is contained in:
Dario 2023-10-13 14:32:22 -03:00
parent 51f81e1c88
commit a9a197d2dc
10 changed files with 567 additions and 526 deletions

View File

@ -18,6 +18,10 @@
<member name="bias" type="float" setter="set_bias" getter="get_bias" default="0.0005"> <member name="bias" type="float" setter="set_bias" getter="get_bias" default="0.0005">
The bias to use when computing shadows. Increasing [member bias] can fix shadow acne on the resulting baked lightmap, but can introduce peter-panning (shadows not connecting to their casters). Real-time [Light3D] shadows are not affected by this [member bias] property. The bias to use when computing shadows. Increasing [member bias] can fix shadow acne on the resulting baked lightmap, but can introduce peter-panning (shadows not connecting to their casters). Real-time [Light3D] shadows are not affected by this [member bias] property.
</member> </member>
<member name="bounce_indirect_energy" type="float" setter="set_bounce_indirect_energy" getter="get_bounce_indirect_energy" default="1.0">
The energy multiplier for each bounce. Higher values will make indirect lighting brighter. A value of [code]1.0[/code] represents physically accurate behavior, but higher values can be used to make indirect lighting propagate more visibly when using a low number of bounces. This can be used to speed up bake times by lowering the number of [member bounces] then increasing [member bounce_indirect_energy].
[b]Note:[/b] [member bounce_indirect_energy] only has an effect if [member bounces] is set to a value greater than or equal to [code]1[/code].
</member>
<member name="bounces" type="int" setter="set_bounces" getter="get_bounces" default="3"> <member name="bounces" type="int" setter="set_bounces" getter="get_bounces" default="3">
Number of light bounces that are taken into account during baking. Higher values result in brighter, more realistic lighting, at the cost of longer bake times. If set to [code]0[/code], only environment lighting, direct light and emissive lighting is baked. Number of light bounces that are taken into account during baking. Higher values result in brighter, more realistic lighting, at the cost of longer bake times. If set to [code]0[/code], only environment lighting, direct light and emissive lighting is baked.
</member> </member>
@ -64,6 +68,10 @@
<member name="use_denoiser" type="bool" setter="set_use_denoiser" getter="is_using_denoiser" default="true"> <member name="use_denoiser" type="bool" setter="set_use_denoiser" getter="is_using_denoiser" default="true">
If [code]true[/code], uses a GPU-based denoising algorithm on the generated lightmap. This eliminates most noise within the generated lightmap at the cost of longer bake times. File sizes are generally not impacted significantly by the use of a denoiser, although lossless compression may do a better job at compressing a denoised image. If [code]true[/code], uses a GPU-based denoising algorithm on the generated lightmap. This eliminates most noise within the generated lightmap at the cost of longer bake times. File sizes are generally not impacted significantly by the use of a denoiser, although lossless compression may do a better job at compressing a denoised image.
</member> </member>
<member name="use_texture_for_bounces" type="bool" setter="set_use_texture_for_bounces" getter="is_using_texture_for_bounces" default="true">
If [code]true[/code], a texture with the lighting information will be generated to speed up the generation of indirect lighting at the cost of some accuracy. The geometry might exhibit extra light leak artifacts when using low resolution lightmaps or UVs that stretch the lightmap significantly across surfaces. Leave [member use_texture_for_bounces] at its default value of [code]true[/code] if unsure.
[b]Note:[/b] [member use_texture_for_bounces] only has an effect if [member bounces] is set to a value greater than or equal to [code]1[/code].
</member>
</members> </members>
<constants> <constants>
<constant name="BAKE_QUALITY_LOW" value="0" enum="BakeQuality"> <constant name="BAKE_QUALITY_LOW" value="0" enum="BakeQuality">

View File

@ -2425,25 +2425,25 @@
<member name="rendering/lightmapping/bake_quality/high_quality_probe_ray_count" type="int" setter="" getter="" default="512"> <member name="rendering/lightmapping/bake_quality/high_quality_probe_ray_count" type="int" setter="" getter="" default="512">
The number of rays to use for baking dynamic object lighting in [LightmapProbe]s when [member LightmapGI.quality] is [constant LightmapGI.BAKE_QUALITY_HIGH]. The number of rays to use for baking dynamic object lighting in [LightmapProbe]s when [member LightmapGI.quality] is [constant LightmapGI.BAKE_QUALITY_HIGH].
</member> </member>
<member name="rendering/lightmapping/bake_quality/high_quality_ray_count" type="int" setter="" getter="" default="256"> <member name="rendering/lightmapping/bake_quality/high_quality_ray_count" type="int" setter="" getter="" default="512">
The number of rays to use for baking lightmaps with [LightmapGI] when [member LightmapGI.quality] is [constant LightmapGI.BAKE_QUALITY_HIGH]. The number of rays to use for baking lightmaps with [LightmapGI] when [member LightmapGI.quality] is [constant LightmapGI.BAKE_QUALITY_HIGH].
</member> </member>
<member name="rendering/lightmapping/bake_quality/low_quality_probe_ray_count" type="int" setter="" getter="" default="64"> <member name="rendering/lightmapping/bake_quality/low_quality_probe_ray_count" type="int" setter="" getter="" default="64">
The number of rays to use for baking dynamic object lighting in [LightmapProbe]s when [member LightmapGI.quality] is [constant LightmapGI.BAKE_QUALITY_LOW]. The number of rays to use for baking dynamic object lighting in [LightmapProbe]s when [member LightmapGI.quality] is [constant LightmapGI.BAKE_QUALITY_LOW].
</member> </member>
<member name="rendering/lightmapping/bake_quality/low_quality_ray_count" type="int" setter="" getter="" default="16"> <member name="rendering/lightmapping/bake_quality/low_quality_ray_count" type="int" setter="" getter="" default="32">
The number of rays to use for baking lightmaps with [LightmapGI] when [member LightmapGI.quality] is [constant LightmapGI.BAKE_QUALITY_LOW]. The number of rays to use for baking lightmaps with [LightmapGI] when [member LightmapGI.quality] is [constant LightmapGI.BAKE_QUALITY_LOW].
</member> </member>
<member name="rendering/lightmapping/bake_quality/medium_quality_probe_ray_count" type="int" setter="" getter="" default="256"> <member name="rendering/lightmapping/bake_quality/medium_quality_probe_ray_count" type="int" setter="" getter="" default="256">
The number of rays to use for baking dynamic object lighting in [LightmapProbe]s when [member LightmapGI.quality] is [constant LightmapGI.BAKE_QUALITY_MEDIUM]. The number of rays to use for baking dynamic object lighting in [LightmapProbe]s when [member LightmapGI.quality] is [constant LightmapGI.BAKE_QUALITY_MEDIUM].
</member> </member>
<member name="rendering/lightmapping/bake_quality/medium_quality_ray_count" type="int" setter="" getter="" default="64"> <member name="rendering/lightmapping/bake_quality/medium_quality_ray_count" type="int" setter="" getter="" default="128">
The number of rays to use for baking lightmaps with [LightmapGI] when [member LightmapGI.quality] is [constant LightmapGI.BAKE_QUALITY_MEDIUM]. The number of rays to use for baking lightmaps with [LightmapGI] when [member LightmapGI.quality] is [constant LightmapGI.BAKE_QUALITY_MEDIUM].
</member> </member>
<member name="rendering/lightmapping/bake_quality/ultra_quality_probe_ray_count" type="int" setter="" getter="" default="2048"> <member name="rendering/lightmapping/bake_quality/ultra_quality_probe_ray_count" type="int" setter="" getter="" default="2048">
The number of rays to use for baking dynamic object lighting in [LightmapProbe]s when [member LightmapGI.quality] is [constant LightmapGI.BAKE_QUALITY_ULTRA]. The number of rays to use for baking dynamic object lighting in [LightmapProbe]s when [member LightmapGI.quality] is [constant LightmapGI.BAKE_QUALITY_ULTRA].
</member> </member>
<member name="rendering/lightmapping/bake_quality/ultra_quality_ray_count" type="int" setter="" getter="" default="1024"> <member name="rendering/lightmapping/bake_quality/ultra_quality_ray_count" type="int" setter="" getter="" default="2048">
The number of rays to use for baking lightmaps with [LightmapGI] when [member LightmapGI.quality] is [constant LightmapGI.BAKE_QUALITY_ULTRA]. The number of rays to use for baking lightmaps with [LightmapGI] when [member LightmapGI.quality] is [constant LightmapGI.BAKE_QUALITY_ULTRA].
</member> </member>
<member name="rendering/lightmapping/denoising/denoiser" type="int" setter="" getter="" default="0"> <member name="rendering/lightmapping/denoising/denoiser" type="int" setter="" getter="" default="0">

View File

@ -55,7 +55,7 @@ void LightmapperRD::add_mesh(const MeshData &p_mesh) {
mesh_instances.push_back(mi); mesh_instances.push_back(mi);
} }
void LightmapperRD::add_directional_light(bool p_static, const Vector3 &p_direction, const Color &p_color, float p_energy, float p_angular_distance, float p_shadow_blur) { void LightmapperRD::add_directional_light(bool p_static, const Vector3 &p_direction, const Color &p_color, float p_energy, float p_indirect_energy, float p_angular_distance, float p_shadow_blur) {
Light l; Light l;
l.type = LIGHT_TYPE_DIRECTIONAL; l.type = LIGHT_TYPE_DIRECTIONAL;
l.direction[0] = p_direction.x; l.direction[0] = p_direction.x;
@ -65,13 +65,14 @@ void LightmapperRD::add_directional_light(bool p_static, const Vector3 &p_direct
l.color[1] = p_color.g; l.color[1] = p_color.g;
l.color[2] = p_color.b; l.color[2] = p_color.b;
l.energy = p_energy; l.energy = p_energy;
l.indirect_energy = p_indirect_energy;
l.static_bake = p_static; l.static_bake = p_static;
l.size = Math::tan(Math::deg_to_rad(p_angular_distance)); l.size = Math::tan(Math::deg_to_rad(p_angular_distance));
l.shadow_blur = p_shadow_blur; l.shadow_blur = p_shadow_blur;
lights.push_back(l); lights.push_back(l);
} }
void LightmapperRD::add_omni_light(bool p_static, const Vector3 &p_position, const Color &p_color, float p_energy, float p_range, float p_attenuation, float p_size, float p_shadow_blur) { void LightmapperRD::add_omni_light(bool p_static, const Vector3 &p_position, const Color &p_color, float p_energy, float p_indirect_energy, float p_range, float p_attenuation, float p_size, float p_shadow_blur) {
Light l; Light l;
l.type = LIGHT_TYPE_OMNI; l.type = LIGHT_TYPE_OMNI;
l.position[0] = p_position.x; l.position[0] = p_position.x;
@ -83,13 +84,14 @@ void LightmapperRD::add_omni_light(bool p_static, const Vector3 &p_position, con
l.color[1] = p_color.g; l.color[1] = p_color.g;
l.color[2] = p_color.b; l.color[2] = p_color.b;
l.energy = p_energy; l.energy = p_energy;
l.indirect_energy = p_indirect_energy;
l.static_bake = p_static; l.static_bake = p_static;
l.size = p_size; l.size = p_size;
l.shadow_blur = p_shadow_blur; l.shadow_blur = p_shadow_blur;
lights.push_back(l); lights.push_back(l);
} }
void LightmapperRD::add_spot_light(bool p_static, const Vector3 &p_position, const Vector3 p_direction, const Color &p_color, float p_energy, float p_range, float p_attenuation, float p_spot_angle, float p_spot_attenuation, float p_size, float p_shadow_blur) { void LightmapperRD::add_spot_light(bool p_static, const Vector3 &p_position, const Vector3 p_direction, const Color &p_color, float p_energy, float p_indirect_energy, float p_range, float p_attenuation, float p_spot_angle, float p_spot_attenuation, float p_size, float p_shadow_blur) {
Light l; Light l;
l.type = LIGHT_TYPE_SPOT; l.type = LIGHT_TYPE_SPOT;
l.position[0] = p_position.x; l.position[0] = p_position.x;
@ -106,6 +108,7 @@ void LightmapperRD::add_spot_light(bool p_static, const Vector3 &p_position, con
l.color[1] = p_color.g; l.color[1] = p_color.g;
l.color[2] = p_color.b; l.color[2] = p_color.b;
l.energy = p_energy; l.energy = p_energy;
l.indirect_energy = p_indirect_energy;
l.static_bake = p_static; l.static_bake = p_static;
l.size = p_size; l.size = p_size;
l.shadow_blur = p_shadow_blur; l.shadow_blur = p_shadow_blur;
@ -869,7 +872,7 @@ LightmapperRD::BakeError LightmapperRD::_denoise(RenderingDevice *p_rd, Ref<RDSh
return BAKE_OK; return BAKE_OK;
} }
LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_denoiser, float p_denoiser_strength, int p_bounces, float p_bias, int p_max_texture_size, bool p_bake_sh, GenerateProbes p_generate_probes, const Ref<Image> &p_environment_panorama, const Basis &p_environment_transform, BakeStepFunc p_step_function, void *p_bake_userdata, float p_exposure_normalization) { LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_denoiser, float p_denoiser_strength, int p_bounces, float p_bounce_indirect_energy, float p_bias, int p_max_texture_size, bool p_bake_sh, bool p_texture_for_bounces, GenerateProbes p_generate_probes, const Ref<Image> &p_environment_panorama, const Basis &p_environment_transform, BakeStepFunc p_step_function, void *p_bake_userdata, float p_exposure_normalization) {
int denoiser = GLOBAL_GET("rendering/lightmapping/denoising/denoiser"); int denoiser = GLOBAL_GET("rendering/lightmapping/denoising/denoiser");
String oidn_path = EDITOR_GET("filesystem/tools/oidn/oidn_denoise_path"); String oidn_path = EDITOR_GET("filesystem/tools/oidn/oidn_denoise_path");
@ -924,19 +927,17 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d
RID light_dest_tex; RID light_dest_tex;
RID light_accum_tex; RID light_accum_tex;
RID light_accum_tex2; RID light_accum_tex2;
RID light_primary_dynamic_tex;
RID light_environment_tex; RID light_environment_tex;
#define FREE_TEXTURES \ #define FREE_TEXTURES \
rd->free(albedo_array_tex); \ rd->free(albedo_array_tex); \
rd->free(emission_array_tex); \ rd->free(emission_array_tex); \
rd->free(normal_tex); \ rd->free(normal_tex); \
rd->free(position_tex); \ rd->free(position_tex); \
rd->free(unocclude_tex); \ rd->free(unocclude_tex); \
rd->free(light_source_tex); \ rd->free(light_source_tex); \
rd->free(light_accum_tex2); \ rd->free(light_accum_tex2); \
rd->free(light_accum_tex); \ rd->free(light_accum_tex); \
rd->free(light_primary_dynamic_tex); \
rd->free(light_environment_tex); rd->free(light_environment_tex);
{ // create all textures { // create all textures
@ -974,8 +975,6 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d
light_source_tex = rd->texture_create(tf, RD::TextureView()); light_source_tex = rd->texture_create(tf, RD::TextureView());
rd->texture_clear(light_source_tex, Color(0, 0, 0, 0), 0, 1, 0, atlas_slices); rd->texture_clear(light_source_tex, Color(0, 0, 0, 0), 0, 1, 0, atlas_slices);
light_primary_dynamic_tex = rd->texture_create(tf, RD::TextureView());
rd->texture_clear(light_primary_dynamic_tex, Color(0, 0, 0, 0), 0, 1, 0, atlas_slices);
if (p_bake_sh) { if (p_bake_sh) {
tf.array_layers *= 4; tf.array_layers *= 4;
@ -1017,6 +1016,7 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d
/* STEP 2: create the acceleration structure for the GPU*/ /* STEP 2: create the acceleration structure for the GPU*/
Vector<int> slice_triangle_count; Vector<int> slice_triangle_count;
RID bake_parameters_buffer;
RID vertex_buffer; RID vertex_buffer;
RID triangle_buffer; RID triangle_buffer;
RID lights_buffer; RID lights_buffer;
@ -1028,6 +1028,7 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d
Vector<int> slice_seam_count; Vector<int> slice_seam_count;
#define FREE_BUFFERS \ #define FREE_BUFFERS \
rd->free(bake_parameters_buffer); \
rd->free(vertex_buffer); \ rd->free(vertex_buffer); \
rd->free(triangle_buffer); \ rd->free(triangle_buffer); \
rd->free(lights_buffer); \ rd->free(lights_buffer); \
@ -1038,6 +1039,41 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d
_create_acceleration_structures(rd, atlas_size, atlas_slices, bounds, grid_size, probe_positions, p_generate_probes, slice_triangle_count, slice_seam_count, vertex_buffer, triangle_buffer, lights_buffer, triangle_cell_indices_buffer, probe_positions_buffer, grid_texture, seams_buffer, p_step_function, p_bake_userdata); _create_acceleration_structures(rd, atlas_size, atlas_slices, bounds, grid_size, probe_positions, p_generate_probes, slice_triangle_count, slice_seam_count, vertex_buffer, triangle_buffer, lights_buffer, triangle_cell_indices_buffer, probe_positions_buffer, grid_texture, seams_buffer, p_step_function, p_bake_userdata);
// Create global bake parameters buffer.
BakeParameters bake_parameters;
bake_parameters.world_size[0] = bounds.size.x;
bake_parameters.world_size[1] = bounds.size.y;
bake_parameters.world_size[2] = bounds.size.z;
bake_parameters.bias = p_bias;
bake_parameters.to_cell_offset[0] = bounds.position.x;
bake_parameters.to_cell_offset[1] = bounds.position.y;
bake_parameters.to_cell_offset[2] = bounds.position.z;
bake_parameters.grid_size = grid_size;
bake_parameters.to_cell_size[0] = (1.0 / bounds.size.x) * float(grid_size);
bake_parameters.to_cell_size[1] = (1.0 / bounds.size.y) * float(grid_size);
bake_parameters.to_cell_size[2] = (1.0 / bounds.size.z) * float(grid_size);
bake_parameters.light_count = lights.size();
bake_parameters.env_transform[0] = p_environment_transform.rows[0][0];
bake_parameters.env_transform[1] = p_environment_transform.rows[1][0];
bake_parameters.env_transform[2] = p_environment_transform.rows[2][0];
bake_parameters.env_transform[3] = 0.0f;
bake_parameters.env_transform[4] = p_environment_transform.rows[0][1];
bake_parameters.env_transform[5] = p_environment_transform.rows[1][1];
bake_parameters.env_transform[6] = p_environment_transform.rows[2][1];
bake_parameters.env_transform[7] = 0.0f;
bake_parameters.env_transform[8] = p_environment_transform.rows[0][2];
bake_parameters.env_transform[9] = p_environment_transform.rows[1][2];
bake_parameters.env_transform[10] = p_environment_transform.rows[2][2];
bake_parameters.env_transform[11] = 0.0f;
bake_parameters.atlas_size[0] = atlas_size.width;
bake_parameters.atlas_size[1] = atlas_size.height;
bake_parameters.exposure_normalization = p_exposure_normalization;
bake_parameters.bounces = p_bounces;
bake_parameters.bounce_indirect_energy = p_bounce_indirect_energy;
bake_parameters_buffer = rd->uniform_buffer_create(sizeof(BakeParameters));
rd->buffer_update(bake_parameters_buffer, 0, sizeof(BakeParameters), &bake_parameters);
if (p_step_function) { if (p_step_function) {
p_step_function(0.47, RTR("Preparing shaders"), p_bake_userdata, true); p_step_function(0.47, RTR("Preparing shaders"), p_bake_userdata, true);
} }
@ -1072,6 +1108,13 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d
Vector<RD::Uniform> base_uniforms; Vector<RD::Uniform> base_uniforms;
{ {
{
RD::Uniform u;
u.uniform_type = RD::UNIFORM_TYPE_UNIFORM_BUFFER;
u.binding = 0;
u.append_id(bake_parameters_buffer);
base_uniforms.push_back(u);
}
{ {
RD::Uniform u; RD::Uniform u;
u.uniform_type = RD::UNIFORM_TYPE_STORAGE_BUFFER; u.uniform_type = RD::UNIFORM_TYPE_STORAGE_BUFFER;
@ -1186,8 +1229,17 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d
/* Plot direct light */ /* Plot direct light */
Ref<RDShaderFile> compute_shader; Ref<RDShaderFile> compute_shader;
String defines = "";
if (p_bake_sh) {
defines += "\n#define USE_SH_LIGHTMAPS\n";
}
if (p_texture_for_bounces) {
defines += "\n#define USE_LIGHT_TEXTURE_FOR_BOUNCES\n";
}
compute_shader.instantiate(); compute_shader.instantiate();
err = compute_shader->parse_versions_from_text(lm_compute_shader_glsl, p_bake_sh ? "\n#define USE_SH_LIGHTMAPS\n" : ""); err = compute_shader->parse_versions_from_text(lm_compute_shader_glsl, defines);
if (err != OK) { if (err != OK) {
FREE_TEXTURES FREE_TEXTURES
FREE_BUFFERS FREE_BUFFERS
@ -1225,40 +1277,6 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d
rd->free(compute_shader_secondary); \ rd->free(compute_shader_secondary); \
rd->free(compute_shader_light_probes); rd->free(compute_shader_light_probes);
PushConstant push_constant;
{
//set defaults
push_constant.atlas_size[0] = atlas_size.width;
push_constant.atlas_size[1] = atlas_size.height;
push_constant.world_size[0] = bounds.size.x;
push_constant.world_size[1] = bounds.size.y;
push_constant.world_size[2] = bounds.size.z;
push_constant.to_cell_offset[0] = bounds.position.x;
push_constant.to_cell_offset[1] = bounds.position.y;
push_constant.to_cell_offset[2] = bounds.position.z;
push_constant.bias = p_bias;
push_constant.to_cell_size[0] = (1.0 / bounds.size.x) * float(grid_size);
push_constant.to_cell_size[1] = (1.0 / bounds.size.y) * float(grid_size);
push_constant.to_cell_size[2] = (1.0 / bounds.size.z) * float(grid_size);
push_constant.light_count = lights.size();
push_constant.grid_size = grid_size;
push_constant.atlas_slice = 0;
push_constant.region_ofs[0] = 0;
push_constant.region_ofs[1] = 0;
push_constant.environment_xform[0] = p_environment_transform.rows[0][0];
push_constant.environment_xform[1] = p_environment_transform.rows[1][0];
push_constant.environment_xform[2] = p_environment_transform.rows[2][0];
push_constant.environment_xform[3] = 0;
push_constant.environment_xform[4] = p_environment_transform.rows[0][1];
push_constant.environment_xform[5] = p_environment_transform.rows[1][1];
push_constant.environment_xform[6] = p_environment_transform.rows[2][1];
push_constant.environment_xform[7] = 0;
push_constant.environment_xform[8] = p_environment_transform.rows[0][2];
push_constant.environment_xform[9] = p_environment_transform.rows[1][2];
push_constant.environment_xform[10] = p_environment_transform.rows[2][2];
push_constant.environment_xform[11] = 0;
}
Vector3i group_size((atlas_size.x - 1) / 8 + 1, (atlas_size.y - 1) / 8 + 1, 1); Vector3i group_size((atlas_size.x - 1) / 8 + 1, (atlas_size.y - 1) / 8 + 1, 1);
rd->submit(); rd->submit();
rd->sync(); rd->sync();
@ -1267,6 +1285,8 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d
p_step_function(0.49, RTR("Un-occluding geometry"), p_bake_userdata, true); p_step_function(0.49, RTR("Un-occluding geometry"), p_bake_userdata, true);
} }
PushConstant push_constant;
/* UNOCCLUDE */ /* UNOCCLUDE */
{ {
Vector<RD::Uniform> uniforms; Vector<RD::Uniform> uniforms;
@ -1307,6 +1327,24 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d
p_step_function(0.5, RTR("Plot direct lighting"), p_bake_userdata, true); p_step_function(0.5, RTR("Plot direct lighting"), p_bake_userdata, true);
} }
// Set ray count to the quality used for direct light and bounces.
switch (p_quality) {
case BAKE_QUALITY_LOW: {
push_constant.ray_count = GLOBAL_GET("rendering/lightmapping/bake_quality/low_quality_ray_count");
} break;
case BAKE_QUALITY_MEDIUM: {
push_constant.ray_count = GLOBAL_GET("rendering/lightmapping/bake_quality/medium_quality_ray_count");
} break;
case BAKE_QUALITY_HIGH: {
push_constant.ray_count = GLOBAL_GET("rendering/lightmapping/bake_quality/high_quality_ray_count");
} break;
case BAKE_QUALITY_ULTRA: {
push_constant.ray_count = GLOBAL_GET("rendering/lightmapping/bake_quality/ultra_quality_ray_count");
} break;
}
push_constant.ray_count = CLAMP(push_constant.ray_count, 16u, 8192u);
/* PRIMARY (direct) LIGHT PASS */ /* PRIMARY (direct) LIGHT PASS */
{ {
Vector<RD::Uniform> uniforms; Vector<RD::Uniform> uniforms;
@ -1346,41 +1384,15 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d
u.append_id(light_accum_tex); u.append_id(light_accum_tex);
uniforms.push_back(u); uniforms.push_back(u);
} }
{
RD::Uniform u;
u.uniform_type = RD::UNIFORM_TYPE_IMAGE;
u.binding = 5;
u.append_id(light_primary_dynamic_tex);
uniforms.push_back(u);
}
} }
RID light_uniform_set = rd->uniform_set_create(uniforms, compute_shader_primary, 1); RID light_uniform_set = rd->uniform_set_create(uniforms, compute_shader_primary, 1);
switch (p_quality) {
case BAKE_QUALITY_LOW: {
push_constant.ray_count = GLOBAL_GET("rendering/lightmapping/bake_quality/low_quality_ray_count");
} break;
case BAKE_QUALITY_MEDIUM: {
push_constant.ray_count = GLOBAL_GET("rendering/lightmapping/bake_quality/medium_quality_ray_count");
} break;
case BAKE_QUALITY_HIGH: {
push_constant.ray_count = GLOBAL_GET("rendering/lightmapping/bake_quality/high_quality_ray_count");
} break;
case BAKE_QUALITY_ULTRA: {
push_constant.ray_count = GLOBAL_GET("rendering/lightmapping/bake_quality/ultra_quality_ray_count");
} break;
}
push_constant.ray_count = CLAMP(push_constant.ray_count, 16u, 8192u);
RD::ComputeListID compute_list = rd->compute_list_begin(); RD::ComputeListID compute_list = rd->compute_list_begin();
rd->compute_list_bind_compute_pipeline(compute_list, compute_shader_primary_pipeline); rd->compute_list_bind_compute_pipeline(compute_list, compute_shader_primary_pipeline);
rd->compute_list_bind_uniform_set(compute_list, compute_base_uniform_set, 0); rd->compute_list_bind_uniform_set(compute_list, compute_base_uniform_set, 0);
rd->compute_list_bind_uniform_set(compute_list, light_uniform_set, 1); rd->compute_list_bind_uniform_set(compute_list, light_uniform_set, 1);
push_constant.environment_xform[11] = p_exposure_normalization;
for (int i = 0; i < atlas_slices; i++) { for (int i = 0; i < atlas_slices; i++) {
push_constant.atlas_slice = i; push_constant.atlas_slice = i;
rd->compute_list_set_push_constant(compute_list, &push_constant, sizeof(PushConstant)); rd->compute_list_set_push_constant(compute_list, &push_constant, sizeof(PushConstant));
@ -1388,8 +1400,6 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d
//no barrier, let them run all together //no barrier, let them run all together
} }
rd->compute_list_end(); //done rd->compute_list_end(); //done
push_constant.environment_xform[11] = 0.0;
} }
#ifdef DEBUG_TEXTURES #ifdef DEBUG_TEXTURES
@ -1410,6 +1420,7 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d
Vector<RD::Uniform> uniforms; Vector<RD::Uniform> uniforms;
{ {
{ {
// Unused.
RD::Uniform u; RD::Uniform u;
u.uniform_type = RD::UNIFORM_TYPE_IMAGE; u.uniform_type = RD::UNIFORM_TYPE_IMAGE;
u.binding = 0; u.binding = 0;
@ -1444,95 +1455,71 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d
u.append_id(light_accum_tex); u.append_id(light_accum_tex);
uniforms.push_back(u); uniforms.push_back(u);
} }
{
RD::Uniform u;
u.uniform_type = RD::UNIFORM_TYPE_IMAGE;
u.binding = 5;
u.append_id(unocclude_tex); //reuse unocclude tex
uniforms.push_back(u);
}
{ {
RD::Uniform u; RD::Uniform u;
u.uniform_type = RD::UNIFORM_TYPE_TEXTURE; u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
u.binding = 6; u.binding = 5;
u.append_id(light_environment_tex); u.append_id(light_environment_tex);
uniforms.push_back(u); uniforms.push_back(u);
} }
} }
RID secondary_uniform_set[2]; RID secondary_uniform_set;
secondary_uniform_set[0] = rd->uniform_set_create(uniforms, compute_shader_secondary, 1); secondary_uniform_set = rd->uniform_set_create(uniforms, compute_shader_secondary, 1);
uniforms.write[0].set_id(0, light_source_tex);
uniforms.write[1].set_id(0, light_dest_tex);
secondary_uniform_set[1] = rd->uniform_set_create(uniforms, compute_shader_secondary, 1);
int max_region_size = nearest_power_of_2_templated(int(GLOBAL_GET("rendering/lightmapping/bake_performance/region_size"))); int max_region_size = nearest_power_of_2_templated(int(GLOBAL_GET("rendering/lightmapping/bake_performance/region_size")));
int max_rays = GLOBAL_GET("rendering/lightmapping/bake_performance/max_rays_per_pass"); int max_rays = GLOBAL_GET("rendering/lightmapping/bake_performance/max_rays_per_pass");
int x_regions = (atlas_size.width - 1) / max_region_size + 1; int x_regions = (atlas_size.width - 1) / max_region_size + 1;
int y_regions = (atlas_size.height - 1) / max_region_size + 1; int y_regions = (atlas_size.height - 1) / max_region_size + 1;
int ray_iterations = (push_constant.ray_count - 1) / max_rays + 1; int ray_iterations = (push_constant.ray_count - 1) / max_rays + 1;
rd->submit(); rd->submit();
rd->sync(); rd->sync();
for (int b = 0; b < p_bounces; b++) { int count = 0;
int count = 0; for (int s = 0; s < atlas_slices; s++) {
if (b > 0) { push_constant.atlas_slice = s;
SWAP(light_source_tex, light_dest_tex);
SWAP(secondary_uniform_set[0], secondary_uniform_set[1]);
}
for (int s = 0; s < atlas_slices; s++) { for (int i = 0; i < x_regions; i++) {
push_constant.atlas_slice = s; for (int j = 0; j < y_regions; j++) {
int x = i * max_region_size;
int y = j * max_region_size;
int w = MIN((i + 1) * max_region_size, atlas_size.width) - x;
int h = MIN((j + 1) * max_region_size, atlas_size.height) - y;
for (int i = 0; i < x_regions; i++) { push_constant.region_ofs[0] = x;
for (int j = 0; j < y_regions; j++) { push_constant.region_ofs[1] = y;
int x = i * max_region_size;
int y = j * max_region_size;
int w = MIN((i + 1) * max_region_size, atlas_size.width) - x;
int h = MIN((j + 1) * max_region_size, atlas_size.height) - y;
push_constant.region_ofs[0] = x; group_size = Vector3i((w - 1) / 8 + 1, (h - 1) / 8 + 1, 1);
push_constant.region_ofs[1] = y;
group_size = Vector3i((w - 1) / 8 + 1, (h - 1) / 8 + 1, 1); for (int k = 0; k < ray_iterations; k++) {
RD::ComputeListID compute_list = rd->compute_list_begin();
rd->compute_list_bind_compute_pipeline(compute_list, compute_shader_secondary_pipeline);
rd->compute_list_bind_uniform_set(compute_list, compute_base_uniform_set, 0);
rd->compute_list_bind_uniform_set(compute_list, secondary_uniform_set, 1);
for (int k = 0; k < ray_iterations; k++) { push_constant.ray_from = k * max_rays;
RD::ComputeListID compute_list = rd->compute_list_begin(); push_constant.ray_to = MIN((k + 1) * max_rays, int32_t(push_constant.ray_count));
rd->compute_list_bind_compute_pipeline(compute_list, compute_shader_secondary_pipeline); rd->compute_list_set_push_constant(compute_list, &push_constant, sizeof(PushConstant));
rd->compute_list_bind_uniform_set(compute_list, compute_base_uniform_set, 0); rd->compute_list_dispatch(compute_list, group_size.x, group_size.y, group_size.z);
rd->compute_list_bind_uniform_set(compute_list, secondary_uniform_set[0], 1);
push_constant.ray_from = k * max_rays; rd->compute_list_end();
push_constant.ray_to = MIN((k + 1) * max_rays, int32_t(push_constant.ray_count)); rd->submit();
rd->compute_list_set_push_constant(compute_list, &push_constant, sizeof(PushConstant)); rd->sync();
rd->compute_list_dispatch(compute_list, group_size.x, group_size.y, group_size.z);
rd->compute_list_end(); //done count++;
rd->submit(); if (p_step_function) {
rd->sync(); int total = (atlas_slices * x_regions * y_regions * ray_iterations);
int percent = count * 100 / total;
count++; float p = float(count) / total * 0.1;
if (p_step_function) { p_step_function(0.6 + p, vformat(RTR("Integrate indirect lighting %d%%"), percent), p_bake_userdata, false);
int total = (atlas_slices * x_regions * y_regions * ray_iterations);
int percent = count * 100 / total;
float p = float(count) / total * 0.1;
p_step_function(0.6 + p, vformat(RTR("Bounce %d/%d: Integrate indirect lighting %d%%"), b + 1, p_bounces, percent), p_bake_userdata, false);
}
} }
} }
} }
} }
if (b == 0) {
// This disables the environment for subsequent bounces
push_constant.environment_xform[3] = -99.0f;
}
} }
// Restore the correct environment transform
push_constant.environment_xform[3] = 0.0f;
} }
/* LIGHTPROBES */ /* LIGHTPROBES */
@ -1559,20 +1546,13 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d
RD::Uniform u; RD::Uniform u;
u.uniform_type = RD::UNIFORM_TYPE_TEXTURE; u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
u.binding = 1; u.binding = 1;
u.append_id(light_dest_tex); u.append_id(light_source_tex);
uniforms.push_back(u); uniforms.push_back(u);
} }
{ {
RD::Uniform u; RD::Uniform u;
u.uniform_type = RD::UNIFORM_TYPE_TEXTURE; u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
u.binding = 2; u.binding = 2;
u.append_id(light_primary_dynamic_tex);
uniforms.push_back(u);
}
{
RD::Uniform u;
u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
u.binding = 3;
u.append_id(light_environment_tex); u.append_id(light_environment_tex);
uniforms.push_back(u); uniforms.push_back(u);
} }
@ -1594,8 +1574,8 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d
} break; } break;
} }
push_constant.atlas_size[0] = probe_positions.size();
push_constant.ray_count = CLAMP(push_constant.ray_count, 16u, 8192u); push_constant.ray_count = CLAMP(push_constant.ray_count, 16u, 8192u);
push_constant.probe_count = probe_positions.size();
int max_rays = GLOBAL_GET("rendering/lightmapping/bake_performance/max_rays_per_probe_pass"); int max_rays = GLOBAL_GET("rendering/lightmapping/bake_performance/max_rays_per_probe_pass");
int ray_iterations = (push_constant.ray_count - 1) / max_rays + 1; int ray_iterations = (push_constant.ray_count - 1) / max_rays + 1;
@ -1621,8 +1601,6 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d
p_step_function(0.7 + p, vformat(RTR("Integrating light probes %d%%"), percent), p_bake_userdata, false); p_step_function(0.7 + p, vformat(RTR("Integrating light probes %d%%"), percent), p_bake_userdata, false);
} }
} }
push_constant.atlas_size[0] = atlas_size.x; //restore
} }
#if 0 #if 0

View File

@ -40,6 +40,26 @@ class RDShaderFile;
class LightmapperRD : public Lightmapper { class LightmapperRD : public Lightmapper {
GDCLASS(LightmapperRD, Lightmapper) GDCLASS(LightmapperRD, Lightmapper)
struct BakeParameters {
float world_size[3] = {};
float bias = 0.0;
float to_cell_offset[3] = {};
int32_t grid_size = 0;
float to_cell_size[3] = {};
uint32_t light_count = 0;
float env_transform[12] = {};
int32_t atlas_size[2] = {};
float exposure_normalization = 0.0f;
uint32_t bounces = 0;
float bounce_indirect_energy = 0.0f;
uint32_t pad[3] = {};
};
struct MeshInstance { struct MeshInstance {
MeshData data; MeshData data;
int slice = 0; int slice = 0;
@ -57,9 +77,10 @@ class LightmapperRD : public Lightmapper {
float attenuation = 0.0; float attenuation = 0.0;
float cos_spot_angle = 0.0; float cos_spot_angle = 0.0;
float inv_spot_attenuation = 0.0; float inv_spot_attenuation = 0.0;
float indirect_energy = 0.0;
float shadow_blur = 0.0; float shadow_blur = 0.0;
uint32_t static_bake = 0; uint32_t static_bake = 0;
uint32_t pad[2] = {}; uint32_t pad = 0;
bool operator<(const Light &p_light) const { bool operator<(const Light &p_light) const {
return type < p_light.type; return type < p_light.type;
@ -206,24 +227,13 @@ class LightmapperRD : public Lightmapper {
}; };
struct PushConstant { struct PushConstant {
int32_t atlas_size[2] = {}; uint32_t atlas_slice = 0;
uint32_t ray_count = 0; uint32_t ray_count = 0;
uint32_t ray_to = 0;
float world_size[3] = {};
float bias = 0.0;
float to_cell_offset[3] = {};
uint32_t ray_from = 0; uint32_t ray_from = 0;
uint32_t ray_to = 0;
float to_cell_size[3] = {}; uint32_t region_ofs[2] = {};
uint32_t light_count = 0; uint32_t probe_count = 0;
uint32_t pad = 0;
int32_t grid_size = 0;
int32_t atlas_slice = 0;
int32_t region_ofs[2] = {};
float environment_xform[12] = {};
}; };
Vector<Ref<Image>> bake_textures; Vector<Ref<Image>> bake_textures;
@ -252,11 +262,11 @@ class LightmapperRD : public Lightmapper {
public: public:
virtual void add_mesh(const MeshData &p_mesh) override; virtual void add_mesh(const MeshData &p_mesh) override;
virtual void add_directional_light(bool p_static, const Vector3 &p_direction, const Color &p_color, float p_energy, float p_angular_distance, float p_shadow_blur) override; virtual void add_directional_light(bool p_static, const Vector3 &p_direction, const Color &p_color, float p_energy, float p_indirect_energy, float p_angular_distance, float p_shadow_blur) override;
virtual void add_omni_light(bool p_static, const Vector3 &p_position, const Color &p_color, float p_energy, float p_range, float p_attenuation, float p_size, float p_shadow_blur) override; virtual void add_omni_light(bool p_static, const Vector3 &p_position, const Color &p_color, float p_energy, float p_indirect_energy, float p_range, float p_attenuation, float p_size, float p_shadow_blur) override;
virtual void add_spot_light(bool p_static, const Vector3 &p_position, const Vector3 p_direction, const Color &p_color, float p_energy, float p_range, float p_attenuation, float p_spot_angle, float p_spot_attenuation, float p_size, float p_shadow_blur) override; virtual void add_spot_light(bool p_static, const Vector3 &p_position, const Vector3 p_direction, const Color &p_color, float p_energy, float p_indirect_energy, float p_range, float p_attenuation, float p_spot_angle, float p_spot_attenuation, float p_size, float p_shadow_blur) override;
virtual void add_probe(const Vector3 &p_position) override; virtual void add_probe(const Vector3 &p_position) override;
virtual BakeError bake(BakeQuality p_quality, bool p_use_denoiser, float p_denoiser_strength, int p_bounces, float p_bias, int p_max_texture_size, bool p_bake_sh, GenerateProbes p_generate_probes, const Ref<Image> &p_environment_panorama, const Basis &p_environment_transform, BakeStepFunc p_step_function = nullptr, void *p_bake_userdata = nullptr, float p_exposure_normalization = 1.0) override; virtual BakeError bake(BakeQuality p_quality, bool p_use_denoiser, float p_denoiser_strength, int p_bounces, float p_bounce_indirect_energy, float p_bias, int p_max_texture_size, bool p_bake_sh, bool p_texture_for_bounces, GenerateProbes p_generate_probes, const Ref<Image> &p_environment_panorama, const Basis &p_environment_transform, BakeStepFunc p_step_function = nullptr, void *p_bake_userdata = nullptr, float p_exposure_normalization = 1.0) override;
int get_bake_texture_count() const override; int get_bake_texture_count() const override;
Ref<Image> get_bake_texture(int p_index) const override; Ref<Image> get_bake_texture(int p_index) const override;

View File

@ -1,6 +1,26 @@
/* SET 0, static data that does not change between any call */ /* SET 0, static data that does not change between any call */
layout(set = 0, binding = 0) uniform BakeParameters {
vec3 world_size;
float bias;
vec3 to_cell_offset;
int grid_size;
vec3 to_cell_size;
uint light_count;
mat3x4 env_transform;
ivec2 atlas_size;
float exposure_normalization;
uint bounces;
float bounce_indirect_energy;
}
bake_params;
struct Vertex { struct Vertex {
vec3 position; vec3 position;
float normal_z; float normal_z;
@ -51,9 +71,10 @@ struct Light {
float cos_spot_angle; float cos_spot_angle;
float inv_spot_attenuation; float inv_spot_attenuation;
float indirect_energy;
float shadow_blur; float shadow_blur;
bool static_bake; bool static_bake;
uint pad[2]; uint pad;
}; };
layout(set = 0, binding = 4, std430) restrict readonly buffer Lights { layout(set = 0, binding = 4, std430) restrict readonly buffer Lights {

View File

@ -37,8 +37,7 @@ layout(set = 1, binding = 0, std430) restrict buffer LightProbeData {
light_probes; light_probes;
layout(set = 1, binding = 1) uniform texture2DArray source_light; layout(set = 1, binding = 1) uniform texture2DArray source_light;
layout(set = 1, binding = 2) uniform texture2DArray source_direct_light; //also need the direct light, which was omitted layout(set = 1, binding = 2) uniform texture2D environment;
layout(set = 1, binding = 3) uniform texture2D environment;
#endif #endif
#ifdef MODE_UNOCCLUDE #ifdef MODE_UNOCCLUDE
@ -59,11 +58,7 @@ layout(rgba16f, set = 1, binding = 4) uniform restrict image2DArray accum_light;
#endif #endif
#ifdef MODE_BOUNCE_LIGHT #ifdef MODE_BOUNCE_LIGHT
layout(rgba32f, set = 1, binding = 5) uniform restrict image2DArray bounce_accum; layout(set = 1, binding = 5) uniform texture2D environment;
layout(set = 1, binding = 6) uniform texture2D environment;
#endif
#ifdef MODE_DIRECT_LIGHT
layout(rgba32f, set = 1, binding = 5) uniform restrict writeonly image2DArray primary_dynamic;
#endif #endif
#if defined(MODE_DILATE) || defined(MODE_DENOISE) #if defined(MODE_DILATE) || defined(MODE_DENOISE)
@ -85,24 +80,13 @@ denoise_params;
#endif #endif
layout(push_constant, std430) uniform Params { layout(push_constant, std430) uniform Params {
ivec2 atlas_size; // x used for light probe mode total probes uint atlas_slice;
uint ray_count; uint ray_count;
uint ray_from;
uint ray_to; uint ray_to;
vec3 world_size;
float bias;
vec3 to_cell_offset;
uint ray_from;
vec3 to_cell_size;
uint light_count;
int grid_size;
int atlas_slice;
ivec2 region_ofs; ivec2 region_ofs;
uint probe_count;
mat3x4 env_transform;
} }
params; params;
@ -127,7 +111,7 @@ bool ray_hits_triangle(vec3 from, vec3 dir, float max_dist, vec3 p0, vec3 p1, ve
r_barycentric.x = 1.0 - (r_barycentric.z + r_barycentric.y); r_barycentric.x = 1.0 - (r_barycentric.z + r_barycentric.y);
r_distance = dot(triangle_normal, e2); r_distance = dot(triangle_normal, e2);
return (r_distance > params.bias) && (r_distance < max_dist) && all(greaterThanEqual(r_barycentric, vec3(0.0))); return (r_distance > bake_params.bias) && (r_distance < max_dist) && all(greaterThanEqual(r_barycentric, vec3(0.0)));
} }
const uint RAY_MISS = 0; const uint RAY_MISS = 0;
@ -135,40 +119,28 @@ const uint RAY_FRONT = 1;
const uint RAY_BACK = 2; const uint RAY_BACK = 2;
const uint RAY_ANY = 3; const uint RAY_ANY = 3;
uint trace_ray(vec3 p_from, vec3 p_to uint trace_ray(vec3 p_from, vec3 p_to, bool p_any_hit, out float r_distance, out vec3 r_normal, out uint r_triangle, out vec3 r_barycentric) {
#if defined(MODE_BOUNCE_LIGHT) || defined(MODE_LIGHT_PROBES) // World coordinates.
,
out uint r_triangle, out vec3 r_barycentric
#endif
#if defined(MODE_UNOCCLUDE)
,
out float r_distance, out vec3 r_normal
#endif
) {
/* world coords */
vec3 rel = p_to - p_from; vec3 rel = p_to - p_from;
float rel_len = length(rel); float rel_len = length(rel);
vec3 dir = normalize(rel); vec3 dir = normalize(rel);
vec3 inv_dir = 1.0 / dir; vec3 inv_dir = 1.0 / dir;
/* cell coords */ // Cell coordinates.
vec3 from_cell = (p_from - bake_params.to_cell_offset) * bake_params.to_cell_size;
vec3 to_cell = (p_to - bake_params.to_cell_offset) * bake_params.to_cell_size;
vec3 from_cell = (p_from - params.to_cell_offset) * params.to_cell_size; // Prepare DDA.
vec3 to_cell = (p_to - params.to_cell_offset) * params.to_cell_size;
//prepare DDA
vec3 rel_cell = to_cell - from_cell; vec3 rel_cell = to_cell - from_cell;
ivec3 icell = ivec3(from_cell); ivec3 icell = ivec3(from_cell);
ivec3 iendcell = ivec3(to_cell); ivec3 iendcell = ivec3(to_cell);
vec3 dir_cell = normalize(rel_cell); vec3 dir_cell = normalize(rel_cell);
vec3 delta = min(abs(1.0 / dir_cell), params.grid_size); // use params.grid_size as max to prevent infinity values vec3 delta = min(abs(1.0 / dir_cell), bake_params.grid_size); // Use bake_params.grid_size as max to prevent infinity values.
ivec3 step = ivec3(sign(rel_cell)); ivec3 step = ivec3(sign(rel_cell));
vec3 side = (sign(rel_cell) * (vec3(icell) - from_cell) + (sign(rel_cell) * 0.5) + 0.5) * delta; vec3 side = (sign(rel_cell) * (vec3(icell) - from_cell) + (sign(rel_cell) * 0.5) + 0.5) * delta;
uint iters = 0; uint iters = 0;
while (all(greaterThanEqual(icell, ivec3(0))) && all(lessThan(icell, ivec3(params.grid_size))) && iters < 1000) { while (all(greaterThanEqual(icell, ivec3(0))) && all(lessThan(icell, ivec3(bake_params.grid_size))) && (iters < 1000)) {
uvec2 cell_data = texelFetch(usampler3D(grid, linear_sampler), icell, 0).xy; uvec2 cell_data = texelFetch(usampler3D(grid, linear_sampler), icell, 0).xy;
if (cell_data.x > 0) { //triangles here if (cell_data.x > 0) { //triangles here
uint hit = RAY_MISS; uint hit = RAY_MISS;
@ -177,70 +149,58 @@ uint trace_ray(vec3 p_from, vec3 p_to
for (uint i = 0; i < cell_data.x; i++) { for (uint i = 0; i < cell_data.x; i++) {
uint tidx = grid_indices.data[cell_data.y + i]; uint tidx = grid_indices.data[cell_data.y + i];
//Ray-Box test // Ray-Box test.
Triangle triangle = triangles.data[tidx]; Triangle triangle = triangles.data[tidx];
vec3 t0 = (triangle.min_bounds - p_from) * inv_dir; vec3 t0 = (triangle.min_bounds - p_from) * inv_dir;
vec3 t1 = (triangle.max_bounds - p_from) * inv_dir; vec3 t1 = (triangle.max_bounds - p_from) * inv_dir;
vec3 tmin = min(t0, t1), tmax = max(t0, t1); vec3 tmin = min(t0, t1), tmax = max(t0, t1);
if (max(tmin.x, max(tmin.y, tmin.z)) > min(tmax.x, min(tmax.y, tmax.z))) { if (max(tmin.x, max(tmin.y, tmin.z)) > min(tmax.x, min(tmax.y, tmax.z))) {
continue; //ray box failed continue; // Ray-Box test failed.
} }
//prepare triangle vertices // Prepare triangle vertices.
vec3 vtx0 = vertices.data[triangle.indices.x].position; vec3 vtx0 = vertices.data[triangle.indices.x].position;
vec3 vtx1 = vertices.data[triangle.indices.y].position; vec3 vtx1 = vertices.data[triangle.indices.y].position;
vec3 vtx2 = vertices.data[triangle.indices.z].position; vec3 vtx2 = vertices.data[triangle.indices.z].position;
#if defined(MODE_UNOCCLUDE) || defined(MODE_BOUNCE_LIGHT) || defined(MODE_LIGHT_PROBES)
vec3 normal = -normalize(cross((vtx0 - vtx1), (vtx0 - vtx2))); vec3 normal = -normalize(cross((vtx0 - vtx1), (vtx0 - vtx2)));
bool backface = dot(normal, dir) >= 0.0; bool backface = dot(normal, dir) >= 0.0;
#endif
float distance; float distance;
vec3 barycentric; vec3 barycentric;
if (ray_hits_triangle(p_from, dir, rel_len, vtx0, vtx1, vtx2, distance, barycentric)) { if (ray_hits_triangle(p_from, dir, rel_len, vtx0, vtx1, vtx2, distance, barycentric)) {
#ifdef MODE_DIRECT_LIGHT if (p_any_hit) {
return RAY_ANY; //any hit good // Return early if any hit was requested.
#endif return RAY_ANY;
}
vec3 position = p_from + dir * distance; vec3 position = p_from + dir * distance;
vec3 hit_cell = (position - params.to_cell_offset) * params.to_cell_size; vec3 hit_cell = (position - bake_params.to_cell_offset) * bake_params.to_cell_size;
if (icell != ivec3(hit_cell)) { if (icell != ivec3(hit_cell)) {
// It's possible for the ray to hit a triangle in a position outside the bounds of the cell // It's possible for the ray to hit a triangle in a position outside the bounds of the cell
// if it's large enough to cover multiple ones. The hit must be ignored if this is the case. // if it's large enough to cover multiple ones. The hit must be ignored if this is the case.
continue; continue;
} }
#if defined(MODE_UNOCCLUDE) || defined(MODE_BOUNCE_LIGHT) || defined(MODE_LIGHT_PROBES)
if (!backface) { if (!backface) {
// the case of meshes having both a front and back face in the same plane is more common than // The case of meshes having both a front and back face in the same plane is more common than expected.
// expected, so if this is a front-face, bias it closer to the ray origin, so it always wins over the back-face // If this is a front-face, bias it closer to the ray origin, so it always wins over the back-face.
distance = max(params.bias, distance - params.bias); distance = max(bake_params.bias, distance - bake_params.bias);
} }
if (distance < best_distance) { if (distance < best_distance) {
hit = backface ? RAY_BACK : RAY_FRONT; hit = backface ? RAY_BACK : RAY_FRONT;
best_distance = distance; best_distance = distance;
#if defined(MODE_UNOCCLUDE)
r_distance = distance; r_distance = distance;
r_normal = normal; r_normal = normal;
#endif
#if defined(MODE_BOUNCE_LIGHT) || defined(MODE_LIGHT_PROBES)
r_triangle = tidx; r_triangle = tidx;
r_barycentric = barycentric; r_barycentric = barycentric;
#endif
} }
#endif
} }
} }
#if defined(MODE_UNOCCLUDE) || defined(MODE_BOUNCE_LIGHT) || defined(MODE_LIGHT_PROBES)
if (hit != RAY_MISS) { if (hit != RAY_MISS) {
return hit; return hit;
} }
#endif
} }
if (icell == iendcell) { if (icell == iendcell) {
@ -250,13 +210,32 @@ uint trace_ray(vec3 p_from, vec3 p_to
bvec3 mask = lessThanEqual(side.xyz, min(side.yzx, side.zxy)); bvec3 mask = lessThanEqual(side.xyz, min(side.yzx, side.zxy));
side += vec3(mask) * delta; side += vec3(mask) * delta;
icell += ivec3(vec3(mask)) * step; icell += ivec3(vec3(mask)) * step;
iters++; iters++;
} }
return RAY_MISS; return RAY_MISS;
} }
uint trace_ray_closest_hit_triangle(vec3 p_from, vec3 p_to, out uint r_triangle, out vec3 r_barycentric) {
float distance;
vec3 normal;
return trace_ray(p_from, p_to, false, distance, normal, r_triangle, r_barycentric);
}
uint trace_ray_closest_hit_distance(vec3 p_from, vec3 p_to, out float r_distance, out vec3 r_normal) {
uint triangle;
vec3 barycentric;
return trace_ray(p_from, p_to, false, r_distance, r_normal, triangle, barycentric);
}
uint trace_ray_any_hit(vec3 p_from, vec3 p_to) {
float distance;
vec3 normal;
uint triangle;
vec3 barycentric;
return trace_ray(p_from, p_to, true, distance, normal, triangle, barycentric);
}
// https://www.reedbeta.com/blog/hash-functions-for-gpu-rendering/ // https://www.reedbeta.com/blog/hash-functions-for-gpu-rendering/
uint hash(uint value) { uint hash(uint value) {
uint state = value * 747796405u + 2891336453u; uint state = value * 747796405u + 2891336453u;
@ -277,14 +256,6 @@ float randomize(inout uint value) {
const float PI = 3.14159265f; const float PI = 3.14159265f;
// http://www.realtimerendering.com/raytracinggems/unofficial_RayTracingGems_v1.4.pdf (chapter 15) // http://www.realtimerendering.com/raytracinggems/unofficial_RayTracingGems_v1.4.pdf (chapter 15)
vec3 generate_hemisphere_uniform_direction(inout uint noise) {
float noise1 = randomize(noise);
float noise2 = randomize(noise) * 2.0 * PI;
float factor = sqrt(1 - (noise1 * noise1));
return vec3(factor * cos(noise2), factor * sin(noise2), noise1);
}
vec3 generate_hemisphere_cosine_weighted_direction(inout uint noise) { vec3 generate_hemisphere_cosine_weighted_direction(inout uint noise) {
float noise1 = randomize(noise); float noise1 = randomize(noise);
float noise2 = randomize(noise) * 2.0 * PI; float noise2 = randomize(noise) * 2.0 * PI;
@ -292,6 +263,24 @@ vec3 generate_hemisphere_cosine_weighted_direction(inout uint noise) {
return vec3(sqrt(noise1) * cos(noise2), sqrt(noise1) * sin(noise2), sqrt(1.0 - noise1)); return vec3(sqrt(noise1) * cos(noise2), sqrt(noise1) * sin(noise2), sqrt(1.0 - noise1));
} }
// Distribution generation adapted from "Generating uniformly distributed numbers on a sphere"
// <http://corysimon.github.io/articles/uniformdistn-on-sphere/>
vec3 generate_sphere_uniform_direction(inout uint noise) {
float theta = 2.0 * PI * randomize(noise);
float phi = acos(1.0 - 2.0 * randomize(noise));
return vec3(sin(phi) * cos(theta), sin(phi) * sin(theta), cos(phi));
}
vec3 generate_ray_dir_from_normal(vec3 normal, inout uint noise) {
vec3 v0 = abs(normal.z) < 0.999 ? vec3(0.0, 0.0, 1.0) : vec3(0.0, 1.0, 0.0);
vec3 tangent = normalize(cross(v0, normal));
vec3 bitangent = normalize(cross(tangent, normal));
mat3 normal_mat = mat3(tangent, bitangent, normal);
return normal_mat * generate_hemisphere_cosine_weighted_direction(noise);
}
#if defined(MODE_DIRECT_LIGHT) || defined(MODE_BOUNCE_LIGHT) || defined(MODE_LIGHT_PROBES)
float get_omni_attenuation(float distance, float inv_range, float decay) { float get_omni_attenuation(float distance, float inv_range, float decay) {
float nd = distance * inv_range; float nd = distance * inv_range;
nd *= nd; nd *= nd;
@ -301,101 +290,70 @@ float get_omni_attenuation(float distance, float inv_range, float decay) {
return nd * pow(max(distance, 0.0001), -decay); return nd * pow(max(distance, 0.0001), -decay);
} }
void main() { void trace_direct_light(vec3 p_position, vec3 p_normal, uint p_light_index, bool p_soft_shadowing, out vec3 r_light, out vec3 r_light_dir, inout uint r_noise) {
#ifdef MODE_LIGHT_PROBES r_light = vec3(0.0f);
int probe_index = int(gl_GlobalInvocationID.x);
if (probe_index >= params.atlas_size.x) { //too large, do nothing vec3 light_pos;
float dist;
float attenuation;
float soft_shadowing_disk_size;
Light light_data = lights.data[p_light_index];
if (light_data.type == LIGHT_TYPE_DIRECTIONAL) {
vec3 light_vec = light_data.direction;
light_pos = p_position - light_vec * length(bake_params.world_size);
r_light_dir = normalize(light_pos - p_position);
dist = length(bake_params.world_size);
attenuation = 1.0;
soft_shadowing_disk_size = light_data.size;
} else {
light_pos = light_data.position;
r_light_dir = normalize(light_pos - p_position);
dist = distance(p_position, light_pos);
if (dist > light_data.range) {
return;
}
soft_shadowing_disk_size = light_data.size / dist;
attenuation = get_omni_attenuation(dist, 1.0 / light_data.range, light_data.attenuation);
if (light_data.type == LIGHT_TYPE_SPOT) {
vec3 rel = normalize(p_position - light_pos);
float cos_spot_angle = light_data.cos_spot_angle;
float cos_angle = dot(rel, light_data.direction);
if (cos_angle < cos_spot_angle) {
return;
}
float scos = max(cos_angle, cos_spot_angle);
float spot_rim = max(0.0001, (1.0 - scos) / (1.0 - cos_spot_angle));
attenuation *= 1.0 - pow(spot_rim, light_data.inv_spot_attenuation);
}
}
attenuation *= max(0.0, dot(p_normal, r_light_dir));
if (attenuation <= 0.0001) {
return; return;
} }
#else float penumbra = 0.0;
ivec2 atlas_pos = ivec2(gl_GlobalInvocationID.xy) + params.region_ofs; if ((light_data.size > 0.0) && p_soft_shadowing) {
if (any(greaterThanEqual(atlas_pos, params.atlas_size))) { //too large, do nothing vec3 light_to_point = -r_light_dir;
return; vec3 aux = light_to_point.y < 0.777 ? vec3(0.0, 1.0, 0.0) : vec3(1.0, 0.0, 0.0);
} vec3 light_to_point_tan = normalize(cross(light_to_point, aux));
#endif vec3 light_to_point_bitan = normalize(cross(light_to_point, light_to_point_tan));
#ifdef MODE_DIRECT_LIGHT const uint shadowing_rays_check_penumbra_denom = 2;
uint shadowing_ray_count = p_soft_shadowing ? params.ray_count : 1;
vec3 normal = texelFetch(sampler2DArray(source_normal, linear_sampler), ivec3(atlas_pos, params.atlas_slice), 0).xyz; uint hits = 0;
if (length(normal) < 0.5) { vec3 light_disk_to_point = light_to_point;
return; //empty texel, no process for (uint j = 0; j < shadowing_ray_count; j++) {
} // Optimization:
vec3 position = texelFetch(sampler2DArray(source_position, linear_sampler), ivec3(atlas_pos, params.atlas_slice), 0).xyz; // Once already traced an important proportion of rays, if all are hits or misses,
// assume we're not in the penumbra so we can infer the rest would have the same result
//go through all lights if (p_soft_shadowing) {
//start by own light (emissive)
vec3 static_light = vec3(0.0);
vec3 dynamic_light = vec3(0.0);
#ifdef USE_SH_LIGHTMAPS
vec4 sh_accum[4] = vec4[](
vec4(0.0, 0.0, 0.0, 1.0),
vec4(0.0, 0.0, 0.0, 1.0),
vec4(0.0, 0.0, 0.0, 1.0),
vec4(0.0, 0.0, 0.0, 1.0));
#endif
for (uint i = 0; i < params.light_count; i++) {
vec3 light_pos;
float dist;
float attenuation;
float soft_shadowing_disk_size;
if (lights.data[i].type == LIGHT_TYPE_DIRECTIONAL) {
vec3 light_vec = lights.data[i].direction;
light_pos = position - light_vec * length(params.world_size);
dist = length(params.world_size);
attenuation = 1.0;
soft_shadowing_disk_size = lights.data[i].size;
} else {
light_pos = lights.data[i].position;
dist = distance(position, light_pos);
if (dist > lights.data[i].range) {
continue;
}
soft_shadowing_disk_size = lights.data[i].size / dist;
attenuation = get_omni_attenuation(dist, 1.0 / lights.data[i].range, lights.data[i].attenuation);
if (lights.data[i].type == LIGHT_TYPE_SPOT) {
vec3 rel = normalize(position - light_pos);
float cos_spot_angle = lights.data[i].cos_spot_angle;
float cos_angle = dot(rel, lights.data[i].direction);
if (cos_angle < cos_spot_angle) {
continue; //invisible, dont try
}
float scos = max(cos_angle, cos_spot_angle);
float spot_rim = max(0.0001, (1.0 - scos) / (1.0 - cos_spot_angle));
attenuation *= 1.0 - pow(spot_rim, lights.data[i].inv_spot_attenuation);
}
}
vec3 light_dir = normalize(light_pos - position);
attenuation *= max(0.0, dot(normal, light_dir));
if (attenuation <= 0.0001) {
continue; //no need to do anything
}
float penumbra = 0.0;
if (lights.data[i].size > 0.0) {
vec3 light_to_point = -light_dir;
vec3 aux = light_to_point.y < 0.777 ? vec3(0.0, 1.0, 0.0) : vec3(1.0, 0.0, 0.0);
vec3 light_to_point_tan = normalize(cross(light_to_point, aux));
vec3 light_to_point_bitan = normalize(cross(light_to_point, light_to_point_tan));
const uint shadowing_rays_check_penumbra_denom = 2;
uint shadowing_ray_count = params.ray_count;
uint hits = 0;
uint noise = random_seed(ivec3(atlas_pos, 43573547 /* some prime */));
vec3 light_disk_to_point = light_to_point;
for (uint j = 0; j < shadowing_ray_count; j++) {
// Optimization:
// Once already traced an important proportion of rays, if all are hits or misses,
// assume we're not in the penumbra so we can infer the rest would have the same result
if (j == shadowing_ray_count / shadowing_rays_check_penumbra_denom) { if (j == shadowing_ray_count / shadowing_rays_check_penumbra_denom) {
if (hits == j) { if (hits == j) {
// Assume totally lit // Assume totally lit
@ -407,28 +365,160 @@ void main() {
break; break;
} }
} }
float r = randomize(noise);
float a = randomize(noise) * 2.0 * PI;
vec2 disk_sample = (r * vec2(cos(a), sin(a))) * soft_shadowing_disk_size * lights.data[i].shadow_blur;
light_disk_to_point = normalize(light_to_point + disk_sample.x * light_to_point_tan + disk_sample.y * light_to_point_bitan);
if (trace_ray(position - light_disk_to_point * params.bias, position - light_disk_to_point * dist) == RAY_MISS) {
hits++;
}
} }
penumbra = float(hits) / float(shadowing_ray_count);
} else { float r = randomize(r_noise);
if (trace_ray(position + light_dir * params.bias, light_pos) == RAY_MISS) { float a = randomize(r_noise) * 2.0 * PI;
penumbra = 1.0; vec2 disk_sample = (r * vec2(cos(a), sin(a))) * soft_shadowing_disk_size * light_data.shadow_blur;
light_disk_to_point = normalize(light_to_point + disk_sample.x * light_to_point_tan + disk_sample.y * light_to_point_bitan);
if (trace_ray_any_hit(p_position - light_disk_to_point * bake_params.bias, p_position - light_disk_to_point * dist) == RAY_MISS) {
hits++;
} }
} }
vec3 light = lights.data[i].color * lights.data[i].energy * attenuation * penumbra; penumbra = float(hits) / float(shadowing_ray_count);
if (lights.data[i].static_bake) { } else {
static_light += light; if (trace_ray_any_hit(p_position + r_light_dir * bake_params.bias, light_pos) == RAY_MISS) {
#ifdef USE_SH_LIGHTMAPS penumbra = 1.0;
}
}
r_light = light_data.color * light_data.energy * attenuation * penumbra;
}
#endif
#if defined(MODE_BOUNCE_LIGHT) || defined(MODE_LIGHT_PROBES)
vec3 trace_environment_color(vec3 ray_dir) {
vec3 sky_dir = normalize(mat3(bake_params.env_transform) * ray_dir);
vec2 st = vec2(atan(sky_dir.x, sky_dir.z), acos(sky_dir.y));
if (st.x < 0.0) {
st.x += PI * 2.0;
}
return textureLod(sampler2D(environment, linear_sampler), st / vec2(PI * 2.0, PI), 0.0).rgb;
}
vec3 trace_indirect_light(vec3 p_position, vec3 p_ray_dir, inout uint r_noise) {
// The lower limit considers the case where the lightmapper might have bounces disabled but light probes are requested.
vec3 position = p_position;
vec3 ray_dir = p_ray_dir;
uint max_depth = max(bake_params.bounces, 1);
vec3 throughput = vec3(1.0);
vec3 light = vec3(0.0);
for (uint depth = 0; depth < max_depth; depth++) {
uint tidx;
vec3 barycentric;
uint trace_result = trace_ray_closest_hit_triangle(position + ray_dir * bake_params.bias, position + ray_dir * length(bake_params.world_size), tidx, barycentric);
if (trace_result == RAY_FRONT) {
Vertex vert0 = vertices.data[triangles.data[tidx].indices.x];
Vertex vert1 = vertices.data[triangles.data[tidx].indices.y];
Vertex vert2 = vertices.data[triangles.data[tidx].indices.z];
vec3 uvw = vec3(barycentric.x * vert0.uv + barycentric.y * vert1.uv + barycentric.z * vert2.uv, float(triangles.data[tidx].slice));
position = barycentric.x * vert0.position + barycentric.y * vert1.position + barycentric.z * vert2.position;
vec3 norm0 = vec3(vert0.normal_xy, vert0.normal_z);
vec3 norm1 = vec3(vert1.normal_xy, vert1.normal_z);
vec3 norm2 = vec3(vert2.normal_xy, vert2.normal_z);
vec3 normal = barycentric.x * norm0 + barycentric.y * norm1 + barycentric.z * norm2;
vec3 direct_light = vec3(0.0f);
#ifdef USE_LIGHT_TEXTURE_FOR_BOUNCES
direct_light += textureLod(sampler2DArray(source_light, linear_sampler), uvw, 0.0).rgb;
#else
// Trace the lights directly. Significantly more expensive but more accurate in scenarios
// where the lightmap texture isn't reliable.
for (uint i = 0; i < bake_params.light_count; i++) {
vec3 light;
vec3 light_dir;
trace_direct_light(position, normal, i, false, light, light_dir, r_noise);
direct_light += light * lights.data[i].indirect_energy;
}
direct_light *= bake_params.exposure_normalization;
#endif
vec3 albedo = textureLod(sampler2DArray(albedo_tex, linear_sampler), uvw, 0).rgb;
vec3 emissive = textureLod(sampler2DArray(emission_tex, linear_sampler), uvw, 0).rgb;
emissive *= bake_params.exposure_normalization;
light += throughput * emissive;
throughput *= albedo;
light += throughput * direct_light * bake_params.bounce_indirect_energy;
// Use Russian Roulette to determine a probability to terminate the bounce earlier as an optimization.
// <https://computergraphics.stackexchange.com/questions/2316/is-russian-roulette-really-the-answer>
float p = max(max(throughput.x, throughput.y), throughput.z);
if (randomize(r_noise) > p) {
break;
}
// Boost the throughput from the probability of the ray being terminated early.
throughput *= 1.0 / p;
// Generate a new ray direction for the next bounce from this surface's normal.
ray_dir = generate_ray_dir_from_normal(normal, r_noise);
} else if (trace_result == RAY_MISS) {
// Look for the environment color and stop bouncing.
light += throughput * trace_environment_color(ray_dir);
break;
} else {
// Ignore any other trace results.
break;
}
}
return light;
}
#endif
void main() {
// Check if invocation is out of bounds.
#ifdef MODE_LIGHT_PROBES
int probe_index = int(gl_GlobalInvocationID.x);
if (probe_index >= params.probe_count) {
return;
}
#else
ivec2 atlas_pos = ivec2(gl_GlobalInvocationID.xy) + params.region_ofs;
if (any(greaterThanEqual(atlas_pos, bake_params.atlas_size))) {
return;
}
#endif
#ifdef MODE_DIRECT_LIGHT
vec3 normal = texelFetch(sampler2DArray(source_normal, linear_sampler), ivec3(atlas_pos, params.atlas_slice), 0).xyz;
if (length(normal) < 0.5) {
return; //empty texel, no process
}
vec3 position = texelFetch(sampler2DArray(source_position, linear_sampler), ivec3(atlas_pos, params.atlas_slice), 0).xyz;
vec3 light_for_texture = vec3(0.0);
vec3 light_for_bounces = vec3(0.0);
#ifdef USE_SH_LIGHTMAPS
vec4 sh_accum[4] = vec4[](
vec4(0.0, 0.0, 0.0, 1.0),
vec4(0.0, 0.0, 0.0, 1.0),
vec4(0.0, 0.0, 0.0, 1.0),
vec4(0.0, 0.0, 0.0, 1.0));
#endif
// Use atlas position and a prime number as the seed.
uint noise = random_seed(ivec3(atlas_pos, 43573547));
for (uint i = 0; i < bake_params.light_count; i++) {
vec3 light;
vec3 light_dir;
trace_direct_light(position, normal, i, true, light, light_dir, noise);
if (lights.data[i].static_bake) {
light_for_texture += light;
#ifdef USE_SH_LIGHTMAPS
float c[4] = float[]( float c[4] = float[](
0.282095, //l0 0.282095, //l0
0.488603 * light_dir.y, //l1n1 0.488603 * light_dir.y, //l1n1
@ -440,103 +530,53 @@ void main() {
sh_accum[j].rgb += light * c[j] * 8.0; sh_accum[j].rgb += light * c[j] * 8.0;
} }
#endif #endif
} else {
dynamic_light += light;
} }
light_for_bounces += light * lights.data[i].indirect_energy;
} }
vec3 albedo = texelFetch(sampler2DArray(albedo_tex, linear_sampler), ivec3(atlas_pos, params.atlas_slice), 0).rgb; light_for_bounces *= bake_params.exposure_normalization;
vec3 emissive = texelFetch(sampler2DArray(emission_tex, linear_sampler), ivec3(atlas_pos, params.atlas_slice), 0).rgb; imageStore(dest_light, ivec3(atlas_pos, params.atlas_slice), vec4(light_for_bounces, 1.0));
dynamic_light *= albedo; //if it will bounce, must multiply by albedo
dynamic_light += emissive;
//keep for lightprobes
imageStore(primary_dynamic, ivec3(atlas_pos, params.atlas_slice), vec4(dynamic_light, 1.0));
dynamic_light += static_light * albedo; //send for bounces
dynamic_light *= params.env_transform[2][3]; // exposure_normalization
imageStore(dest_light, ivec3(atlas_pos, params.atlas_slice), vec4(dynamic_light, 1.0));
#ifdef USE_SH_LIGHTMAPS #ifdef USE_SH_LIGHTMAPS
//keep for adding at the end // Keep for adding at the end.
imageStore(accum_light, ivec3(atlas_pos, params.atlas_slice * 4 + 0), sh_accum[0]); imageStore(accum_light, ivec3(atlas_pos, params.atlas_slice * 4 + 0), sh_accum[0]);
imageStore(accum_light, ivec3(atlas_pos, params.atlas_slice * 4 + 1), sh_accum[1]); imageStore(accum_light, ivec3(atlas_pos, params.atlas_slice * 4 + 1), sh_accum[1]);
imageStore(accum_light, ivec3(atlas_pos, params.atlas_slice * 4 + 2), sh_accum[2]); imageStore(accum_light, ivec3(atlas_pos, params.atlas_slice * 4 + 2), sh_accum[2]);
imageStore(accum_light, ivec3(atlas_pos, params.atlas_slice * 4 + 3), sh_accum[3]); imageStore(accum_light, ivec3(atlas_pos, params.atlas_slice * 4 + 3), sh_accum[3]);
#else #else
static_light *= params.env_transform[2][3]; // exposure_normalization light_for_texture *= bake_params.exposure_normalization;
imageStore(accum_light, ivec3(atlas_pos, params.atlas_slice), vec4(static_light, 1.0)); imageStore(accum_light, ivec3(atlas_pos, params.atlas_slice), vec4(light_for_texture, 1.0));
#endif #endif
#endif #endif
#ifdef MODE_BOUNCE_LIGHT #ifdef MODE_BOUNCE_LIGHT
vec3 normal = texelFetch(sampler2DArray(source_normal, linear_sampler), ivec3(atlas_pos, params.atlas_slice), 0).xyz;
if (length(normal) < 0.5) {
return; //empty texel, no process
}
vec3 position = texelFetch(sampler2DArray(source_position, linear_sampler), ivec3(atlas_pos, params.atlas_slice), 0).xyz;
vec3 v0 = abs(normal.z) < 0.999 ? vec3(0.0, 0.0, 1.0) : vec3(0.0, 1.0, 0.0);
vec3 tangent = normalize(cross(v0, normal));
vec3 bitangent = normalize(cross(tangent, normal));
mat3 normal_mat = mat3(tangent, bitangent, normal);
#ifdef USE_SH_LIGHTMAPS #ifdef USE_SH_LIGHTMAPS
vec4 sh_accum[4] = vec4[]( vec4 sh_accum[4] = vec4[](
vec4(0.0, 0.0, 0.0, 1.0), vec4(0.0, 0.0, 0.0, 1.0),
vec4(0.0, 0.0, 0.0, 1.0), vec4(0.0, 0.0, 0.0, 1.0),
vec4(0.0, 0.0, 0.0, 1.0), vec4(0.0, 0.0, 0.0, 1.0),
vec4(0.0, 0.0, 0.0, 1.0)); vec4(0.0, 0.0, 0.0, 1.0));
#else
vec3 light_accum = vec3(0.0);
#endif #endif
vec3 light_average = vec3(0.0);
float active_rays = 0.0; // Retrieve starting normal and position.
vec3 normal = texelFetch(sampler2DArray(source_normal, linear_sampler), ivec3(atlas_pos, params.atlas_slice), 0).xyz;
if (length(normal) < 0.5) {
// The pixel is empty, skip processing it.
return;
}
vec3 position = texelFetch(sampler2DArray(source_position, linear_sampler), ivec3(atlas_pos, params.atlas_slice), 0).xyz;
uint noise = random_seed(ivec3(params.ray_from, atlas_pos)); uint noise = random_seed(ivec3(params.ray_from, atlas_pos));
for (uint i = params.ray_from; i < params.ray_to; i++) { for (uint i = params.ray_from; i < params.ray_to; i++) {
vec3 ray_dir = normal_mat * generate_hemisphere_cosine_weighted_direction(noise); vec3 ray_dir = generate_ray_dir_from_normal(normal, noise);
vec3 light = trace_indirect_light(position, ray_dir, noise);
uint tidx;
vec3 barycentric;
vec3 light = vec3(0.0);
uint trace_result = trace_ray(position + ray_dir * params.bias, position + ray_dir * length(params.world_size), tidx, barycentric);
if (trace_result == RAY_FRONT) {
//hit a triangle
vec2 uv0 = vertices.data[triangles.data[tidx].indices.x].uv;
vec2 uv1 = vertices.data[triangles.data[tidx].indices.y].uv;
vec2 uv2 = vertices.data[triangles.data[tidx].indices.z].uv;
vec3 uvw = vec3(barycentric.x * uv0 + barycentric.y * uv1 + barycentric.z * uv2, float(triangles.data[tidx].slice));
light = textureLod(sampler2DArray(source_light, linear_sampler), uvw, 0.0).rgb;
active_rays += 1.0;
} else if (trace_result == RAY_MISS) {
if (params.env_transform[0][3] == 0.0) { // Use env_transform[0][3] to indicate when we are computing the first bounce
// Did not hit a triangle, reach out for the sky
vec3 sky_dir = normalize(mat3(params.env_transform) * ray_dir);
vec2 st = vec2(
atan(sky_dir.x, sky_dir.z),
acos(sky_dir.y));
if (st.x < 0.0)
st.x += PI * 2.0;
st /= vec2(PI * 2.0, PI);
light = textureLod(sampler2D(environment, linear_sampler), st, 0.0).rgb;
}
active_rays += 1.0;
}
light_average += light;
#ifdef USE_SH_LIGHTMAPS #ifdef USE_SH_LIGHTMAPS
float c[4] = float[]( float c[4] = float[](
0.282095, //l0 0.282095, //l0
0.488603 * ray_dir.y, //l1n1 0.488603 * ray_dir.y, //l1n1
@ -545,44 +585,25 @@ void main() {
); );
for (uint j = 0; j < 4; j++) { for (uint j = 0; j < 4; j++) {
sh_accum[j].rgb += light * c[j] * (8.0 / float(params.ray_count)); sh_accum[j].rgb += light * c[j] * 8.0;
} }
#else
light_accum += light;
#endif #endif
} }
vec3 light_total; // Add the averaged result to the accumulated light texture.
if (params.ray_from == 0) {
light_total = vec3(0.0);
} else {
vec4 accum = imageLoad(bounce_accum, ivec3(atlas_pos, params.atlas_slice));
light_total = accum.rgb;
active_rays += accum.a;
}
light_total += light_average;
#ifdef USE_SH_LIGHTMAPS #ifdef USE_SH_LIGHTMAPS
for (int i = 0; i < 4; i++) { for (int i = 0; i < 4; i++) {
vec4 accum = imageLoad(accum_light, ivec3(atlas_pos, params.atlas_slice * 4 + i)); vec4 accum = imageLoad(accum_light, ivec3(atlas_pos, params.atlas_slice * 4 + i));
accum.rgb += sh_accum[i].rgb; accum.rgb += sh_accum[i].rgb / float(params.ray_count);
imageStore(accum_light, ivec3(atlas_pos, params.atlas_slice * 4 + i), accum); imageStore(accum_light, ivec3(atlas_pos, params.atlas_slice * 4 + i), accum);
} }
#else
vec4 accum = imageLoad(accum_light, ivec3(atlas_pos, params.atlas_slice));
accum.rgb += light_accum / float(params.ray_count);
imageStore(accum_light, ivec3(atlas_pos, params.atlas_slice), accum);
#endif #endif
if (params.ray_to == params.ray_count) {
if (active_rays > 0) {
light_total /= active_rays;
}
imageStore(dest_light, ivec3(atlas_pos, params.atlas_slice), vec4(light_total, 1.0));
#ifndef USE_SH_LIGHTMAPS
vec4 accum = imageLoad(accum_light, ivec3(atlas_pos, params.atlas_slice));
accum.rgb += light_total;
imageStore(accum_light, ivec3(atlas_pos, params.atlas_slice), accum);
#endif
} else {
imageStore(bounce_accum, ivec3(atlas_pos, params.atlas_slice), vec4(light_total, active_rays));
}
#endif #endif
@ -605,7 +626,7 @@ void main() {
vec3 v0 = abs(face_normal.z) < 0.999 ? vec3(0.0, 0.0, 1.0) : vec3(0.0, 1.0, 0.0); vec3 v0 = abs(face_normal.z) < 0.999 ? vec3(0.0, 0.0, 1.0) : vec3(0.0, 1.0, 0.0);
vec3 tangent = normalize(cross(v0, face_normal)); vec3 tangent = normalize(cross(v0, face_normal));
vec3 bitangent = normalize(cross(tangent, face_normal)); vec3 bitangent = normalize(cross(tangent, face_normal));
vec3 base_pos = vertex_pos + face_normal * params.bias; //raise a bit vec3 base_pos = vertex_pos + face_normal * bake_params.bias; // Raise a bit.
vec3 rays[4] = vec3[](tangent, bitangent, -tangent, -bitangent); vec3 rays[4] = vec3[](tangent, bitangent, -tangent, -bitangent);
float min_d = 1e20; float min_d = 1e20;
@ -614,9 +635,10 @@ void main() {
float d; float d;
vec3 norm; vec3 norm;
if (trace_ray(base_pos, ray_to, d, norm) == RAY_BACK) { if (trace_ray_closest_hit_distance(base_pos, ray_to, d, norm) == RAY_BACK) {
if (d < min_d) { if (d < min_d) {
vertex_pos = base_pos + rays[i] * d + norm * params.bias * 10.0; //this bias needs to be greater than the regular bias, because otherwise later, rays will go the other side when pointing back. // This bias needs to be greater than the regular bias, because otherwise later, rays will go the other side when pointing back.
vertex_pos = base_pos + rays[i] * d + norm * bake_params.bias * 10.0;
min_d = d; min_d = d;
} }
} }
@ -645,58 +667,24 @@ void main() {
uint noise = random_seed(ivec3(params.ray_from, probe_index, 49502741 /* some prime */)); uint noise = random_seed(ivec3(params.ray_from, probe_index, 49502741 /* some prime */));
for (uint i = params.ray_from; i < params.ray_to; i++) { for (uint i = params.ray_from; i < params.ray_to; i++) {
vec3 ray_dir = generate_hemisphere_uniform_direction(noise); vec3 ray_dir = generate_sphere_uniform_direction(noise);
if (bool(i & 1)) { vec3 light = trace_indirect_light(position, ray_dir, noise);
//throw to both sides, so alternate them
ray_dir.z *= -1.0;
}
uint tidx; float c[9] = float[](
vec3 barycentric; 0.282095, //l0
vec3 light; 0.488603 * ray_dir.y, //l1n1
0.488603 * ray_dir.z, //l1n0
0.488603 * ray_dir.x, //l1p1
1.092548 * ray_dir.x * ray_dir.y, //l2n2
1.092548 * ray_dir.y * ray_dir.z, //l2n1
//0.315392 * (ray_dir.x * ray_dir.x + ray_dir.y * ray_dir.y + 2.0 * ray_dir.z * ray_dir.z), //l20
0.315392 * (3.0 * ray_dir.z * ray_dir.z - 1.0), //l20
1.092548 * ray_dir.x * ray_dir.z, //l2p1
0.546274 * (ray_dir.x * ray_dir.x - ray_dir.y * ray_dir.y) //l2p2
);
uint trace_result = trace_ray(position + ray_dir * params.bias, position + ray_dir * length(params.world_size), tidx, barycentric); for (uint j = 0; j < 9; j++) {
if (trace_result == RAY_FRONT) { probe_sh_accum[j].rgb += light * c[j];
vec2 uv0 = vertices.data[triangles.data[tidx].indices.x].uv;
vec2 uv1 = vertices.data[triangles.data[tidx].indices.y].uv;
vec2 uv2 = vertices.data[triangles.data[tidx].indices.z].uv;
vec3 uvw = vec3(barycentric.x * uv0 + barycentric.y * uv1 + barycentric.z * uv2, float(triangles.data[tidx].slice));
light = textureLod(sampler2DArray(source_light, linear_sampler), uvw, 0.0).rgb;
light += textureLod(sampler2DArray(source_direct_light, linear_sampler), uvw, 0.0).rgb;
} else if (trace_result == RAY_MISS) {
//did not hit a triangle, reach out for the sky
vec3 sky_dir = normalize(mat3(params.env_transform) * ray_dir);
vec2 st = vec2(
atan(sky_dir.x, sky_dir.z),
acos(sky_dir.y));
if (st.x < 0.0)
st.x += PI * 2.0;
st /= vec2(PI * 2.0, PI);
light = textureLod(sampler2D(environment, linear_sampler), st, 0.0).rgb;
}
{
float c[9] = float[](
0.282095, //l0
0.488603 * ray_dir.y, //l1n1
0.488603 * ray_dir.z, //l1n0
0.488603 * ray_dir.x, //l1p1
1.092548 * ray_dir.x * ray_dir.y, //l2n2
1.092548 * ray_dir.y * ray_dir.z, //l2n1
//0.315392 * (ray_dir.x * ray_dir.x + ray_dir.y * ray_dir.y + 2.0 * ray_dir.z * ray_dir.z), //l20
0.315392 * (3.0 * ray_dir.z * ray_dir.z - 1.0), //l20
1.092548 * ray_dir.x * ray_dir.z, //l2p1
0.546274 * (ray_dir.x * ray_dir.x - ray_dir.y * ray_dir.y) //l2p2
);
for (uint j = 0; j < 9; j++) {
probe_sh_accum[j].rgb += light * c[j];
}
} }
} }
@ -868,8 +856,8 @@ void main() {
float weight = 1.0f; float weight = 1.0f;
// Ignore weight if search position is out of bounds. // Ignore weight if search position is out of bounds.
weight *= step(0, search_pos.x) * step(search_pos.x, params.atlas_size.x - 1); weight *= step(0, search_pos.x) * step(search_pos.x, bake_params.atlas_size.x - 1);
weight *= step(0, search_pos.y) * step(search_pos.y, params.atlas_size.y - 1); weight *= step(0, search_pos.y) * step(search_pos.y, bake_params.atlas_size.y - 1);
// Ignore weight if normal is zero length. // Ignore weight if normal is zero length.
weight *= step(EPSILON, length(search_normal)); weight *= step(EPSILON, length(search_normal));

View File

@ -46,10 +46,10 @@ void initialize_lightmapper_rd_module(ModuleInitializationLevel p_level) {
return; return;
} }
GLOBAL_DEF("rendering/lightmapping/bake_quality/low_quality_ray_count", 16); GLOBAL_DEF("rendering/lightmapping/bake_quality/low_quality_ray_count", 32);
GLOBAL_DEF("rendering/lightmapping/bake_quality/medium_quality_ray_count", 64); GLOBAL_DEF("rendering/lightmapping/bake_quality/medium_quality_ray_count", 128);
GLOBAL_DEF("rendering/lightmapping/bake_quality/high_quality_ray_count", 256); GLOBAL_DEF("rendering/lightmapping/bake_quality/high_quality_ray_count", 512);
GLOBAL_DEF("rendering/lightmapping/bake_quality/ultra_quality_ray_count", 1024); GLOBAL_DEF("rendering/lightmapping/bake_quality/ultra_quality_ray_count", 2048);
GLOBAL_DEF("rendering/lightmapping/bake_performance/max_rays_per_pass", 32); GLOBAL_DEF("rendering/lightmapping/bake_performance/max_rays_per_pass", 32);
GLOBAL_DEF("rendering/lightmapping/bake_performance/region_size", 512); GLOBAL_DEF("rendering/lightmapping/bake_performance/region_size", 512);

View File

@ -984,6 +984,7 @@ LightmapGI::BakeError LightmapGI::bake(Node *p_from_node, String p_image_data_pa
} }
// Add everything to lightmapper // Add everything to lightmapper
const bool use_physical_light_units = GLOBAL_GET("rendering/lights_and_shadows/use_physical_light_units");
if (p_bake_step) { if (p_bake_step) {
p_bake_step(0.4, RTR("Preparing Lightmapper"), p_bake_userdata, true); p_bake_step(0.4, RTR("Preparing Lightmapper"), p_bake_userdata, true);
} }
@ -996,28 +997,30 @@ LightmapGI::BakeError LightmapGI::bake(Node *p_from_node, String p_image_data_pa
Light3D *light = lights_found[i].light; Light3D *light = lights_found[i].light;
Transform3D xf = lights_found[i].xform; Transform3D xf = lights_found[i].xform;
// For the lightmapper, the indirect energy represents the multiplier for the indirect bounces caused by the light, so the value is not converted when using physical units.
float indirect_energy = light->get_param(Light3D::PARAM_INDIRECT_ENERGY);
Color linear_color = light->get_color().srgb_to_linear(); Color linear_color = light->get_color().srgb_to_linear();
float energy = light->get_param(Light3D::PARAM_ENERGY); float energy = light->get_param(Light3D::PARAM_ENERGY);
if (GLOBAL_GET("rendering/lights_and_shadows/use_physical_light_units")) { if (use_physical_light_units) {
energy *= light->get_param(Light3D::PARAM_INTENSITY); energy *= light->get_param(Light3D::PARAM_INTENSITY);
linear_color *= light->get_correlated_color().srgb_to_linear(); linear_color *= light->get_correlated_color().srgb_to_linear();
} }
if (Object::cast_to<DirectionalLight3D>(light)) { if (Object::cast_to<DirectionalLight3D>(light)) {
DirectionalLight3D *l = Object::cast_to<DirectionalLight3D>(light); DirectionalLight3D *l = Object::cast_to<DirectionalLight3D>(light);
lightmapper->add_directional_light(light->get_bake_mode() == Light3D::BAKE_STATIC, -xf.basis.get_column(Vector3::AXIS_Z).normalized(), linear_color, energy, l->get_param(Light3D::PARAM_SIZE), l->get_param(Light3D::PARAM_SHADOW_BLUR)); lightmapper->add_directional_light(light->get_bake_mode() == Light3D::BAKE_STATIC, -xf.basis.get_column(Vector3::AXIS_Z).normalized(), linear_color, energy, indirect_energy, l->get_param(Light3D::PARAM_SIZE), l->get_param(Light3D::PARAM_SHADOW_BLUR));
} else if (Object::cast_to<OmniLight3D>(light)) { } else if (Object::cast_to<OmniLight3D>(light)) {
OmniLight3D *l = Object::cast_to<OmniLight3D>(light); OmniLight3D *l = Object::cast_to<OmniLight3D>(light);
if (GLOBAL_GET("rendering/lights_and_shadows/use_physical_light_units")) { if (use_physical_light_units) {
energy *= (1.0 / (Math_PI * 4.0)); energy *= (1.0 / (Math_PI * 4.0));
} }
lightmapper->add_omni_light(light->get_bake_mode() == Light3D::BAKE_STATIC, xf.origin, linear_color, energy, l->get_param(Light3D::PARAM_RANGE), l->get_param(Light3D::PARAM_ATTENUATION), l->get_param(Light3D::PARAM_SIZE), l->get_param(Light3D::PARAM_SHADOW_BLUR)); lightmapper->add_omni_light(light->get_bake_mode() == Light3D::BAKE_STATIC, xf.origin, linear_color, energy, indirect_energy, l->get_param(Light3D::PARAM_RANGE), l->get_param(Light3D::PARAM_ATTENUATION), l->get_param(Light3D::PARAM_SIZE), l->get_param(Light3D::PARAM_SHADOW_BLUR));
} else if (Object::cast_to<SpotLight3D>(light)) { } else if (Object::cast_to<SpotLight3D>(light)) {
SpotLight3D *l = Object::cast_to<SpotLight3D>(light); SpotLight3D *l = Object::cast_to<SpotLight3D>(light);
if (GLOBAL_GET("rendering/lights_and_shadows/use_physical_light_units")) { if (use_physical_light_units) {
energy *= (1.0 / Math_PI); energy *= (1.0 / Math_PI);
} }
lightmapper->add_spot_light(light->get_bake_mode() == Light3D::BAKE_STATIC, xf.origin, -xf.basis.get_column(Vector3::AXIS_Z).normalized(), linear_color, energy, l->get_param(Light3D::PARAM_RANGE), l->get_param(Light3D::PARAM_ATTENUATION), l->get_param(Light3D::PARAM_SPOT_ANGLE), l->get_param(Light3D::PARAM_SPOT_ATTENUATION), l->get_param(Light3D::PARAM_SIZE), l->get_param(Light3D::PARAM_SHADOW_BLUR)); lightmapper->add_spot_light(light->get_bake_mode() == Light3D::BAKE_STATIC, xf.origin, -xf.basis.get_column(Vector3::AXIS_Z).normalized(), linear_color, energy, indirect_energy, l->get_param(Light3D::PARAM_RANGE), l->get_param(Light3D::PARAM_ATTENUATION), l->get_param(Light3D::PARAM_SPOT_ANGLE), l->get_param(Light3D::PARAM_SPOT_ATTENUATION), l->get_param(Light3D::PARAM_SIZE), l->get_param(Light3D::PARAM_SHADOW_BLUR));
} }
} }
for (int i = 0; i < probes_found.size(); i++) { for (int i = 0; i < probes_found.size(); i++) {
@ -1075,12 +1078,12 @@ LightmapGI::BakeError LightmapGI::bake(Node *p_from_node, String p_image_data_pa
float exposure_normalization = 1.0; float exposure_normalization = 1.0;
if (camera_attributes.is_valid()) { if (camera_attributes.is_valid()) {
exposure_normalization = camera_attributes->get_exposure_multiplier(); exposure_normalization = camera_attributes->get_exposure_multiplier();
if (GLOBAL_GET("rendering/lights_and_shadows/use_physical_light_units")) { if (use_physical_light_units) {
exposure_normalization = camera_attributes->calculate_exposure_normalization(); exposure_normalization = camera_attributes->calculate_exposure_normalization();
} }
} }
Lightmapper::BakeError bake_err = lightmapper->bake(Lightmapper::BakeQuality(bake_quality), use_denoiser, denoiser_strength, bounces, bias, max_texture_size, directional, Lightmapper::GenerateProbes(gen_probes), environment_image, environment_transform, _lightmap_bake_step_function, &bsud, exposure_normalization); Lightmapper::BakeError bake_err = lightmapper->bake(Lightmapper::BakeQuality(bake_quality), use_denoiser, denoiser_strength, bounces, bounce_indirect_energy, bias, max_texture_size, directional, use_texture_for_bounces, Lightmapper::GenerateProbes(gen_probes), environment_image, environment_transform, _lightmap_bake_step_function, &bsud, exposure_normalization);
if (bake_err == Lightmapper::BAKE_ERROR_LIGHTMAP_TOO_SMALL) { if (bake_err == Lightmapper::BAKE_ERROR_LIGHTMAP_TOO_SMALL) {
return BAKE_ERROR_TEXTURE_SIZE_TOO_SMALL; return BAKE_ERROR_TEXTURE_SIZE_TOO_SMALL;
@ -1385,6 +1388,14 @@ bool LightmapGI::is_directional() const {
return directional; return directional;
} }
void LightmapGI::set_use_texture_for_bounces(bool p_enable) {
use_texture_for_bounces = p_enable;
}
bool LightmapGI::is_using_texture_for_bounces() const {
return use_texture_for_bounces;
}
void LightmapGI::set_interior(bool p_enable) { void LightmapGI::set_interior(bool p_enable) {
interior = p_enable; interior = p_enable;
} }
@ -1435,6 +1446,15 @@ int LightmapGI::get_bounces() const {
return bounces; return bounces;
} }
void LightmapGI::set_bounce_indirect_energy(float p_indirect_energy) {
ERR_FAIL_COND(p_indirect_energy < 0.0);
bounce_indirect_energy = p_indirect_energy;
}
float LightmapGI::get_bounce_indirect_energy() const {
return bounce_indirect_energy;
}
void LightmapGI::set_bias(float p_bias) { void LightmapGI::set_bias(float p_bias) {
ERR_FAIL_COND(p_bias < 0.00001); ERR_FAIL_COND(p_bias < 0.00001);
bias = p_bias; bias = p_bias;
@ -1506,6 +1526,9 @@ void LightmapGI::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_bounces", "bounces"), &LightmapGI::set_bounces); ClassDB::bind_method(D_METHOD("set_bounces", "bounces"), &LightmapGI::set_bounces);
ClassDB::bind_method(D_METHOD("get_bounces"), &LightmapGI::get_bounces); ClassDB::bind_method(D_METHOD("get_bounces"), &LightmapGI::get_bounces);
ClassDB::bind_method(D_METHOD("set_bounce_indirect_energy", "bounce_indirect_energy"), &LightmapGI::set_bounce_indirect_energy);
ClassDB::bind_method(D_METHOD("get_bounce_indirect_energy"), &LightmapGI::get_bounce_indirect_energy);
ClassDB::bind_method(D_METHOD("set_generate_probes", "subdivision"), &LightmapGI::set_generate_probes); ClassDB::bind_method(D_METHOD("set_generate_probes", "subdivision"), &LightmapGI::set_generate_probes);
ClassDB::bind_method(D_METHOD("get_generate_probes"), &LightmapGI::get_generate_probes); ClassDB::bind_method(D_METHOD("get_generate_probes"), &LightmapGI::get_generate_probes);
@ -1539,6 +1562,9 @@ void LightmapGI::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_directional", "directional"), &LightmapGI::set_directional); ClassDB::bind_method(D_METHOD("set_directional", "directional"), &LightmapGI::set_directional);
ClassDB::bind_method(D_METHOD("is_directional"), &LightmapGI::is_directional); ClassDB::bind_method(D_METHOD("is_directional"), &LightmapGI::is_directional);
ClassDB::bind_method(D_METHOD("set_use_texture_for_bounces", "use_texture_for_bounces"), &LightmapGI::set_use_texture_for_bounces);
ClassDB::bind_method(D_METHOD("is_using_texture_for_bounces"), &LightmapGI::is_using_texture_for_bounces);
ClassDB::bind_method(D_METHOD("set_camera_attributes", "camera_attributes"), &LightmapGI::set_camera_attributes); ClassDB::bind_method(D_METHOD("set_camera_attributes", "camera_attributes"), &LightmapGI::set_camera_attributes);
ClassDB::bind_method(D_METHOD("get_camera_attributes"), &LightmapGI::get_camera_attributes); ClassDB::bind_method(D_METHOD("get_camera_attributes"), &LightmapGI::get_camera_attributes);
@ -1546,8 +1572,10 @@ void LightmapGI::_bind_methods() {
ADD_GROUP("Tweaks", ""); ADD_GROUP("Tweaks", "");
ADD_PROPERTY(PropertyInfo(Variant::INT, "quality", PROPERTY_HINT_ENUM, "Low,Medium,High,Ultra"), "set_bake_quality", "get_bake_quality"); ADD_PROPERTY(PropertyInfo(Variant::INT, "quality", PROPERTY_HINT_ENUM, "Low,Medium,High,Ultra"), "set_bake_quality", "get_bake_quality");
ADD_PROPERTY(PropertyInfo(Variant::INT, "bounces", PROPERTY_HINT_RANGE, "0,16,1"), "set_bounces", "get_bounces"); ADD_PROPERTY(PropertyInfo(Variant::INT, "bounces", PROPERTY_HINT_RANGE, "0,6,1,or_greater"), "set_bounces", "get_bounces");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "bounce_indirect_energy", PROPERTY_HINT_RANGE, "0,2,0.01"), "set_bounce_indirect_energy", "get_bounce_indirect_energy");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "directional"), "set_directional", "is_directional"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "directional"), "set_directional", "is_directional");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_texture_for_bounces"), "set_use_texture_for_bounces", "is_using_texture_for_bounces");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "interior"), "set_interior", "is_interior"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "interior"), "set_interior", "is_interior");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_denoiser"), "set_use_denoiser", "is_using_denoiser"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_denoiser"), "set_use_denoiser", "is_using_denoiser");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "denoiser_strength", PROPERTY_HINT_RANGE, "0.001,0.2,0.001,or_greater"), "set_denoiser_strength", "get_denoiser_strength"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "denoiser_strength", PROPERTY_HINT_RANGE, "0.001,0.2,0.001,or_greater"), "set_denoiser_strength", "get_denoiser_strength");

View File

@ -147,6 +147,7 @@ private:
bool use_denoiser = true; bool use_denoiser = true;
float denoiser_strength = 0.1f; float denoiser_strength = 0.1f;
int bounces = 3; int bounces = 3;
float bounce_indirect_energy = 1.0;
float bias = 0.0005; float bias = 0.0005;
int max_texture_size = 16384; int max_texture_size = 16384;
bool interior = false; bool interior = false;
@ -155,6 +156,7 @@ private:
Color environment_custom_color = Color(1, 1, 1); Color environment_custom_color = Color(1, 1, 1);
float environment_custom_energy = 1.0; float environment_custom_energy = 1.0;
bool directional = false; bool directional = false;
bool use_texture_for_bounces = true;
GenerateProbes gen_probes = GENERATE_PROBES_SUBDIV_8; GenerateProbes gen_probes = GENERATE_PROBES_SUBDIV_8;
Ref<CameraAttributes> camera_attributes; Ref<CameraAttributes> camera_attributes;
@ -246,6 +248,9 @@ public:
void set_directional(bool p_enable); void set_directional(bool p_enable);
bool is_directional() const; bool is_directional() const;
void set_use_texture_for_bounces(bool p_enable);
bool is_using_texture_for_bounces() const;
void set_interior(bool p_interior); void set_interior(bool p_interior);
bool is_interior() const; bool is_interior() const;
@ -264,6 +269,9 @@ public:
void set_bounces(int p_bounces); void set_bounces(int p_bounces);
int get_bounces() const; int get_bounces() const;
void set_bounce_indirect_energy(float p_indirect_energy);
float get_bounce_indirect_energy() const;
void set_bias(float p_bias); void set_bias(float p_bias);
float get_bias() const; float get_bias() const;

View File

@ -176,11 +176,11 @@ public:
}; };
virtual void add_mesh(const MeshData &p_mesh) = 0; virtual void add_mesh(const MeshData &p_mesh) = 0;
virtual void add_directional_light(bool p_static, const Vector3 &p_direction, const Color &p_color, float p_energy, float p_angular_distance, float p_shadow_blur) = 0; virtual void add_directional_light(bool p_static, const Vector3 &p_direction, const Color &p_color, float p_energy, float p_indirect_energy, float p_angular_distance, float p_shadow_blur) = 0;
virtual void add_omni_light(bool p_static, const Vector3 &p_position, const Color &p_color, float p_energy, float p_range, float p_attenuation, float p_size, float p_shadow_blur) = 0; virtual void add_omni_light(bool p_static, const Vector3 &p_position, const Color &p_color, float p_energy, float p_indirect_energy, float p_range, float p_attenuation, float p_size, float p_shadow_blur) = 0;
virtual void add_spot_light(bool p_static, const Vector3 &p_position, const Vector3 p_direction, const Color &p_color, float p_energy, float p_range, float p_attenuation, float p_spot_angle, float p_spot_attenuation, float p_size, float p_shadow_blur) = 0; virtual void add_spot_light(bool p_static, const Vector3 &p_position, const Vector3 p_direction, const Color &p_color, float p_energy, float p_indirect_energy, float p_range, float p_attenuation, float p_spot_angle, float p_spot_attenuation, float p_size, float p_shadow_blur) = 0;
virtual void add_probe(const Vector3 &p_position) = 0; virtual void add_probe(const Vector3 &p_position) = 0;
virtual BakeError bake(BakeQuality p_quality, bool p_use_denoiser, float p_denoiser_strength, int p_bounces, float p_bias, int p_max_texture_size, bool p_bake_sh, GenerateProbes p_generate_probes, const Ref<Image> &p_environment_panorama, const Basis &p_environment_transform, BakeStepFunc p_step_function = nullptr, void *p_step_userdata = nullptr, float p_exposure_normalization = 1.0) = 0; virtual BakeError bake(BakeQuality p_quality, bool p_use_denoiser, float p_denoiser_strength, int p_bounces, float p_bounce_indirect_energy, float p_bias, int p_max_texture_size, bool p_bake_sh, bool p_texture_for_bounces, GenerateProbes p_generate_probes, const Ref<Image> &p_environment_panorama, const Basis &p_environment_transform, BakeStepFunc p_step_function = nullptr, void *p_step_userdata = nullptr, float p_exposure_normalization = 1.0) = 0;
virtual int get_bake_texture_count() const = 0; virtual int get_bake_texture_count() const = 0;
virtual Ref<Image> get_bake_texture(int p_index) const = 0; virtual Ref<Image> get_bake_texture(int p_index) const = 0;