feat: updated engine version to 4.4-rc1
This commit is contained in:
parent
ee00efde1f
commit
21ba8e33af
5459 changed files with 1128836 additions and 198305 deletions
|
|
@ -1,4 +1,5 @@
|
|||
#!/usr/bin/env python
|
||||
from misc.utility.scons_hints import *
|
||||
|
||||
Import("env")
|
||||
Import("env_modules")
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@
|
|||
|
||||
#include "lightmapper_rd.h"
|
||||
|
||||
#include "core/string/print_string.h"
|
||||
#include "lm_blendseams.glsl.gen.h"
|
||||
#include "lm_compute.glsl.gen.h"
|
||||
#include "lm_raster.glsl.gen.h"
|
||||
|
|
@ -40,10 +41,14 @@
|
|||
#include "editor/editor_paths.h"
|
||||
#include "editor/editor_settings.h"
|
||||
#include "servers/rendering/rendering_device_binds.h"
|
||||
#include "servers/rendering/rendering_server_globals.h"
|
||||
|
||||
#if defined(VULKAN_ENABLED)
|
||||
#include "drivers/vulkan/rendering_context_driver_vulkan.h"
|
||||
#endif
|
||||
#if defined(METAL_ENABLED)
|
||||
#include "drivers/metal/rendering_context_driver_metal.h"
|
||||
#endif
|
||||
|
||||
//uncomment this if you want to see textures from all the process saved
|
||||
//#define DEBUG_TEXTURES
|
||||
|
|
@ -59,7 +64,7 @@ void LightmapperRD::add_mesh(const MeshData &p_mesh) {
|
|||
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_indirect_energy, float p_angular_distance, float p_shadow_blur) {
|
||||
void LightmapperRD::add_directional_light(const String &p_name, 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;
|
||||
l.type = LIGHT_TYPE_DIRECTIONAL;
|
||||
l.direction[0] = p_direction.x;
|
||||
|
|
@ -74,9 +79,10 @@ void LightmapperRD::add_directional_light(bool p_static, const Vector3 &p_direct
|
|||
l.size = Math::tan(Math::deg_to_rad(p_angular_distance));
|
||||
l.shadow_blur = p_shadow_blur;
|
||||
lights.push_back(l);
|
||||
light_names.push_back(p_name);
|
||||
}
|
||||
|
||||
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) {
|
||||
void LightmapperRD::add_omni_light(const String &p_name, 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;
|
||||
l.type = LIGHT_TYPE_OMNI;
|
||||
l.position[0] = p_position.x;
|
||||
|
|
@ -93,9 +99,10 @@ void LightmapperRD::add_omni_light(bool p_static, const Vector3 &p_position, con
|
|||
l.size = p_size;
|
||||
l.shadow_blur = p_shadow_blur;
|
||||
lights.push_back(l);
|
||||
light_names.push_back(p_name);
|
||||
}
|
||||
|
||||
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) {
|
||||
void LightmapperRD::add_spot_light(const String &p_name, 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;
|
||||
l.type = LIGHT_TYPE_SPOT;
|
||||
l.position[0] = p_position.x;
|
||||
|
|
@ -117,6 +124,7 @@ void LightmapperRD::add_spot_light(bool p_static, const Vector3 &p_position, con
|
|||
l.size = p_size;
|
||||
l.shadow_blur = p_shadow_blur;
|
||||
lights.push_back(l);
|
||||
light_names.push_back(p_name);
|
||||
}
|
||||
|
||||
void LightmapperRD::add_probe(const Vector3 &p_position) {
|
||||
|
|
@ -226,14 +234,14 @@ void LightmapperRD::_sort_triangle_clusters(uint32_t p_cluster_size, uint32_t p_
|
|||
}
|
||||
}
|
||||
|
||||
Lightmapper::BakeError LightmapperRD::_blit_meshes_into_atlas(int p_max_texture_size, int p_denoiser_range, Vector<Ref<Image>> &albedo_images, Vector<Ref<Image>> &emission_images, AABB &bounds, Size2i &atlas_size, int &atlas_slices, BakeStepFunc p_step_function, void *p_bake_userdata) {
|
||||
Lightmapper::BakeError LightmapperRD::_blit_meshes_into_atlas(int p_max_texture_size, int p_denoiser_range, Vector<Ref<Image>> &albedo_images, Vector<Ref<Image>> &emission_images, AABB &bounds, Size2i &atlas_size, int &atlas_slices, float p_supersampling_factor, BakeStepFunc p_step_function, void *p_bake_userdata) {
|
||||
Vector<Size2i> sizes;
|
||||
|
||||
for (int m_i = 0; m_i < mesh_instances.size(); m_i++) {
|
||||
MeshInstance &mi = mesh_instances.write[m_i];
|
||||
Size2i s = Size2i(mi.data.albedo_on_uv2->get_width(), mi.data.albedo_on_uv2->get_height());
|
||||
sizes.push_back(s);
|
||||
atlas_size = atlas_size.max(s + Size2i(2, 2).maxi(p_denoiser_range));
|
||||
atlas_size = atlas_size.max(s + Size2i(2, 2).maxi(p_denoiser_range) * p_supersampling_factor);
|
||||
}
|
||||
|
||||
int max = nearest_power_of_2_templated(atlas_size.width);
|
||||
|
|
@ -244,7 +252,9 @@ Lightmapper::BakeError LightmapperRD::_blit_meshes_into_atlas(int p_max_texture_
|
|||
}
|
||||
|
||||
if (p_step_function) {
|
||||
p_step_function(0.1, RTR("Determining optimal atlas size"), p_bake_userdata, true);
|
||||
if (p_step_function(0.1, RTR("Determining optimal atlas size"), p_bake_userdata, true)) {
|
||||
return BAKE_ERROR_USER_ABORTED;
|
||||
}
|
||||
}
|
||||
|
||||
atlas_size = Size2i(max, max);
|
||||
|
|
@ -261,7 +271,10 @@ Lightmapper::BakeError LightmapperRD::_blit_meshes_into_atlas(int p_max_texture_
|
|||
source_sizes.resize(sizes.size());
|
||||
source_indices.resize(sizes.size());
|
||||
for (int i = 0; i < source_indices.size(); i++) {
|
||||
source_sizes.write[i] = sizes[i] + Vector2i(2, 2).maxi(p_denoiser_range); // Add padding between lightmaps.
|
||||
// Add padding between lightmaps.
|
||||
// Scale the padding if the lightmap will be downsampled at the end of the baking process
|
||||
// Otherwise the padding would be insufficient.
|
||||
source_sizes.write[i] = sizes[i] + Vector2i(2, 2).maxi(p_denoiser_range) * p_supersampling_factor;
|
||||
source_indices.write[i] = i;
|
||||
}
|
||||
Vector<Vector3i> atlas_offsets;
|
||||
|
|
@ -321,7 +334,9 @@ Lightmapper::BakeError LightmapperRD::_blit_meshes_into_atlas(int p_max_texture_
|
|||
emission_images.resize(atlas_slices);
|
||||
|
||||
if (p_step_function) {
|
||||
p_step_function(0.2, RTR("Blitting albedo and emission"), p_bake_userdata, true);
|
||||
if (p_step_function(0.2, RTR("Blitting albedo and emission"), p_bake_userdata, true)) {
|
||||
return BAKE_ERROR_USER_ABORTED;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < atlas_slices; i++) {
|
||||
|
|
@ -467,7 +482,14 @@ void LightmapperRD::_create_acceleration_structures(RenderingDevice *rd, Size2i
|
|||
t.max_bounds[0] = taabb.position.x + MAX(taabb.size.x, 0.0001);
|
||||
t.max_bounds[1] = taabb.position.y + MAX(taabb.size.y, 0.0001);
|
||||
t.max_bounds[2] = taabb.position.z + MAX(taabb.size.z, 0.0001);
|
||||
t.pad0 = t.pad1 = 0; //make valgrind not complain
|
||||
|
||||
t.cull_mode = RS::CULL_MODE_BACK;
|
||||
|
||||
RID material = mi.data.material[i];
|
||||
if (material.is_valid()) {
|
||||
t.cull_mode = RSG::material_storage->material_get_cull_mode(material);
|
||||
}
|
||||
t.pad1 = 0; //make valgrind not complain
|
||||
triangles.push_back(t);
|
||||
slice_triangle_count.write[t.slice]++;
|
||||
}
|
||||
|
|
@ -715,7 +737,7 @@ void LightmapperRD::_raster_geometry(RenderingDevice *rd, Size2i atlas_size, int
|
|||
raster_push_constant.uv_offset[0] = -0.5f / float(atlas_size.x);
|
||||
raster_push_constant.uv_offset[1] = -0.5f / float(atlas_size.y);
|
||||
|
||||
RD::DrawListID draw_list = rd->draw_list_begin(framebuffers[i], RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_DISCARD, clear_colors);
|
||||
RD::DrawListID draw_list = rd->draw_list_begin(framebuffers[i], RD::DRAW_CLEAR_ALL, clear_colors, 1.0f, 0, Rect2(), RDD::BreadcrumbMarker::LIGHTMAPPER_PASS);
|
||||
//draw opaque
|
||||
rd->draw_list_bind_render_pipeline(draw_list, raster_pipeline);
|
||||
rd->draw_list_bind_uniform_set(draw_list, raster_base_uniform, 0);
|
||||
|
|
@ -790,9 +812,38 @@ LightmapperRD::BakeError LightmapperRD::_dilate(RenderingDevice *rd, Ref<RDShade
|
|||
return BAKE_OK;
|
||||
}
|
||||
|
||||
Error LightmapperRD::_store_pfm(RenderingDevice *p_rd, RID p_atlas_tex, int p_index, const Size2i &p_atlas_size, const String &p_name) {
|
||||
LightmapperRD::BakeError LightmapperRD::_pack_l1(RenderingDevice *rd, Ref<RDShaderFile> &compute_shader, RID &compute_base_uniform_set, PushConstant &push_constant, RID &source_light_tex, RID &dest_light_tex, const Size2i &atlas_size, int atlas_slices) {
|
||||
Vector<RD::Uniform> uniforms = dilate_or_denoise_common_uniforms(source_light_tex, dest_light_tex);
|
||||
|
||||
RID compute_shader_pack = rd->shader_create_from_spirv(compute_shader->get_spirv_stages("pack_coeffs"));
|
||||
ERR_FAIL_COND_V(compute_shader_pack.is_null(), BAKE_ERROR_LIGHTMAP_CANT_PRE_BAKE_MESHES); //internal check, should not happen
|
||||
RID compute_shader_pack_pipeline = rd->compute_pipeline_create(compute_shader_pack);
|
||||
|
||||
RID dilate_uniform_set = rd->uniform_set_create(uniforms, compute_shader_pack, 1);
|
||||
|
||||
RD::ComputeListID compute_list = rd->compute_list_begin();
|
||||
rd->compute_list_bind_compute_pipeline(compute_list, compute_shader_pack_pipeline);
|
||||
rd->compute_list_bind_uniform_set(compute_list, compute_base_uniform_set, 0);
|
||||
rd->compute_list_bind_uniform_set(compute_list, dilate_uniform_set, 1);
|
||||
push_constant.region_ofs[0] = 0;
|
||||
push_constant.region_ofs[1] = 0;
|
||||
Vector3i group_size(Math::division_round_up(atlas_size.x, 8), Math::division_round_up(atlas_size.y, 8), 1); //restore group size
|
||||
|
||||
for (int i = 0; i < atlas_slices; i++) {
|
||||
push_constant.atlas_slice = i;
|
||||
rd->compute_list_set_push_constant(compute_list, &push_constant, sizeof(PushConstant));
|
||||
rd->compute_list_dispatch(compute_list, group_size.x, group_size.y, group_size.z);
|
||||
//no barrier, let them run all together
|
||||
}
|
||||
rd->compute_list_end();
|
||||
rd->free(compute_shader_pack);
|
||||
|
||||
return BAKE_OK;
|
||||
}
|
||||
|
||||
Error LightmapperRD::_store_pfm(RenderingDevice *p_rd, RID p_atlas_tex, int p_index, const Size2i &p_atlas_size, const String &p_name, bool p_shadowmask) {
|
||||
Vector<uint8_t> data = p_rd->texture_get_data(p_atlas_tex, p_index);
|
||||
Ref<Image> img = Image::create_from_data(p_atlas_size.width, p_atlas_size.height, false, Image::FORMAT_RGBAH, data);
|
||||
Ref<Image> img = Image::create_from_data(p_atlas_size.width, p_atlas_size.height, false, p_shadowmask ? Image::FORMAT_RGBA8 : Image::FORMAT_RGBAH, data);
|
||||
img->convert(Image::FORMAT_RGBF);
|
||||
Vector<uint8_t> data_float = img->get_data();
|
||||
|
||||
|
|
@ -812,7 +863,7 @@ Error LightmapperRD::_store_pfm(RenderingDevice *p_rd, RID p_atlas_tex, int p_in
|
|||
return OK;
|
||||
}
|
||||
|
||||
Ref<Image> LightmapperRD::_read_pfm(const String &p_name) {
|
||||
Ref<Image> LightmapperRD::_read_pfm(const String &p_name, bool p_shadowmask) {
|
||||
Error err = OK;
|
||||
Ref<FileAccess> file = FileAccess::open(p_name, FileAccess::READ, &err);
|
||||
ERR_FAIL_COND_V_MSG(err, Ref<Image>(), vformat("Can't load PFM at path: '%s'.", p_name));
|
||||
|
|
@ -845,23 +896,23 @@ Ref<Image> LightmapperRD::_read_pfm(const String &p_name) {
|
|||
}
|
||||
#endif
|
||||
Ref<Image> img = Image::create_from_data(new_width, new_height, false, Image::FORMAT_RGBF, new_data);
|
||||
img->convert(Image::FORMAT_RGBAH);
|
||||
img->convert(p_shadowmask ? Image::FORMAT_RGBA8 : Image::FORMAT_RGBAH);
|
||||
return img;
|
||||
}
|
||||
|
||||
LightmapperRD::BakeError LightmapperRD::_denoise_oidn(RenderingDevice *p_rd, RID p_source_light_tex, RID p_source_normal_tex, RID p_dest_light_tex, const Size2i &p_atlas_size, int p_atlas_slices, bool p_bake_sh, const String &p_exe) {
|
||||
LightmapperRD::BakeError LightmapperRD::_denoise_oidn(RenderingDevice *p_rd, RID p_source_light_tex, RID p_source_normal_tex, RID p_dest_light_tex, const Size2i &p_atlas_size, int p_atlas_slices, bool p_bake_sh, bool p_shadowmask, const String &p_exe) {
|
||||
Ref<DirAccess> da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
|
||||
|
||||
for (int i = 0; i < p_atlas_slices; i++) {
|
||||
String fname_norm_in = EditorPaths::get_singleton()->get_cache_dir().path_join(vformat("temp_norm_%d.pfm", i));
|
||||
_store_pfm(p_rd, p_source_normal_tex, i, p_atlas_size, fname_norm_in);
|
||||
_store_pfm(p_rd, p_source_normal_tex, i, p_atlas_size, fname_norm_in, false);
|
||||
|
||||
for (int j = 0; j < (p_bake_sh ? 4 : 1); j++) {
|
||||
int index = i * (p_bake_sh ? 4 : 1) + j;
|
||||
String fname_light_in = EditorPaths::get_singleton()->get_cache_dir().path_join(vformat("temp_light_%d.pfm", index));
|
||||
String fname_out = EditorPaths::get_singleton()->get_cache_dir().path_join(vformat("temp_denoised_%d.pfm", index));
|
||||
|
||||
_store_pfm(p_rd, p_source_light_tex, index, p_atlas_size, fname_light_in);
|
||||
_store_pfm(p_rd, p_source_light_tex, index, p_atlas_size, fname_light_in, p_shadowmask);
|
||||
|
||||
List<String> args;
|
||||
args.push_back("--device");
|
||||
|
|
@ -870,7 +921,7 @@ LightmapperRD::BakeError LightmapperRD::_denoise_oidn(RenderingDevice *p_rd, RID
|
|||
args.push_back("--filter");
|
||||
args.push_back("RTLightmap");
|
||||
|
||||
args.push_back("--hdr");
|
||||
args.push_back(p_shadowmask ? "--ldr" : "--hdr");
|
||||
args.push_back(fname_light_in);
|
||||
|
||||
args.push_back("--nrm");
|
||||
|
|
@ -892,7 +943,7 @@ LightmapperRD::BakeError LightmapperRD::_denoise_oidn(RenderingDevice *p_rd, RID
|
|||
ERR_FAIL_V_MSG(BAKE_ERROR_LIGHTMAP_CANT_PRE_BAKE_MESHES, vformat("OIDN denoiser failed, return code: %d", exitcode));
|
||||
}
|
||||
|
||||
Ref<Image> img = _read_pfm(fname_out);
|
||||
Ref<Image> img = _read_pfm(fname_out, p_shadowmask);
|
||||
da->remove(fname_out);
|
||||
|
||||
ERR_FAIL_COND_V(img.is_null(), BAKE_ERROR_LIGHTMAP_CANT_PRE_BAKE_MESHES);
|
||||
|
|
@ -915,7 +966,7 @@ LightmapperRD::BakeError LightmapperRD::_denoise_oidn(RenderingDevice *p_rd, RID
|
|||
return BAKE_OK;
|
||||
}
|
||||
|
||||
LightmapperRD::BakeError LightmapperRD::_denoise(RenderingDevice *p_rd, Ref<RDShaderFile> &p_compute_shader, const RID &p_compute_base_uniform_set, PushConstant &p_push_constant, RID p_source_light_tex, RID p_source_normal_tex, RID p_dest_light_tex, float p_denoiser_strength, int p_denoiser_range, const Size2i &p_atlas_size, int p_atlas_slices, bool p_bake_sh, BakeStepFunc p_step_function) {
|
||||
LightmapperRD::BakeError LightmapperRD::_denoise(RenderingDevice *p_rd, Ref<RDShaderFile> &p_compute_shader, const RID &p_compute_base_uniform_set, PushConstant &p_push_constant, RID p_source_light_tex, RID p_source_normal_tex, RID p_dest_light_tex, float p_denoiser_strength, int p_denoiser_range, const Size2i &p_atlas_size, int p_atlas_slices, bool p_bake_sh, BakeStepFunc p_step_function, void *p_bake_userdata) {
|
||||
RID denoise_params_buffer = p_rd->uniform_buffer_create(sizeof(DenoiseParams));
|
||||
DenoiseParams denoise_params;
|
||||
denoise_params.spatial_bandwidth = 5.0f;
|
||||
|
|
@ -951,7 +1002,8 @@ LightmapperRD::BakeError LightmapperRD::_denoise(RenderingDevice *p_rd, Ref<RDSh
|
|||
// We denoise in fixed size regions and synchronize execution to avoid GPU timeouts.
|
||||
// We use a region with 1/4 the amount of pixels if we're denoising SH lightmaps, as
|
||||
// all four of them are denoised in the shader in one dispatch.
|
||||
const int max_region_size = p_bake_sh ? 512 : 1024;
|
||||
const int user_region_size = nearest_power_of_2_templated(int(GLOBAL_GET("rendering/lightmapping/bake_performance/region_size")));
|
||||
const int max_region_size = p_bake_sh ? user_region_size / 2 : user_region_size;
|
||||
int x_regions = Math::division_round_up(p_atlas_size.width, max_region_size);
|
||||
int y_regions = Math::division_round_up(p_atlas_size.height, max_region_size);
|
||||
for (int s = 0; s < p_atlas_slices; s++) {
|
||||
|
|
@ -978,6 +1030,13 @@ LightmapperRD::BakeError LightmapperRD::_denoise(RenderingDevice *p_rd, Ref<RDSh
|
|||
p_rd->sync();
|
||||
}
|
||||
}
|
||||
if (p_step_function) {
|
||||
int percent = (s + 1) * 100 / p_atlas_slices;
|
||||
float p = float(s) / p_atlas_slices * 0.1;
|
||||
if (p_step_function(0.8 + p, vformat(RTR("Denoising %d%%"), percent), p_bake_userdata, false)) {
|
||||
return BAKE_ERROR_USER_ABORTED;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
p_rd->free(compute_shader_denoise);
|
||||
|
|
@ -986,7 +1045,7 @@ LightmapperRD::BakeError LightmapperRD::_denoise(RenderingDevice *p_rd, Ref<RDSh
|
|||
return BAKE_OK;
|
||||
}
|
||||
|
||||
LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_denoiser, float p_denoiser_strength, int p_denoiser_range, 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) {
|
||||
LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_denoiser, float p_denoiser_strength, int p_denoiser_range, int p_bounces, float p_bounce_indirect_energy, float p_bias, int p_max_texture_size, bool p_bake_sh, bool p_bake_shadowmask, 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, float p_supersampling_factor) {
|
||||
int denoiser = GLOBAL_GET("rendering/lightmapping/denoising/denoiser");
|
||||
String oidn_path = EDITOR_GET("filesystem/tools/oidn/oidn_denoise_path");
|
||||
|
||||
|
|
@ -1007,7 +1066,8 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d
|
|||
if (p_step_function) {
|
||||
p_step_function(0.0, RTR("Begin Bake"), p_bake_userdata, true);
|
||||
}
|
||||
bake_textures.clear();
|
||||
lightmap_textures.clear();
|
||||
shadowmask_textures.clear();
|
||||
int grid_size = 128;
|
||||
|
||||
/* STEP 1: Fetch material textures and compute the bounds */
|
||||
|
|
@ -1018,11 +1078,40 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d
|
|||
Vector<Ref<Image>> albedo_images;
|
||||
Vector<Ref<Image>> emission_images;
|
||||
|
||||
BakeError bake_error = _blit_meshes_into_atlas(p_max_texture_size, p_denoiser_range, albedo_images, emission_images, bounds, atlas_size, atlas_slices, p_step_function, p_bake_userdata);
|
||||
BakeError bake_error = _blit_meshes_into_atlas(p_max_texture_size, p_denoiser_range, albedo_images, emission_images, bounds, atlas_size, atlas_slices, p_supersampling_factor, p_step_function, p_bake_userdata);
|
||||
if (bake_error != BAKE_OK) {
|
||||
return bake_error;
|
||||
}
|
||||
|
||||
// The index of the directional light used for shadowmasking.
|
||||
int shadowmask_light_idx = -1;
|
||||
|
||||
// If there are no valid directional lights for shadowmasking, the entire
|
||||
// scene would be shadowed and this saves baking time.
|
||||
if (p_bake_shadowmask) {
|
||||
int shadowmask_lights_count = 0;
|
||||
|
||||
for (int i = 0; i < lights.size(); i++) {
|
||||
if (lights[i].type == LightType::LIGHT_TYPE_DIRECTIONAL && !lights[i].static_bake) {
|
||||
if (shadowmask_light_idx < 0) {
|
||||
shadowmask_light_idx = i;
|
||||
}
|
||||
|
||||
shadowmask_lights_count += 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (shadowmask_light_idx < 0) {
|
||||
p_bake_shadowmask = false;
|
||||
WARN_PRINT("Shadowmask disabled: no directional light with their bake mode set to dynamic exists.");
|
||||
|
||||
} else if (shadowmask_lights_count > 1) {
|
||||
WARN_PRINT(
|
||||
vformat("%d directional lights detected for shadowmask baking. Only %s will be used.",
|
||||
shadowmask_lights_count, light_names[shadowmask_light_idx]));
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef DEBUG_TEXTURES
|
||||
for (int i = 0; i < atlas_slices; i++) {
|
||||
albedo_images[i]->save_png("res://0_albedo_" + itos(i) + ".png");
|
||||
|
|
@ -1038,10 +1127,16 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d
|
|||
RenderingDevice *rd = RenderingServer::get_singleton()->create_local_rendering_device();
|
||||
if (rd == nullptr) {
|
||||
#if defined(RD_ENABLED)
|
||||
#if defined(VULKAN_ENABLED)
|
||||
rcd = memnew(RenderingContextDriverVulkan);
|
||||
#if defined(METAL_ENABLED)
|
||||
rcd = memnew(RenderingContextDriverMetal);
|
||||
rd = memnew(RenderingDevice);
|
||||
#endif
|
||||
#if defined(VULKAN_ENABLED)
|
||||
if (rcd == nullptr) {
|
||||
rcd = memnew(RenderingContextDriverVulkan);
|
||||
rd = memnew(RenderingDevice);
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
if (rcd != nullptr && rd != nullptr) {
|
||||
err = rcd->initialize();
|
||||
|
|
@ -1070,17 +1165,23 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d
|
|||
RID light_accum_tex;
|
||||
RID light_accum_tex2;
|
||||
RID light_environment_tex;
|
||||
RID shadowmask_tex;
|
||||
RID shadowmask_tex2;
|
||||
|
||||
#define FREE_TEXTURES \
|
||||
rd->free(albedo_array_tex); \
|
||||
rd->free(emission_array_tex); \
|
||||
rd->free(normal_tex); \
|
||||
rd->free(position_tex); \
|
||||
rd->free(unocclude_tex); \
|
||||
rd->free(light_source_tex); \
|
||||
rd->free(light_accum_tex2); \
|
||||
rd->free(light_accum_tex); \
|
||||
rd->free(light_environment_tex);
|
||||
#define FREE_TEXTURES \
|
||||
rd->free(albedo_array_tex); \
|
||||
rd->free(emission_array_tex); \
|
||||
rd->free(normal_tex); \
|
||||
rd->free(position_tex); \
|
||||
rd->free(unocclude_tex); \
|
||||
rd->free(light_source_tex); \
|
||||
rd->free(light_accum_tex2); \
|
||||
rd->free(light_accum_tex); \
|
||||
rd->free(light_environment_tex); \
|
||||
if (p_bake_shadowmask) { \
|
||||
rd->free(shadowmask_tex); \
|
||||
rd->free(shadowmask_tex2); \
|
||||
}
|
||||
|
||||
{ // create all textures
|
||||
|
||||
|
|
@ -1112,9 +1213,22 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d
|
|||
position_tex = rd->texture_create(tf, RD::TextureView());
|
||||
unocclude_tex = rd->texture_create(tf, RD::TextureView());
|
||||
|
||||
tf.format = RD::DATA_FORMAT_R16G16B16A16_SFLOAT;
|
||||
tf.usage_bits = RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT | RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_STORAGE_BIT | RD::TEXTURE_USAGE_CAN_COPY_FROM_BIT | RD::TEXTURE_USAGE_CAN_COPY_TO_BIT | RD::TEXTURE_USAGE_CAN_UPDATE_BIT;
|
||||
|
||||
// shadowmask
|
||||
if (p_bake_shadowmask) {
|
||||
tf.format = RD::DATA_FORMAT_R8G8B8A8_UNORM;
|
||||
|
||||
shadowmask_tex = rd->texture_create(tf, RD::TextureView());
|
||||
rd->texture_clear(shadowmask_tex, Color(0, 0, 0, 0), 0, 1, 0, atlas_slices);
|
||||
|
||||
shadowmask_tex2 = rd->texture_create(tf, RD::TextureView());
|
||||
rd->texture_clear(shadowmask_tex2, Color(0, 0, 0, 0), 0, 1, 0, atlas_slices);
|
||||
}
|
||||
|
||||
// lightmap
|
||||
tf.format = RD::DATA_FORMAT_R16G16B16A16_SFLOAT;
|
||||
|
||||
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);
|
||||
|
||||
|
|
@ -1217,12 +1331,24 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d
|
|||
bake_parameters.exposure_normalization = p_exposure_normalization;
|
||||
bake_parameters.bounces = p_bounces;
|
||||
bake_parameters.bounce_indirect_energy = p_bounce_indirect_energy;
|
||||
bake_parameters.shadowmask_light_idx = shadowmask_light_idx;
|
||||
// Same number of rays for transparency regardless of quality (it's more of a retry rather than shooting new ones).
|
||||
bake_parameters.transparency_rays = GLOBAL_GET("rendering/lightmapping/bake_performance/max_transparency_rays");
|
||||
bake_parameters.supersampling_factor = p_supersampling_factor;
|
||||
|
||||
bake_parameters_buffer = rd->uniform_buffer_create(sizeof(BakeParameters));
|
||||
rd->buffer_update(bake_parameters_buffer, 0, sizeof(BakeParameters), &bake_parameters);
|
||||
|
||||
if (p_step_function) {
|
||||
p_step_function(0.47, RTR("Preparing shaders"), p_bake_userdata, true);
|
||||
if (p_step_function(0.47, RTR("Preparing shaders"), p_bake_userdata, true)) {
|
||||
FREE_TEXTURES
|
||||
FREE_BUFFERS
|
||||
memdelete(rd);
|
||||
if (rcd != nullptr) {
|
||||
memdelete(rcd);
|
||||
}
|
||||
return BAKE_ERROR_USER_ABORTED;
|
||||
}
|
||||
}
|
||||
|
||||
//shaders
|
||||
|
|
@ -1362,6 +1488,7 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d
|
|||
tf.texture_type = RD::TEXTURE_TYPE_2D;
|
||||
tf.usage_bits = RD::TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;
|
||||
tf.format = RD::DATA_FORMAT_D32_SFLOAT;
|
||||
tf.is_discardable = true;
|
||||
|
||||
raster_depth_buffer = rd->texture_create(tf, RD::TextureView());
|
||||
}
|
||||
|
|
@ -1405,6 +1532,10 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d
|
|||
defines += "\n#define USE_LIGHT_TEXTURE_FOR_BOUNCES\n";
|
||||
}
|
||||
|
||||
if (p_bake_shadowmask) {
|
||||
defines += "\n#define USE_SHADOWMASK\n";
|
||||
}
|
||||
|
||||
compute_shader.instantiate();
|
||||
err = compute_shader->parse_versions_from_text(lm_compute_shader_glsl, defines);
|
||||
if (err != OK) {
|
||||
|
|
@ -1454,7 +1585,17 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d
|
|||
rd->sync();
|
||||
|
||||
if (p_step_function) {
|
||||
p_step_function(0.49, RTR("Un-occluding geometry"), p_bake_userdata, true);
|
||||
if (p_step_function(0.49, RTR("Un-occluding geometry"), p_bake_userdata, true)) {
|
||||
FREE_TEXTURES
|
||||
FREE_BUFFERS
|
||||
FREE_RASTER_RESOURCES
|
||||
FREE_COMPUTE_RESOURCES
|
||||
memdelete(rd);
|
||||
if (rcd != nullptr) {
|
||||
memdelete(rcd);
|
||||
}
|
||||
return BAKE_ERROR_USER_ABORTED;
|
||||
}
|
||||
}
|
||||
|
||||
PushConstant push_constant;
|
||||
|
|
@ -1496,9 +1637,23 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d
|
|||
}
|
||||
|
||||
if (p_step_function) {
|
||||
p_step_function(0.5, RTR("Plot direct lighting"), p_bake_userdata, true);
|
||||
if (p_step_function(0.5, RTR("Plot direct lighting"), p_bake_userdata, true)) {
|
||||
FREE_TEXTURES
|
||||
FREE_BUFFERS
|
||||
FREE_RASTER_RESOURCES
|
||||
FREE_COMPUTE_RESOURCES
|
||||
memdelete(rd);
|
||||
if (rcd != nullptr) {
|
||||
memdelete(rcd);
|
||||
}
|
||||
return BAKE_ERROR_USER_ABORTED;
|
||||
}
|
||||
}
|
||||
|
||||
const int max_region_size = nearest_power_of_2_templated(int(GLOBAL_GET("rendering/lightmapping/bake_performance/region_size")));
|
||||
const int x_regions = Math::division_round_up(atlas_size.width, max_region_size);
|
||||
const int y_regions = Math::division_round_up(atlas_size.height, max_region_size);
|
||||
|
||||
// Set ray count to the quality used for direct light and bounces.
|
||||
switch (p_quality) {
|
||||
case BAKE_QUALITY_LOW: {
|
||||
|
|
@ -1556,22 +1711,64 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d
|
|||
u.append_id(light_accum_tex);
|
||||
uniforms.push_back(u);
|
||||
}
|
||||
|
||||
if (p_bake_shadowmask) {
|
||||
RD::Uniform u;
|
||||
u.uniform_type = RD::UNIFORM_TYPE_IMAGE;
|
||||
u.binding = 5;
|
||||
u.append_id(shadowmask_tex);
|
||||
uniforms.push_back(u);
|
||||
}
|
||||
}
|
||||
|
||||
RID light_uniform_set = rd->uniform_set_create(uniforms, compute_shader_primary, 1);
|
||||
|
||||
RD::ComputeListID compute_list = rd->compute_list_begin();
|
||||
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, light_uniform_set, 1);
|
||||
int count = 0;
|
||||
for (int s = 0; s < atlas_slices; s++) {
|
||||
push_constant.atlas_slice = s;
|
||||
|
||||
for (int i = 0; i < atlas_slices; i++) {
|
||||
push_constant.atlas_slice = i;
|
||||
rd->compute_list_set_push_constant(compute_list, &push_constant, sizeof(PushConstant));
|
||||
rd->compute_list_dispatch(compute_list, group_size.x, group_size.y, group_size.z);
|
||||
//no barrier, let them run all together
|
||||
for (int i = 0; i < x_regions; i++) {
|
||||
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;
|
||||
|
||||
push_constant.region_ofs[0] = x;
|
||||
push_constant.region_ofs[1] = y;
|
||||
|
||||
group_size = Vector3i(Math::division_round_up(w, 8), Math::division_round_up(h, 8), 1);
|
||||
RD::ComputeListID compute_list = rd->compute_list_begin();
|
||||
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, light_uniform_set, 1);
|
||||
rd->compute_list_set_push_constant(compute_list, &push_constant, sizeof(PushConstant));
|
||||
rd->compute_list_dispatch(compute_list, group_size.x, group_size.y, group_size.z);
|
||||
rd->compute_list_end();
|
||||
|
||||
rd->submit();
|
||||
rd->sync();
|
||||
|
||||
count++;
|
||||
if (p_step_function) {
|
||||
int total = (atlas_slices * x_regions * y_regions);
|
||||
int percent = count * 100 / total;
|
||||
float p = float(count) / total * 0.1;
|
||||
if (p_step_function(0.5 + p, vformat(RTR("Plot direct lighting %d%%"), percent), p_bake_userdata, false)) {
|
||||
FREE_TEXTURES
|
||||
FREE_BUFFERS
|
||||
FREE_RASTER_RESOURCES
|
||||
FREE_COMPUTE_RESOURCES
|
||||
memdelete(rd);
|
||||
if (rcd != nullptr) {
|
||||
memdelete(rcd);
|
||||
}
|
||||
return BAKE_ERROR_USER_ABORTED;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
rd->compute_list_end(); //done
|
||||
}
|
||||
|
||||
#ifdef DEBUG_TEXTURES
|
||||
|
|
@ -1581,12 +1778,17 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d
|
|||
Ref<Image> img = Image::create_from_data(atlas_size.width, atlas_size.height, false, Image::FORMAT_RGBAH, s);
|
||||
img->save_exr("res://2_light_primary_" + itos(i) + ".exr", false);
|
||||
}
|
||||
|
||||
if (p_bake_sh) {
|
||||
for (int i = 0; i < atlas_slices * 4; i++) {
|
||||
Vector<uint8_t> s = rd->texture_get_data(light_accum_tex, i);
|
||||
Ref<Image> img = Image::create_from_data(atlas_size.width, atlas_size.height, false, Image::FORMAT_RGBAH, s);
|
||||
img->save_exr("res://2_light_primary_accum_" + itos(i) + ".exr", false);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/* SECONDARY (indirect) LIGHT PASS(ES) */
|
||||
if (p_step_function) {
|
||||
p_step_function(0.6, RTR("Integrate indirect lighting"), p_bake_userdata, true);
|
||||
}
|
||||
|
||||
if (p_bounces > 0) {
|
||||
Vector<RD::Uniform> uniforms;
|
||||
|
|
@ -1639,16 +1841,22 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d
|
|||
RID secondary_uniform_set;
|
||||
secondary_uniform_set = 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_rays = GLOBAL_GET("rendering/lightmapping/bake_performance/max_rays_per_pass");
|
||||
|
||||
int x_regions = Math::division_round_up(atlas_size.width, max_region_size);
|
||||
int y_regions = Math::division_round_up(atlas_size.height, max_region_size);
|
||||
|
||||
const int max_rays = GLOBAL_GET("rendering/lightmapping/bake_performance/max_rays_per_pass");
|
||||
int ray_iterations = Math::division_round_up((int32_t)push_constant.ray_count, max_rays);
|
||||
|
||||
rd->submit();
|
||||
rd->sync();
|
||||
if (p_step_function) {
|
||||
if (p_step_function(0.6, RTR("Integrate indirect lighting"), p_bake_userdata, true)) {
|
||||
FREE_TEXTURES
|
||||
FREE_BUFFERS
|
||||
FREE_RASTER_RESOURCES
|
||||
FREE_COMPUTE_RESOURCES
|
||||
memdelete(rd);
|
||||
if (rcd != nullptr) {
|
||||
memdelete(rcd);
|
||||
}
|
||||
return BAKE_ERROR_USER_ABORTED;
|
||||
}
|
||||
}
|
||||
|
||||
int count = 0;
|
||||
for (int s = 0; s < atlas_slices; s++) {
|
||||
|
|
@ -1686,7 +1894,17 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d
|
|||
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("Integrate indirect lighting %d%%"), percent), p_bake_userdata, false);
|
||||
if (p_step_function(0.6 + p, vformat(RTR("Integrate indirect lighting %d%%"), percent), p_bake_userdata, false)) {
|
||||
FREE_TEXTURES
|
||||
FREE_BUFFERS
|
||||
FREE_RASTER_RESOURCES
|
||||
FREE_COMPUTE_RESOURCES
|
||||
memdelete(rd);
|
||||
if (rcd != nullptr) {
|
||||
memdelete(rcd);
|
||||
}
|
||||
return BAKE_ERROR_USER_ABORTED;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1702,7 +1920,20 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d
|
|||
light_probe_buffer = rd->storage_buffer_create(sizeof(float) * 4 * 9 * probe_positions.size());
|
||||
|
||||
if (p_step_function) {
|
||||
p_step_function(0.7, RTR("Baking lightprobes"), p_bake_userdata, true);
|
||||
if (p_step_function(0.7, RTR("Baking light probes"), p_bake_userdata, true)) {
|
||||
FREE_TEXTURES
|
||||
FREE_BUFFERS
|
||||
FREE_RASTER_RESOURCES
|
||||
FREE_COMPUTE_RESOURCES
|
||||
if (probe_positions.size() > 0) {
|
||||
rd->free(light_probe_buffer);
|
||||
}
|
||||
memdelete(rd);
|
||||
if (rcd != nullptr) {
|
||||
memdelete(rcd);
|
||||
}
|
||||
return BAKE_ERROR_USER_ABORTED;
|
||||
}
|
||||
}
|
||||
|
||||
Vector<RD::Uniform> uniforms;
|
||||
|
|
@ -1770,7 +2001,20 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d
|
|||
if (p_step_function) {
|
||||
int percent = i * 100 / ray_iterations;
|
||||
float p = float(i) / ray_iterations * 0.1;
|
||||
p_step_function(0.7 + p, vformat(RTR("Integrating light probes %d%%"), percent), p_bake_userdata, false);
|
||||
if (p_step_function(0.7 + p, vformat(RTR("Integrating light probes %d%%"), percent), p_bake_userdata, false)) {
|
||||
FREE_TEXTURES
|
||||
FREE_BUFFERS
|
||||
FREE_RASTER_RESOURCES
|
||||
FREE_COMPUTE_RESOURCES
|
||||
if (probe_positions.size() > 0) {
|
||||
rd->free(light_probe_buffer);
|
||||
}
|
||||
memdelete(rd);
|
||||
if (rcd != nullptr) {
|
||||
memdelete(rcd);
|
||||
}
|
||||
return BAKE_ERROR_USER_ABORTED;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1792,18 +2036,46 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d
|
|||
|
||||
if (p_use_denoiser) {
|
||||
if (p_step_function) {
|
||||
p_step_function(0.8, RTR("Denoising"), p_bake_userdata, true);
|
||||
if (p_step_function(0.8, RTR("Denoising"), p_bake_userdata, true)) {
|
||||
FREE_TEXTURES
|
||||
FREE_BUFFERS
|
||||
FREE_RASTER_RESOURCES
|
||||
FREE_COMPUTE_RESOURCES
|
||||
if (probe_positions.size() > 0) {
|
||||
rd->free(light_probe_buffer);
|
||||
}
|
||||
memdelete(rd);
|
||||
if (rcd != nullptr) {
|
||||
memdelete(rcd);
|
||||
}
|
||||
return BAKE_ERROR_USER_ABORTED;
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
BakeError error;
|
||||
if (denoiser == 1) {
|
||||
// OIDN (external).
|
||||
error = _denoise_oidn(rd, light_accum_tex, normal_tex, light_accum_tex, atlas_size, atlas_slices, p_bake_sh, oidn_path);
|
||||
error = _denoise_oidn(rd, light_accum_tex, normal_tex, light_accum_tex, atlas_size, atlas_slices, p_bake_sh, false, oidn_path);
|
||||
} else {
|
||||
// JNLM (built-in).
|
||||
SWAP(light_accum_tex, light_accum_tex2);
|
||||
error = _denoise(rd, compute_shader, compute_base_uniform_set, push_constant, light_accum_tex2, normal_tex, light_accum_tex, p_denoiser_strength, p_denoiser_range, atlas_size, atlas_slices, p_bake_sh, p_step_function);
|
||||
error = _denoise(rd, compute_shader, compute_base_uniform_set, push_constant, light_accum_tex2, normal_tex, light_accum_tex, p_denoiser_strength, p_denoiser_range, atlas_size, atlas_slices, p_bake_sh, p_step_function, p_bake_userdata);
|
||||
}
|
||||
if (unlikely(error != BAKE_OK)) {
|
||||
return error;
|
||||
}
|
||||
}
|
||||
|
||||
if (p_bake_shadowmask) {
|
||||
BakeError error;
|
||||
if (denoiser == 1) {
|
||||
// OIDN (external).
|
||||
error = _denoise_oidn(rd, shadowmask_tex, normal_tex, shadowmask_tex, atlas_size, atlas_slices, false, true, oidn_path);
|
||||
} else {
|
||||
// JNLM (built-in).
|
||||
SWAP(shadowmask_tex, shadowmask_tex2);
|
||||
error = _denoise(rd, compute_shader, compute_base_uniform_set, push_constant, shadowmask_tex2, normal_tex, shadowmask_tex, p_denoiser_strength, p_denoiser_range, atlas_size, atlas_slices, false, p_step_function, p_bake_userdata);
|
||||
}
|
||||
if (unlikely(error != BAKE_OK)) {
|
||||
return error;
|
||||
|
|
@ -1811,12 +2083,22 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d
|
|||
}
|
||||
}
|
||||
|
||||
/* DILATE */
|
||||
|
||||
{
|
||||
SWAP(light_accum_tex, light_accum_tex2);
|
||||
BakeError error = _dilate(rd, compute_shader, compute_base_uniform_set, push_constant, light_accum_tex2, light_accum_tex, atlas_size, atlas_slices * (p_bake_sh ? 4 : 1));
|
||||
if (unlikely(error != BAKE_OK)) {
|
||||
return error;
|
||||
}
|
||||
|
||||
if (p_bake_shadowmask) {
|
||||
SWAP(shadowmask_tex, shadowmask_tex2);
|
||||
error = _dilate(rd, compute_shader, compute_base_uniform_set, push_constant, shadowmask_tex2, shadowmask_tex, atlas_size, atlas_slices);
|
||||
if (unlikely(error != BAKE_OK)) {
|
||||
return error;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef DEBUG_TEXTURES
|
||||
|
|
@ -1904,8 +2186,6 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d
|
|||
uint32_t seam_offset = 0;
|
||||
uint32_t triangle_offset = 0;
|
||||
|
||||
Vector<Color> clear_colors;
|
||||
clear_colors.push_back(Color(0, 0, 0, 1));
|
||||
for (int i = 0; i < atlas_slices; i++) {
|
||||
int subslices = (p_bake_sh ? 4 : 1);
|
||||
|
||||
|
|
@ -1918,7 +2198,8 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d
|
|||
seams_push_constant.slice = uint32_t(i * subslices + k);
|
||||
seams_push_constant.debug = debug;
|
||||
|
||||
RD::DrawListID draw_list = rd->draw_list_begin(framebuffers[i * subslices + k], RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_DISCARD, clear_colors);
|
||||
// Store the current subslice in the breadcrumb.
|
||||
RD::DrawListID draw_list = rd->draw_list_begin(framebuffers[i * subslices + k], RD::DRAW_CLEAR_DEPTH, Vector<Color>(), 1.0f, 0, Rect2(), RDD::BreadcrumbMarker::LIGHTMAPPER_PASS | seams_push_constant.slice);
|
||||
|
||||
rd->draw_list_bind_uniform_set(draw_list, raster_base_uniform, 0);
|
||||
rd->draw_list_bind_uniform_set(draw_list, blendseams_raster_uniform, 1);
|
||||
|
|
@ -1978,6 +2259,14 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d
|
|||
}
|
||||
}
|
||||
|
||||
if (p_bake_sh) {
|
||||
SWAP(light_accum_tex, light_accum_tex2);
|
||||
BakeError error = _pack_l1(rd, compute_shader, compute_base_uniform_set, push_constant, light_accum_tex2, light_accum_tex, atlas_size, atlas_slices);
|
||||
if (unlikely(error != BAKE_OK)) {
|
||||
return error;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef DEBUG_TEXTURES
|
||||
|
||||
for (int i = 0; i < atlas_slices * (p_bake_sh ? 4 : 1); i++) {
|
||||
|
|
@ -1986,6 +2275,7 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d
|
|||
img->save_exr("res://5_blendseams" + itos(i) + ".exr", false);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (p_step_function) {
|
||||
p_step_function(0.9, RTR("Retrieving textures"), p_bake_userdata, true);
|
||||
}
|
||||
|
|
@ -1994,7 +2284,16 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d
|
|||
Vector<uint8_t> s = rd->texture_get_data(light_accum_tex, i);
|
||||
Ref<Image> img = Image::create_from_data(atlas_size.width, atlas_size.height, false, Image::FORMAT_RGBAH, s);
|
||||
img->convert(Image::FORMAT_RGBH); //remove alpha
|
||||
bake_textures.push_back(img);
|
||||
lightmap_textures.push_back(img);
|
||||
}
|
||||
|
||||
if (p_bake_shadowmask) {
|
||||
for (int i = 0; i < atlas_slices; i++) {
|
||||
Vector<uint8_t> s = rd->texture_get_data(shadowmask_tex, i);
|
||||
Ref<Image> img = Image::create_from_data(atlas_size.width, atlas_size.height, false, Image::FORMAT_RGBA8, s);
|
||||
img->convert(Image::FORMAT_R8);
|
||||
shadowmask_textures.push_back(img);
|
||||
}
|
||||
}
|
||||
|
||||
if (probe_positions.size() > 0) {
|
||||
|
|
@ -2027,12 +2326,21 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d
|
|||
}
|
||||
|
||||
int LightmapperRD::get_bake_texture_count() const {
|
||||
return bake_textures.size();
|
||||
return lightmap_textures.size();
|
||||
}
|
||||
|
||||
Ref<Image> LightmapperRD::get_bake_texture(int p_index) const {
|
||||
ERR_FAIL_INDEX_V(p_index, bake_textures.size(), Ref<Image>());
|
||||
return bake_textures[p_index];
|
||||
ERR_FAIL_INDEX_V(p_index, lightmap_textures.size(), Ref<Image>());
|
||||
return lightmap_textures[p_index];
|
||||
}
|
||||
|
||||
int LightmapperRD::get_shadowmask_texture_count() const {
|
||||
return shadowmask_textures.size();
|
||||
}
|
||||
|
||||
Ref<Image> LightmapperRD::get_shadowmask_texture(int p_index) const {
|
||||
ERR_FAIL_INDEX_V(p_index, shadowmask_textures.size(), Ref<Image>());
|
||||
return shadowmask_textures[p_index];
|
||||
}
|
||||
|
||||
int LightmapperRD::get_bake_mesh_count() const {
|
||||
|
|
@ -2045,9 +2353,9 @@ Variant LightmapperRD::get_bake_mesh_userdata(int p_index) const {
|
|||
}
|
||||
|
||||
Rect2 LightmapperRD::get_bake_mesh_uv_scale(int p_index) const {
|
||||
ERR_FAIL_COND_V(bake_textures.is_empty(), Rect2());
|
||||
ERR_FAIL_COND_V(lightmap_textures.is_empty(), Rect2());
|
||||
Rect2 uv_ofs;
|
||||
Vector2 atlas_size = Vector2(bake_textures[0]->get_width(), bake_textures[0]->get_height());
|
||||
Vector2 atlas_size = Vector2(lightmap_textures[0]->get_width(), lightmap_textures[0]->get_height());
|
||||
uv_ofs.position = Vector2(mesh_instances[p_index].offset) / atlas_size;
|
||||
uv_ofs.size = Vector2(mesh_instances[p_index].data.albedo_on_uv2->get_width(), mesh_instances[p_index].data.albedo_on_uv2->get_height()) / atlas_size;
|
||||
return uv_ofs;
|
||||
|
|
|
|||
|
|
@ -57,7 +57,9 @@ class LightmapperRD : public Lightmapper {
|
|||
uint32_t bounces = 0;
|
||||
|
||||
float bounce_indirect_energy = 0.0f;
|
||||
uint32_t pad[3] = {};
|
||||
uint32_t shadowmask_light_idx = 0;
|
||||
uint32_t transparency_rays = 0;
|
||||
float supersampling_factor = 0.0f;
|
||||
};
|
||||
|
||||
struct MeshInstance {
|
||||
|
|
@ -184,7 +186,7 @@ class LightmapperRD : public Lightmapper {
|
|||
uint32_t indices[3] = {};
|
||||
uint32_t slice = 0;
|
||||
float min_bounds[3] = {};
|
||||
float pad0 = 0.0;
|
||||
uint32_t cull_mode = 0;
|
||||
float max_bounds[3] = {};
|
||||
float pad1 = 0.0;
|
||||
bool operator<(const Triangle &p_triangle) const {
|
||||
|
|
@ -202,6 +204,7 @@ class LightmapperRD : public Lightmapper {
|
|||
Vector<MeshInstance> mesh_instances;
|
||||
|
||||
Vector<Light> lights;
|
||||
Vector<String> light_names;
|
||||
|
||||
struct TriangleSort {
|
||||
uint32_t cell_index = 0;
|
||||
|
|
@ -253,9 +256,15 @@ class LightmapperRD : public Lightmapper {
|
|||
uint32_t pad = 0;
|
||||
};
|
||||
|
||||
Vector<Ref<Image>> bake_textures;
|
||||
Vector<Ref<Image>> lightmap_textures;
|
||||
Vector<Ref<Image>> shadowmask_textures;
|
||||
Vector<Color> probe_values;
|
||||
|
||||
struct DilateParams {
|
||||
uint32_t radius;
|
||||
uint32_t pad[3];
|
||||
};
|
||||
|
||||
struct DenoiseParams {
|
||||
float spatial_bandwidth;
|
||||
float light_bandwidth;
|
||||
|
|
@ -267,27 +276,30 @@ class LightmapperRD : public Lightmapper {
|
|||
float pad[2];
|
||||
};
|
||||
|
||||
BakeError _blit_meshes_into_atlas(int p_max_texture_size, int p_denoiser_range, Vector<Ref<Image>> &albedo_images, Vector<Ref<Image>> &emission_images, AABB &bounds, Size2i &atlas_size, int &atlas_slices, BakeStepFunc p_step_function, void *p_bake_userdata);
|
||||
BakeError _blit_meshes_into_atlas(int p_max_texture_size, int p_denoiser_range, Vector<Ref<Image>> &albedo_images, Vector<Ref<Image>> &emission_images, AABB &bounds, Size2i &atlas_size, int &atlas_slices, float p_supersampling_factor, BakeStepFunc p_step_function, void *p_bake_userdata);
|
||||
void _create_acceleration_structures(RenderingDevice *rd, Size2i atlas_size, int atlas_slices, AABB &bounds, int grid_size, uint32_t p_cluster_size, Vector<Probe> &probe_positions, GenerateProbes p_generate_probes, Vector<int> &slice_triangle_count, Vector<int> &slice_seam_count, RID &vertex_buffer, RID &triangle_buffer, RID &lights_buffer, RID &r_triangle_indices_buffer, RID &r_cluster_indices_buffer, RID &r_cluster_aabbs_buffer, RID &probe_positions_buffer, RID &grid_texture, RID &seams_buffer, BakeStepFunc p_step_function, void *p_bake_userdata);
|
||||
void _raster_geometry(RenderingDevice *rd, Size2i atlas_size, int atlas_slices, int grid_size, AABB bounds, float p_bias, Vector<int> slice_triangle_count, RID position_tex, RID unocclude_tex, RID normal_tex, RID raster_depth_buffer, RID rasterize_shader, RID raster_base_uniform);
|
||||
|
||||
BakeError _dilate(RenderingDevice *rd, Ref<RDShaderFile> &compute_shader, RID &compute_base_uniform_set, PushConstant &push_constant, RID &source_light_tex, RID &dest_light_tex, const Size2i &atlas_size, int atlas_slices);
|
||||
BakeError _denoise(RenderingDevice *p_rd, Ref<RDShaderFile> &p_compute_shader, const RID &p_compute_base_uniform_set, PushConstant &p_push_constant, RID p_source_light_tex, RID p_source_normal_tex, RID p_dest_light_tex, float p_denoiser_strength, int p_denoiser_range, const Size2i &p_atlas_size, int p_atlas_slices, bool p_bake_sh, BakeStepFunc p_step_function);
|
||||
BakeError _denoise(RenderingDevice *p_rd, Ref<RDShaderFile> &p_compute_shader, const RID &p_compute_base_uniform_set, PushConstant &p_push_constant, RID p_source_light_tex, RID p_source_normal_tex, RID p_dest_light_tex, float p_denoiser_strength, int p_denoiser_range, const Size2i &p_atlas_size, int p_atlas_slices, bool p_bake_sh, BakeStepFunc p_step_function, void *p_bake_userdata);
|
||||
BakeError _pack_l1(RenderingDevice *rd, Ref<RDShaderFile> &compute_shader, RID &compute_base_uniform_set, PushConstant &push_constant, RID &source_light_tex, RID &dest_light_tex, const Size2i &atlas_size, int atlas_slices);
|
||||
|
||||
Error _store_pfm(RenderingDevice *p_rd, RID p_atlas_tex, int p_index, const Size2i &p_atlas_size, const String &p_name);
|
||||
Ref<Image> _read_pfm(const String &p_name);
|
||||
BakeError _denoise_oidn(RenderingDevice *p_rd, RID p_source_light_tex, RID p_source_normal_tex, RID p_dest_light_tex, const Size2i &p_atlas_size, int p_atlas_slices, bool p_bake_sh, const String &p_exe);
|
||||
Error _store_pfm(RenderingDevice *p_rd, RID p_atlas_tex, int p_index, const Size2i &p_atlas_size, const String &p_name, bool p_shadowmask);
|
||||
Ref<Image> _read_pfm(const String &p_name, bool p_shadowmask);
|
||||
BakeError _denoise_oidn(RenderingDevice *p_rd, RID p_source_light_tex, RID p_source_normal_tex, RID p_dest_light_tex, const Size2i &p_atlas_size, int p_atlas_slices, bool p_bake_sh, bool p_shadowmask, const String &p_exe);
|
||||
|
||||
public:
|
||||
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_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_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_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_directional_light(const String &p_name, 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(const String &p_name, 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(const String &p_name, 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 BakeError bake(BakeQuality p_quality, bool p_use_denoiser, float p_denoiser_strength, int p_denoiser_range, 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;
|
||||
virtual BakeError bake(BakeQuality p_quality, bool p_use_denoiser, float p_denoiser_strength, int p_denoiser_range, int p_bounces, float p_bounce_indirect_energy, float p_bias, int p_max_texture_size, bool p_bake_sh, bool p_bake_shadowmask, 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, float p_supersampling_factor = 1.0f) override;
|
||||
|
||||
int get_bake_texture_count() const override;
|
||||
Ref<Image> get_bake_texture(int p_index) const override;
|
||||
int get_shadowmask_texture_count() const override;
|
||||
Ref<Image> get_shadowmask_texture(int p_index) const override;
|
||||
int get_bake_mesh_count() const override;
|
||||
Variant get_bake_mesh_userdata(int p_index) const override;
|
||||
Rect2 get_bake_mesh_uv_scale(int p_index) const override;
|
||||
|
|
|
|||
|
|
@ -1,6 +1,4 @@
|
|||
|
||||
/* SET 0, static data that does not change between any call */
|
||||
|
||||
layout(set = 0, binding = 0) uniform BakeParameters {
|
||||
vec3 world_size;
|
||||
float bias;
|
||||
|
|
@ -18,6 +16,9 @@ layout(set = 0, binding = 0) uniform BakeParameters {
|
|||
uint bounces;
|
||||
|
||||
float bounce_indirect_energy;
|
||||
int shadowmask_light_idx;
|
||||
uint transparency_rays;
|
||||
float supersampling_factor;
|
||||
}
|
||||
bake_params;
|
||||
|
||||
|
|
@ -33,11 +34,15 @@ layout(set = 0, binding = 1, std430) restrict readonly buffer Vertices {
|
|||
}
|
||||
vertices;
|
||||
|
||||
#define CULL_DISABLED 0
|
||||
#define CULL_FRONT 1
|
||||
#define CULL_BACK 2
|
||||
|
||||
struct Triangle {
|
||||
uvec3 indices;
|
||||
uint slice;
|
||||
vec3 min_bounds;
|
||||
uint pad0;
|
||||
uint cull_mode;
|
||||
vec3 max_bounds;
|
||||
uint pad1;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ dilate = "#define MODE_DILATE";
|
|||
unocclude = "#define MODE_UNOCCLUDE";
|
||||
light_probes = "#define MODE_LIGHT_PROBES";
|
||||
denoise = "#define MODE_DENOISE";
|
||||
pack_coeffs = "#define MODE_PACK_L1_COEFFS";
|
||||
|
||||
#[compute]
|
||||
|
||||
|
|
@ -59,11 +60,13 @@ layout(rgba16f, set = 1, binding = 4) uniform restrict image2DArray accum_light;
|
|||
|
||||
#endif
|
||||
|
||||
#ifdef MODE_BOUNCE_LIGHT
|
||||
#if defined(MODE_DIRECT_LIGHT) && defined(USE_SHADOWMASK)
|
||||
layout(rgba8, set = 1, binding = 5) uniform restrict writeonly image2DArray shadowmask;
|
||||
#elif defined(MODE_BOUNCE_LIGHT)
|
||||
layout(set = 1, binding = 5) uniform texture2D environment;
|
||||
#endif
|
||||
|
||||
#if defined(MODE_DILATE) || defined(MODE_DENOISE)
|
||||
#if defined(MODE_DILATE) || defined(MODE_DENOISE) || defined(MODE_PACK_L1_COEFFS)
|
||||
layout(rgba16f, set = 1, binding = 0) uniform restrict writeonly image2DArray dest_light;
|
||||
layout(set = 1, binding = 1) uniform texture2DArray source_light;
|
||||
#endif
|
||||
|
|
@ -151,7 +154,8 @@ uint trace_ray(vec3 p_from, vec3 p_to, bool p_any_hit, out float r_distance, out
|
|||
vec3 dir_cell = normalize(rel_cell);
|
||||
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));
|
||||
vec3 side = (sign(rel_cell) * (vec3(icell) - from_cell) + (sign(rel_cell) * 0.5) + 0.5) * delta;
|
||||
const vec3 init_next_cell = vec3(icell) + max(vec3(0), sign(step));
|
||||
vec3 t_max = mix(vec3(0), (init_next_cell - from_cell) / dir_cell, notEqual(step, vec3(0))); // Distance to next boundary.
|
||||
|
||||
uint iters = 0;
|
||||
while (all(greaterThanEqual(icell, ivec3(0))) && all(lessThan(icell, ivec3(bake_params.grid_size))) && (iters < 1000)) {
|
||||
|
|
@ -222,7 +226,6 @@ uint trace_ray(vec3 p_from, vec3 p_to, bool p_any_hit, out float r_distance, out
|
|||
// Return early if any hit was requested.
|
||||
return RAY_ANY;
|
||||
}
|
||||
|
||||
vec3 position = p_from + dir * distance;
|
||||
vec3 hit_cell = (position - bake_params.to_cell_offset) * bake_params.to_cell_size;
|
||||
if (icell != ivec3(hit_cell)) {
|
||||
|
|
@ -239,6 +242,17 @@ uint trace_ray(vec3 p_from, vec3 p_to, bool p_any_hit, out float r_distance, out
|
|||
}
|
||||
|
||||
if (distance < best_distance) {
|
||||
switch (triangle.cull_mode) {
|
||||
case CULL_DISABLED:
|
||||
backface = false;
|
||||
break;
|
||||
case CULL_FRONT:
|
||||
backface = !backface;
|
||||
break;
|
||||
case CULL_BACK: // Default behavior.
|
||||
break;
|
||||
}
|
||||
|
||||
hit = backface ? RAY_BACK : RAY_FRONT;
|
||||
best_distance = distance;
|
||||
r_distance = distance;
|
||||
|
|
@ -268,17 +282,16 @@ uint trace_ray(vec3 p_from, vec3 p_to, bool p_any_hit, out float r_distance, out
|
|||
}
|
||||
|
||||
// There should be only one axis updated at a time for DDA to work properly.
|
||||
bvec3 mask = bvec3(true, false, false);
|
||||
float m = side.x;
|
||||
if (side.y < m) {
|
||||
m = side.y;
|
||||
mask = bvec3(false, true, false);
|
||||
if (t_max.x < t_max.y && t_max.x < t_max.z) {
|
||||
icell.x += step.x;
|
||||
t_max.x += delta.x;
|
||||
} else if (t_max.y < t_max.z) {
|
||||
icell.y += step.y;
|
||||
t_max.y += delta.y;
|
||||
} else {
|
||||
icell.z += step.z;
|
||||
t_max.z += delta.z;
|
||||
}
|
||||
if (side.z < m) {
|
||||
mask = bvec3(false, false, true);
|
||||
}
|
||||
side += vec3(mask) * delta;
|
||||
icell += ivec3(vec3(mask)) * step;
|
||||
iters++;
|
||||
}
|
||||
|
||||
|
|
@ -291,6 +304,27 @@ uint trace_ray_closest_hit_triangle(vec3 p_from, vec3 p_to, out uint r_triangle,
|
|||
return trace_ray(p_from, p_to, false, distance, normal, r_triangle, r_barycentric);
|
||||
}
|
||||
|
||||
uint trace_ray_closest_hit_triangle_albedo_alpha(vec3 p_from, vec3 p_to, out vec4 albedo_alpha, out vec3 hit_position) {
|
||||
float distance;
|
||||
vec3 normal;
|
||||
uint tidx;
|
||||
vec3 barycentric;
|
||||
|
||||
uint ret = trace_ray(p_from, p_to, false, distance, normal, tidx, barycentric);
|
||||
if (ret != RAY_MISS) {
|
||||
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));
|
||||
|
||||
albedo_alpha = textureLod(sampler2DArray(albedo_tex, linear_sampler), uvw, 0);
|
||||
hit_position = barycentric.x * vert0.position + barycentric.y * vert1.position + barycentric.z * vert2.position;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
uint trace_ray_closest_hit_distance(vec3 p_from, vec3 p_to, out float r_distance, out vec3 r_normal) {
|
||||
uint triangle;
|
||||
vec3 barycentric;
|
||||
|
|
@ -359,8 +393,40 @@ float get_omni_attenuation(float distance, float inv_range, float decay) {
|
|||
return nd * pow(max(distance, 0.0001), -decay);
|
||||
}
|
||||
|
||||
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) {
|
||||
const int AA_SAMPLES = 16;
|
||||
|
||||
const vec2 halton_map[AA_SAMPLES] = vec2[](
|
||||
vec2(0.5, 0.33333333),
|
||||
vec2(0.25, 0.66666667),
|
||||
vec2(0.75, 0.11111111),
|
||||
vec2(0.125, 0.44444444),
|
||||
vec2(0.625, 0.77777778),
|
||||
vec2(0.375, 0.22222222),
|
||||
vec2(0.875, 0.55555556),
|
||||
vec2(0.0625, 0.88888889),
|
||||
vec2(0.5625, 0.03703704),
|
||||
vec2(0.3125, 0.37037037),
|
||||
vec2(0.8125, 0.7037037),
|
||||
vec2(0.1875, 0.14814815),
|
||||
vec2(0.6875, 0.48148148),
|
||||
vec2(0.4375, 0.81481481),
|
||||
vec2(0.9375, 0.25925926),
|
||||
vec2(0.03125, 0.59259259));
|
||||
|
||||
vec2 get_vogel_disk(float p_i, float p_rotation, float p_sample_count_sqrt) {
|
||||
const float golden_angle = 2.4;
|
||||
|
||||
float r = sqrt(p_i + 0.5) / p_sample_count_sqrt;
|
||||
float theta = p_i * golden_angle + p_rotation;
|
||||
|
||||
return vec2(cos(theta), sin(theta)) * r;
|
||||
}
|
||||
|
||||
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, float p_texel_size, out float r_shadow) {
|
||||
const float EPSILON = 0.00001;
|
||||
|
||||
r_light = vec3(0.0f);
|
||||
r_shadow = 0.0f;
|
||||
|
||||
vec3 light_pos;
|
||||
float dist;
|
||||
|
|
@ -407,53 +473,182 @@ void trace_direct_light(vec3 p_position, vec3 p_normal, uint p_light_index, bool
|
|||
}
|
||||
|
||||
float penumbra = 0.0;
|
||||
if ((light_data.size > 0.0) && p_soft_shadowing) {
|
||||
vec3 penumbra_color = vec3(0.0);
|
||||
if (p_soft_shadowing) {
|
||||
const bool use_soft_shadows = (light_data.size > 0.0);
|
||||
const uint ray_count = AA_SAMPLES;
|
||||
const uint total_ray_count = use_soft_shadows ? params.ray_count : ray_count;
|
||||
const uint shadowing_rays_check_penumbra_denom = 2;
|
||||
const uint shadowing_ray_count = max(1, params.ray_count / ray_count);
|
||||
const float shadowing_ray_count_sqrt = sqrt(float(total_ray_count));
|
||||
|
||||
// Setup tangent pass to calculate AA samples over the current texel.
|
||||
vec3 aux = p_normal.y < 0.777 ? vec3(0.0, 1.0, 0.0) : vec3(1.0, 0.0, 0.0);
|
||||
vec3 tangent = normalize(cross(p_normal, aux));
|
||||
vec3 bitan = normalize(cross(p_normal, tangent));
|
||||
|
||||
// Setup light tangent pass to calculate samples over disk aligned towards the light
|
||||
vec3 light_to_point = -r_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_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, light_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 = p_soft_shadowing ? params.ray_count : 1;
|
||||
float aa_power = 0.0;
|
||||
for (uint i = 0; i < ray_count; i++) {
|
||||
// Create a random sample within the texel.
|
||||
vec2 disk_sample = (halton_map[i] - vec2(0.5)) * p_texel_size * light_data.shadow_blur;
|
||||
// Align the sample to world space.
|
||||
vec3 disk_aligned = (disk_sample.x * tangent + disk_sample.y * bitan);
|
||||
vec3 origin = p_position - disk_aligned;
|
||||
vec3 light_dir = normalize(light_pos - origin);
|
||||
|
||||
uint hits = 0;
|
||||
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 (p_soft_shadowing) {
|
||||
if (j == shadowing_ray_count / shadowing_rays_check_penumbra_denom) {
|
||||
if (hits == j) {
|
||||
// Assume totally lit
|
||||
hits = shadowing_ray_count;
|
||||
break;
|
||||
} else if (hits == 0) {
|
||||
// Assume totally dark
|
||||
hits = 0;
|
||||
float power = 0.0;
|
||||
vec3 light_color = vec3(0.0);
|
||||
uint power_accm = 0;
|
||||
vec3 prev_pos = origin;
|
||||
if (use_soft_shadows) {
|
||||
uint soft_shadow_hits = 0;
|
||||
for (uint j = 0; j < shadowing_ray_count; j++) {
|
||||
origin = prev_pos;
|
||||
// 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 (soft_shadow_hits == j) {
|
||||
// Assume totally lit
|
||||
soft_shadow_hits = shadowing_ray_count;
|
||||
break;
|
||||
} else if (soft_shadow_hits == 0) {
|
||||
// Assume totally dark
|
||||
soft_shadow_hits = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
float a = randomize(r_noise) * 2.0 * PI;
|
||||
float vogel_index = float(total_ray_count - 1 - (i * shadowing_ray_count + j)); // Start from (total_ray_count - 1) so we check the outer points first.
|
||||
vec2 light_disk_sample = get_vogel_disk(vogel_index, a, shadowing_ray_count_sqrt) * soft_shadowing_disk_size * light_data.shadow_blur;
|
||||
vec3 light_disk_to_point = normalize(light_to_point + light_disk_sample.x * light_to_point_tan + light_disk_sample.y * light_to_point_bitan);
|
||||
float sample_penumbra = 0.0;
|
||||
vec3 sample_penumbra_color = light_data.color.rgb;
|
||||
bool sample_did_hit = false;
|
||||
|
||||
for (uint iter = 0; iter < bake_params.transparency_rays; iter++) {
|
||||
vec4 hit_albedo = vec4(1.0);
|
||||
vec3 hit_position;
|
||||
// Offset the ray origin for AA, offset the light position for soft shadows.
|
||||
uint ret = trace_ray_closest_hit_triangle_albedo_alpha(origin - light_disk_to_point * (bake_params.bias + length(disk_sample)), p_position - light_disk_to_point * dist, hit_albedo, hit_position);
|
||||
if (ret == RAY_MISS) {
|
||||
if (!sample_did_hit) {
|
||||
sample_penumbra = 1.0;
|
||||
}
|
||||
soft_shadow_hits += 1;
|
||||
break;
|
||||
} else if (ret == RAY_FRONT || ret == RAY_BACK) {
|
||||
bool contribute = ret == RAY_FRONT || !sample_did_hit;
|
||||
if (!sample_did_hit) {
|
||||
sample_penumbra = 1.0;
|
||||
sample_did_hit = true;
|
||||
}
|
||||
|
||||
soft_shadow_hits += 1;
|
||||
|
||||
if (contribute) {
|
||||
sample_penumbra_color = mix(sample_penumbra_color, sample_penumbra_color * hit_albedo.rgb, hit_albedo.a);
|
||||
sample_penumbra *= 1.0 - hit_albedo.a;
|
||||
}
|
||||
origin = hit_position + r_light_dir * bake_params.bias;
|
||||
|
||||
if (sample_penumbra - EPSILON <= 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
power += sample_penumbra;
|
||||
light_color += sample_penumbra_color;
|
||||
power_accm++;
|
||||
}
|
||||
|
||||
} else { // No soft shadows (size == 0).
|
||||
float sample_penumbra = 0.0;
|
||||
vec3 sample_penumbra_color = light_data.color.rgb;
|
||||
bool sample_did_hit = false;
|
||||
for (uint iter = 0; iter < bake_params.transparency_rays; iter++) {
|
||||
vec4 hit_albedo = vec4(1.0);
|
||||
vec3 hit_position;
|
||||
// Offset the ray origin for AA, offset the light position for soft shadows.
|
||||
uint ret = trace_ray_closest_hit_triangle_albedo_alpha(origin + light_dir * (bake_params.bias + length(disk_sample)), light_pos, hit_albedo, hit_position);
|
||||
if (ret == RAY_MISS) {
|
||||
if (!sample_did_hit) {
|
||||
sample_penumbra = 1.0;
|
||||
}
|
||||
break;
|
||||
} else if (ret == RAY_FRONT || ret == RAY_BACK) {
|
||||
bool contribute = ret == RAY_FRONT || !sample_did_hit;
|
||||
if (!sample_did_hit) {
|
||||
sample_penumbra = 1.0;
|
||||
sample_did_hit = true;
|
||||
}
|
||||
|
||||
if (contribute) {
|
||||
sample_penumbra_color = mix(sample_penumbra_color, sample_penumbra_color * hit_albedo.rgb, hit_albedo.a);
|
||||
sample_penumbra *= 1.0 - hit_albedo.a;
|
||||
}
|
||||
origin = hit_position + r_light_dir * bake_params.bias;
|
||||
|
||||
if (sample_penumbra - EPSILON <= 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
power = sample_penumbra;
|
||||
light_color = sample_penumbra_color;
|
||||
power_accm = 1;
|
||||
}
|
||||
aa_power += power / float(power_accm);
|
||||
penumbra_color += light_color / float(power_accm);
|
||||
}
|
||||
penumbra = aa_power / ray_count;
|
||||
penumbra_color /= ray_count;
|
||||
} else { // No soft shadows and anti-aliasing (disabled via parameter).
|
||||
bool did_hit = false;
|
||||
penumbra = 0.0;
|
||||
penumbra_color = light_data.color.rgb;
|
||||
for (uint iter = 0; iter < bake_params.transparency_rays; iter++) {
|
||||
vec4 hit_albedo = vec4(1.0);
|
||||
vec3 hit_position;
|
||||
uint ret = trace_ray_closest_hit_triangle_albedo_alpha(p_position + r_light_dir * bake_params.bias, light_pos, hit_albedo, hit_position);
|
||||
if (ret == RAY_MISS) {
|
||||
if (!did_hit) {
|
||||
penumbra = 1.0;
|
||||
}
|
||||
break;
|
||||
} else if (ret == RAY_FRONT || ret == RAY_BACK) {
|
||||
bool contribute = (ret == RAY_FRONT || !did_hit);
|
||||
if (!did_hit) {
|
||||
penumbra = 1.0;
|
||||
did_hit = true;
|
||||
}
|
||||
|
||||
float r = randomize(r_noise);
|
||||
float a = randomize(r_noise) * 2.0 * PI;
|
||||
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 (contribute) {
|
||||
penumbra_color = mix(penumbra_color, penumbra_color * hit_albedo.rgb, hit_albedo.a);
|
||||
penumbra *= 1.0 - hit_albedo.a;
|
||||
}
|
||||
|
||||
if (trace_ray_any_hit(p_position - light_disk_to_point * bake_params.bias, p_position - light_disk_to_point * dist) == RAY_MISS) {
|
||||
hits++;
|
||||
p_position = hit_position + r_light_dir * bake_params.bias;
|
||||
|
||||
if (penumbra - EPSILON <= 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
penumbra = float(hits) / float(shadowing_ray_count);
|
||||
} else {
|
||||
if (trace_ray_any_hit(p_position + r_light_dir * bake_params.bias, light_pos) == RAY_MISS) {
|
||||
penumbra = 1.0;
|
||||
}
|
||||
penumbra = clamp(penumbra, 0.0, 1.0);
|
||||
}
|
||||
|
||||
r_light = light_data.color * light_data.energy * attenuation * penumbra;
|
||||
r_shadow = penumbra;
|
||||
r_light = light_data.energy * attenuation * penumbra * penumbra_color;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -470,11 +665,12 @@ vec3 trace_environment_color(vec3 ray_dir) {
|
|||
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) {
|
||||
vec3 trace_indirect_light(vec3 p_position, vec3 p_ray_dir, inout uint r_noise, float p_texel_size) {
|
||||
// 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);
|
||||
uint transparency_rays_left = bake_params.transparency_rays;
|
||||
vec3 throughput = vec3(1.0);
|
||||
vec3 light = vec3(0.0);
|
||||
for (uint depth = 0; depth < max_depth; depth++) {
|
||||
|
|
@ -488,6 +684,8 @@ vec3 trace_indirect_light(vec3 p_position, vec3 p_ray_dir, inout uint r_noise) {
|
|||
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 prev_normal = ray_dir;
|
||||
|
||||
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);
|
||||
|
|
@ -502,20 +700,37 @@ vec3 trace_indirect_light(vec3 p_position, vec3 p_ray_dir, inout uint r_noise) {
|
|||
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);
|
||||
float shadow;
|
||||
trace_direct_light(position, normal, i, false, light, light_dir, r_noise, p_texel_size, shadow);
|
||||
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;
|
||||
vec4 albedo_alpha = textureLod(sampler2DArray(albedo_tex, linear_sampler), uvw, 0).rgba;
|
||||
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;
|
||||
light += throughput * emissive * albedo_alpha.a;
|
||||
throughput = mix(throughput, throughput * albedo_alpha.rgb, albedo_alpha.a);
|
||||
light += throughput * direct_light * bake_params.bounce_indirect_energy * albedo_alpha.a;
|
||||
|
||||
if (albedo_alpha.a < 1.0) {
|
||||
transparency_rays_left -= 1;
|
||||
depth -= 1;
|
||||
if (transparency_rays_left <= 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
// Either bounce off the transparent surface or keep going forward.
|
||||
float pa = albedo_alpha.a * albedo_alpha.a;
|
||||
if (randomize(r_noise) > pa) {
|
||||
normal = prev_normal;
|
||||
}
|
||||
|
||||
position += normal * bake_params.bias;
|
||||
}
|
||||
|
||||
// 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>
|
||||
|
|
@ -533,9 +748,55 @@ vec3 trace_indirect_light(vec3 p_position, vec3 p_ray_dir, inout uint r_noise) {
|
|||
// Look for the environment color and stop bouncing.
|
||||
light += throughput * trace_environment_color(ray_dir);
|
||||
break;
|
||||
} else {
|
||||
// Ignore any other trace results.
|
||||
break;
|
||||
} else if (trace_result == RAY_BACK) {
|
||||
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;
|
||||
|
||||
vec4 albedo_alpha = textureLod(sampler2DArray(albedo_tex, linear_sampler), uvw, 0).rgba;
|
||||
|
||||
if (albedo_alpha.a > 1.0) {
|
||||
break;
|
||||
}
|
||||
|
||||
transparency_rays_left -= 1;
|
||||
depth -= 1;
|
||||
if (transparency_rays_left <= 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
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;
|
||||
float shadow;
|
||||
trace_direct_light(position, normal, i, false, light, light_dir, r_noise, p_texel_size, shadow);
|
||||
direct_light += light * lights.data[i].indirect_energy;
|
||||
}
|
||||
|
||||
direct_light *= bake_params.exposure_normalization;
|
||||
#endif
|
||||
|
||||
vec3 emissive = textureLod(sampler2DArray(emission_tex, linear_sampler), uvw, 0).rgb;
|
||||
emissive *= bake_params.exposure_normalization;
|
||||
|
||||
light += throughput * emissive * albedo_alpha.a;
|
||||
throughput = mix(mix(throughput, throughput * albedo_alpha.rgb, albedo_alpha.a), vec3(0.0), albedo_alpha.a);
|
||||
light += throughput * direct_light * bake_params.bounce_indirect_energy * albedo_alpha.a;
|
||||
|
||||
position += ray_dir * bake_params.bias;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -560,15 +821,26 @@ void main() {
|
|||
#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;
|
||||
vec4 neighbor_position = texelFetch(sampler2DArray(source_position, linear_sampler), ivec3(atlas_pos + ivec2(1, 0), params.atlas_slice), 0).xyzw;
|
||||
|
||||
if (neighbor_position.w < 0.001) {
|
||||
// Empty texel, try again.
|
||||
neighbor_position.xyz = texelFetch(sampler2DArray(source_position, linear_sampler), ivec3(atlas_pos + ivec2(-1, 0), params.atlas_slice), 0).xyz;
|
||||
}
|
||||
float texel_size_world_space = distance(position, neighbor_position.xyz) * bake_params.supersampling_factor;
|
||||
|
||||
vec3 light_for_texture = vec3(0.0);
|
||||
vec3 light_for_bounces = vec3(0.0);
|
||||
|
||||
#ifdef USE_SHADOWMASK
|
||||
float shadowmask_value = 0.0f;
|
||||
#endif
|
||||
|
||||
#ifdef USE_SH_LIGHTMAPS
|
||||
vec4 sh_accum[4] = vec4[](
|
||||
vec4(0.0, 0.0, 0.0, 1.0),
|
||||
|
|
@ -582,26 +854,38 @@ void main() {
|
|||
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);
|
||||
float shadow;
|
||||
trace_direct_light(position, normal, i, true, light, light_dir, noise, texel_size_world_space, shadow);
|
||||
|
||||
if (lights.data[i].static_bake) {
|
||||
light_for_texture += light;
|
||||
|
||||
#ifdef USE_SH_LIGHTMAPS
|
||||
// These coefficients include the factored out SH evaluation, diffuse convolution, and final application, as well as the BRDF 1/PI and the spherical monte carlo factor.
|
||||
// LO: 1/(2*sqrtPI) * 1/(2*sqrtPI) * PI * PI * 1/PI = 0.25
|
||||
// L1: sqrt(3/(4*pi)) * sqrt(3/(4*pi)) * (PI*2/3) * (2 * PI) * 1/PI = 1.0
|
||||
// Note: This only works because we aren't scaling, rotating, or combing harmonics, we are just directing applying them in the shader.
|
||||
|
||||
float c[4] = float[](
|
||||
0.282095, //l0
|
||||
0.488603 * light_dir.y, //l1n1
|
||||
0.488603 * light_dir.z, //l1n0
|
||||
0.488603 * light_dir.x //l1p1
|
||||
0.25, //l0
|
||||
light_dir.y, //l1n1
|
||||
light_dir.z, //l1n0
|
||||
light_dir.x //l1p1
|
||||
);
|
||||
|
||||
for (uint j = 0; j < 4; j++) {
|
||||
sh_accum[j].rgb += light * c[j] * 8.0;
|
||||
sh_accum[j].rgb += light * c[j] * bake_params.exposure_normalization;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
light_for_bounces += light * lights.data[i].indirect_energy;
|
||||
|
||||
#ifdef USE_SHADOWMASK
|
||||
if (lights.data[i].type == LIGHT_TYPE_DIRECTIONAL && i == bake_params.shadowmask_light_idx) {
|
||||
shadowmask_value = max(shadowmask_value, shadow);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
light_for_bounces *= bake_params.exposure_normalization;
|
||||
|
|
@ -618,6 +902,10 @@ void main() {
|
|||
imageStore(accum_light, ivec3(atlas_pos, params.atlas_slice), vec4(light_for_texture, 1.0));
|
||||
#endif
|
||||
|
||||
#ifdef USE_SHADOWMASK
|
||||
imageStore(shadowmask, ivec3(atlas_pos, params.atlas_slice), vec4(shadowmask_value, shadowmask_value, shadowmask_value, 1.0));
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef MODE_BOUNCE_LIGHT
|
||||
|
|
@ -640,21 +928,29 @@ void main() {
|
|||
}
|
||||
|
||||
vec3 position = texelFetch(sampler2DArray(source_position, linear_sampler), ivec3(atlas_pos, params.atlas_slice), 0).xyz;
|
||||
int neighbor_offset = atlas_pos.x < bake_params.atlas_size.x - 1 ? 1 : -1;
|
||||
vec3 neighbor_position = texelFetch(sampler2DArray(source_position, linear_sampler), ivec3(atlas_pos + ivec2(neighbor_offset, 0), params.atlas_slice), 0).xyz;
|
||||
float texel_size_world_space = distance(position, neighbor_position);
|
||||
uint noise = random_seed(ivec3(params.ray_from, atlas_pos));
|
||||
for (uint i = params.ray_from; i < params.ray_to; i++) {
|
||||
vec3 ray_dir = generate_ray_dir_from_normal(normal, noise);
|
||||
vec3 light = trace_indirect_light(position, ray_dir, noise);
|
||||
vec3 light = trace_indirect_light(position, ray_dir, noise, texel_size_world_space);
|
||||
|
||||
#ifdef USE_SH_LIGHTMAPS
|
||||
// These coefficients include the factored out SH evaluation, diffuse convolution, and final application, as well as the BRDF 1/PI and the spherical monte carlo factor.
|
||||
// LO: 1/(2*sqrtPI) * 1/(2*sqrtPI) * PI * PI * 1/PI = 0.25
|
||||
// L1: sqrt(3/(4*pi)) * sqrt(3/(4*pi)) * (PI*2/3) * (2 * PI) * 1/PI = 1.0
|
||||
// Note: This only works because we aren't scaling, rotating, or combing harmonics, we are just directing applying them in the shader.
|
||||
|
||||
float c[4] = float[](
|
||||
0.282095, //l0
|
||||
0.488603 * ray_dir.y, //l1n1
|
||||
0.488603 * ray_dir.z, //l1n0
|
||||
0.488603 * ray_dir.x //l1p1
|
||||
0.25, //l0
|
||||
ray_dir.y, //l1n1
|
||||
ray_dir.z, //l1n0
|
||||
ray_dir.x //l1p1
|
||||
);
|
||||
|
||||
for (uint j = 0; j < 4; j++) {
|
||||
sh_accum[j].rgb += light * c[j] * 8.0;
|
||||
sh_accum[j].rgb += light * c[j];
|
||||
}
|
||||
#else
|
||||
light_accum += light;
|
||||
|
|
@ -737,7 +1033,7 @@ void main() {
|
|||
uint noise = random_seed(ivec3(params.ray_from, probe_index, 49502741 /* some prime */));
|
||||
for (uint i = params.ray_from; i < params.ray_to; i++) {
|
||||
vec3 ray_dir = generate_sphere_uniform_direction(noise);
|
||||
vec3 light = trace_indirect_light(position, ray_dir, noise);
|
||||
vec3 light = trace_indirect_light(position, ray_dir, noise, 0.0);
|
||||
|
||||
float c[9] = float[](
|
||||
0.282095, //l0
|
||||
|
|
@ -777,41 +1073,33 @@ void main() {
|
|||
|
||||
#ifdef MODE_DILATE
|
||||
|
||||
vec4 c = texelFetch(sampler2DArray(source_light, linear_sampler), ivec3(atlas_pos, params.atlas_slice), 0);
|
||||
//sides first, as they are closer
|
||||
c = c.a > 0.5 ? c : texelFetch(sampler2DArray(source_light, linear_sampler), ivec3(atlas_pos + ivec2(-1, 0), params.atlas_slice), 0);
|
||||
c = c.a > 0.5 ? c : texelFetch(sampler2DArray(source_light, linear_sampler), ivec3(atlas_pos + ivec2(0, 1), params.atlas_slice), 0);
|
||||
c = c.a > 0.5 ? c : texelFetch(sampler2DArray(source_light, linear_sampler), ivec3(atlas_pos + ivec2(1, 0), params.atlas_slice), 0);
|
||||
c = c.a > 0.5 ? c : texelFetch(sampler2DArray(source_light, linear_sampler), ivec3(atlas_pos + ivec2(0, -1), params.atlas_slice), 0);
|
||||
//endpoints second
|
||||
c = c.a > 0.5 ? c : texelFetch(sampler2DArray(source_light, linear_sampler), ivec3(atlas_pos + ivec2(-1, -1), params.atlas_slice), 0);
|
||||
c = c.a > 0.5 ? c : texelFetch(sampler2DArray(source_light, linear_sampler), ivec3(atlas_pos + ivec2(-1, 1), params.atlas_slice), 0);
|
||||
c = c.a > 0.5 ? c : texelFetch(sampler2DArray(source_light, linear_sampler), ivec3(atlas_pos + ivec2(1, -1), params.atlas_slice), 0);
|
||||
c = c.a > 0.5 ? c : texelFetch(sampler2DArray(source_light, linear_sampler), ivec3(atlas_pos + ivec2(1, 1), params.atlas_slice), 0);
|
||||
const int max_radius = int(4.0 * bake_params.supersampling_factor);
|
||||
const ivec2 directions[8] = ivec2[8](ivec2(-1, 0), ivec2(0, 1), ivec2(1, 0), ivec2(0, -1), ivec2(-1, -1), ivec2(-1, 1), ivec2(1, -1), ivec2(1, 1));
|
||||
|
||||
//far sides third
|
||||
c = c.a > 0.5 ? c : texelFetch(sampler2DArray(source_light, linear_sampler), ivec3(atlas_pos + ivec2(-2, 0), params.atlas_slice), 0);
|
||||
c = c.a > 0.5 ? c : texelFetch(sampler2DArray(source_light, linear_sampler), ivec3(atlas_pos + ivec2(0, 2), params.atlas_slice), 0);
|
||||
c = c.a > 0.5 ? c : texelFetch(sampler2DArray(source_light, linear_sampler), ivec3(atlas_pos + ivec2(2, 0), params.atlas_slice), 0);
|
||||
c = c.a > 0.5 ? c : texelFetch(sampler2DArray(source_light, linear_sampler), ivec3(atlas_pos + ivec2(0, -2), params.atlas_slice), 0);
|
||||
vec4 texel_color = texelFetch(sampler2DArray(source_light, linear_sampler), ivec3(atlas_pos, params.atlas_slice), 0);
|
||||
|
||||
//far-mid endpoints
|
||||
c = c.a > 0.5 ? c : texelFetch(sampler2DArray(source_light, linear_sampler), ivec3(atlas_pos + ivec2(-2, -1), params.atlas_slice), 0);
|
||||
c = c.a > 0.5 ? c : texelFetch(sampler2DArray(source_light, linear_sampler), ivec3(atlas_pos + ivec2(-2, 1), params.atlas_slice), 0);
|
||||
c = c.a > 0.5 ? c : texelFetch(sampler2DArray(source_light, linear_sampler), ivec3(atlas_pos + ivec2(2, -1), params.atlas_slice), 0);
|
||||
c = c.a > 0.5 ? c : texelFetch(sampler2DArray(source_light, linear_sampler), ivec3(atlas_pos + ivec2(2, 1), params.atlas_slice), 0);
|
||||
for (int radius = 1; radius <= max_radius; radius++) {
|
||||
for (uint i = 0; i < 8; i++) {
|
||||
const ivec2 sample_pos = atlas_pos + directions[i] * radius;
|
||||
// Texture bounds check for robustness.
|
||||
if (any(lessThan(sample_pos, ivec2(0))) ||
|
||||
any(greaterThanEqual(sample_pos, textureSize(source_light, 0).xy))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
c = c.a > 0.5 ? c : texelFetch(sampler2DArray(source_light, linear_sampler), ivec3(atlas_pos + ivec2(-1, -2), params.atlas_slice), 0);
|
||||
c = c.a > 0.5 ? c : texelFetch(sampler2DArray(source_light, linear_sampler), ivec3(atlas_pos + ivec2(-1, 2), params.atlas_slice), 0);
|
||||
c = c.a > 0.5 ? c : texelFetch(sampler2DArray(source_light, linear_sampler), ivec3(atlas_pos + ivec2(1, -2), params.atlas_slice), 0);
|
||||
c = c.a > 0.5 ? c : texelFetch(sampler2DArray(source_light, linear_sampler), ivec3(atlas_pos + ivec2(1, 2), params.atlas_slice), 0);
|
||||
//far endpoints
|
||||
c = c.a > 0.5 ? c : texelFetch(sampler2DArray(source_light, linear_sampler), ivec3(atlas_pos + ivec2(-2, -2), params.atlas_slice), 0);
|
||||
c = c.a > 0.5 ? c : texelFetch(sampler2DArray(source_light, linear_sampler), ivec3(atlas_pos + ivec2(-2, 2), params.atlas_slice), 0);
|
||||
c = c.a > 0.5 ? c : texelFetch(sampler2DArray(source_light, linear_sampler), ivec3(atlas_pos + ivec2(2, -2), params.atlas_slice), 0);
|
||||
c = c.a > 0.5 ? c : texelFetch(sampler2DArray(source_light, linear_sampler), ivec3(atlas_pos + ivec2(2, 2), params.atlas_slice), 0);
|
||||
vec4 neighbor_color = texelFetch(sampler2DArray(source_light, linear_sampler), ivec3(sample_pos, params.atlas_slice), 0);
|
||||
if (neighbor_color.a > 0.5) {
|
||||
texel_color = neighbor_color;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
imageStore(dest_light, ivec3(atlas_pos, params.atlas_slice), c);
|
||||
if (texel_color.a > 0.5) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
imageStore(dest_light, ivec3(atlas_pos, params.atlas_slice), texel_color);
|
||||
|
||||
#endif
|
||||
|
||||
|
|
@ -963,4 +1251,28 @@ void main() {
|
|||
imageStore(dest_light, ivec3(atlas_pos, lightmap_slice), vec4(denoised_rgb, input_light.a));
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef MODE_PACK_L1_COEFFS
|
||||
vec4 base_coeff = texelFetch(sampler2DArray(source_light, linear_sampler), ivec3(atlas_pos, params.atlas_slice * 4), 0);
|
||||
|
||||
for (int i = 1; i < 4; i++) {
|
||||
vec4 c = texelFetch(sampler2DArray(source_light, linear_sampler), ivec3(atlas_pos, params.atlas_slice * 4 + i), 0);
|
||||
|
||||
if (abs(base_coeff.r) > 0.0) {
|
||||
c.r /= (base_coeff.r * 8);
|
||||
}
|
||||
|
||||
if (abs(base_coeff.g) > 0.0) {
|
||||
c.g /= (base_coeff.g * 8);
|
||||
}
|
||||
|
||||
if (abs(base_coeff.b) > 0.0) {
|
||||
c.b /= (base_coeff.b * 8);
|
||||
}
|
||||
|
||||
c.rgb += vec3(0.5);
|
||||
c.rgb = clamp(c.rgb, vec3(0.0), vec3(1.0));
|
||||
imageStore(dest_light, ivec3(atlas_pos, params.atlas_slice * 4 + i), c);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
|
|
|||
|
|
@ -50,8 +50,9 @@ void initialize_lightmapper_rd_module(ModuleInitializationLevel p_level) {
|
|||
GLOBAL_DEF(PropertyInfo(Variant::INT, "rendering/lightmapping/bake_quality/medium_quality_ray_count", PROPERTY_HINT_RANGE, "1,4096,1,or_greater"), 128);
|
||||
GLOBAL_DEF(PropertyInfo(Variant::INT, "rendering/lightmapping/bake_quality/high_quality_ray_count", PROPERTY_HINT_RANGE, "1,4096,1,or_greater"), 512);
|
||||
GLOBAL_DEF(PropertyInfo(Variant::INT, "rendering/lightmapping/bake_quality/ultra_quality_ray_count", PROPERTY_HINT_RANGE, "1,4096,1,or_greater"), 2048);
|
||||
GLOBAL_DEF(PropertyInfo(Variant::INT, "rendering/lightmapping/bake_performance/max_rays_per_pass", PROPERTY_HINT_RANGE, "1,256,1,or_greater"), 32);
|
||||
GLOBAL_DEF(PropertyInfo(Variant::INT, "rendering/lightmapping/bake_performance/max_rays_per_pass", PROPERTY_HINT_RANGE, "1,256,1,or_greater"), 4);
|
||||
GLOBAL_DEF(PropertyInfo(Variant::INT, "rendering/lightmapping/bake_performance/region_size", PROPERTY_HINT_RANGE, "1,4096,1,or_greater"), 512);
|
||||
GLOBAL_DEF(PropertyInfo(Variant::INT, "rendering/lightmapping/bake_performance/max_transparency_rays", PROPERTY_HINT_RANGE, "1,256,1,or_greater"), 8);
|
||||
|
||||
GLOBAL_DEF(PropertyInfo(Variant::INT, "rendering/lightmapping/bake_quality/low_quality_probe_ray_count", PROPERTY_HINT_RANGE, "1,4096,1,or_greater"), 64);
|
||||
GLOBAL_DEF(PropertyInfo(Variant::INT, "rendering/lightmapping/bake_quality/medium_quality_probe_ray_count", PROPERTY_HINT_RANGE, "1,4096,1,or_greater"), 256);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue