Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement shadowmask for DirectionalLight in BakedLightmap #51330

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions doc/classes/BakedLightmap.xml
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,10 @@
If [code]true[/code], stores the lightmap textures in a high dynamic range format (EXR). If [code]false[/code], stores the lightmap texture in a low dynamic range PNG image. This can be set to [code]false[/code] to reduce disk usage, but light values over 1.0 will be clamped and you may see banding caused by the reduced precision.
[b]Note:[/b] Setting [member use_hdr] to [code]true[/code] will decrease lightmap banding even when using the GLES2 backend or if [member ProjectSettings.rendering/quality/depth/hdr] is [code]false[/code].
</member>
<member name="use_shadowmask" type="bool" setter="set_use_shadowmask" getter="is_using_shadowmask" default="true">
If [code]true[/code], bakes the [DirectionalLight]s' direct light shadows into a [i]shadowmask[/i] which is stored in the lightmap textures' alpha channel. This shadowmask is used to keep static shadows visible past the DirectionalLights' [member DirectionalLight.directional_shadow_max_distance] by blending the last shadow split with the shadowmask. This in turn allows you to use a lower [member DirectionalLight.directional_shadow_max_distance] for dynamic objects. Lower real-time shadow distances improve shadow detail and performance while making shadow acne less visible.
[b]Note:[/b] Shadowmasking only supports one baked [DirectionalLight] at a time. If you have more than one [DirectionalLight] whose [member Light.light_bake_mode] isn't set to [constant Light.BAKE_DISABLED], the editor will print a warning when baking lightmaps.
</member>
</members>
<constants>
<constant name="BAKE_QUALITY_LOW" value="0" enum="BakeQuality">
Expand Down
1 change: 1 addition & 0 deletions doc/classes/Light.xml
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@
</member>
<member name="light_size" type="float" setter="set_param" getter="get_param" default="0.0">
The size of the light in Godot units. Only considered in baked lightmaps and only if [member light_bake_mode] is set to [constant BAKE_ALL]. Increasing this value will make the shadows appear blurrier. This can be used to simulate area lights to an extent.
[b]Note:[/b] [DirectionalLight]s with their bake mode set to [constant BAKE_INDIRECT] still have an adjustable [member light_size] property, but it will only be used for the baked shadowmask (see [member BakedLightmap.use_shadowmask]). This can be used to better integrate the shadowmask with real-time shadows by making it softer and less pixelated. Values between [code]0.01[/code] and [code]0.1[/code] work well for the purposes of shadowmasking.
</member>
<member name="light_specular" type="float" setter="set_param" getter="get_param" default="0.5">
The intensity of the specular blob in objects affected by the light. At [code]0[/code], the light becomes a pure diffuse light. When not baking emission, this can be used to avoid unrealistic reflections when placing lights above an emissive surface.
Expand Down
30 changes: 23 additions & 7 deletions drivers/gles2/shaders/scene.glsl
Original file line number Diff line number Diff line change
Expand Up @@ -1816,12 +1816,13 @@ FRAGMENT_SHADER_CODE
}

#ifdef USE_LIGHTMAP
//ambient light will come entirely from lightmap is lightmap is used
#if defined(USE_LIGHTMAP_FILTER_BICUBIC)
ambient_light = texture2D_bicubic(lightmap, uv2_interp).rgb * lightmap_energy;
vec4 lightmap_sample = texture2D_bicubic(lightmap, uv2_interp);
#else
ambient_light = texture2D(lightmap, uv2_interp).rgb * lightmap_energy;
vec4 lightmap_sample = texture2D(lightmap, uv2_interp);
#endif
//ambient light will come entirely from lightmap is lightmap is used
ambient_light = lightmap_sample.rgb * lightmap_energy;
#endif

#ifdef USE_LIGHTMAP_CAPTURE
Expand Down Expand Up @@ -1938,6 +1939,16 @@ FRAGMENT_SHADER_CODE
#endif
float depth_z = -vertex.z;

#ifdef USE_LIGHTMAP
// Take the DirectionalLight's shadow color into account for the shadowmask.
// This applies even if the DirectionalLight shadow is disabled, which can be
// done to improve performance by disabling shadows for dynamic objects only.
vec3 shadowmask = mix(shadow_color.rgb, vec3(1.0), lightmap_sample.a);
#else
// No lightmaps, fallback to no shadows in the distance.
vec3 shadowmask = vec3(1.0);
#endif

#if !defined(SHADOWS_DISABLED)

#ifdef USE_SHADOW
Expand Down Expand Up @@ -2001,7 +2012,7 @@ FRAGMENT_SHADER_CODE
shadow_att = mix(shadow_att, shadow_att2, pssm_blend);
}
#endif
light_att *= mix(shadow_color.rgb, vec3(1.0), shadow_att);
light_att *= mix(shadow_color.rgb, shadowmask, shadow_att);
}

#endif //LIGHT_USE_PSSM4
Expand Down Expand Up @@ -2041,14 +2052,14 @@ FRAGMENT_SHADER_CODE
shadow_att = mix(shadow_att, shadow_att2, pssm_blend);
}
#endif
light_att *= mix(shadow_color.rgb, vec3(1.0), shadow_att);
light_att *= mix(shadow_color.rgb, shadowmask, shadow_att);
}

#endif //LIGHT_USE_PSSM2

#if !defined(LIGHT_USE_PSSM4) && !defined(LIGHT_USE_PSSM2)

light_att *= mix(shadow_color.rgb, vec3(1.0), sample_shadow(light_directional_shadow, shadow_coord));
light_att *= mix(shadow_color.rgb, shadowmask, sample_shadow(light_directional_shadow, shadow_coord));
#endif //orthogonal

#else //fragment version of pssm
Expand Down Expand Up @@ -2144,11 +2155,16 @@ FRAGMENT_SHADER_CODE
}
#endif

light_att *= mix(shadow_color.rgb, vec3(1.0), shadow);
light_att *= mix(shadow_color.rgb, shadowmask, shadow);
}
}
#endif //use vertex lighting

#else
// Use the shadowmask only when past the DirectionalLight's maximum shadow distance.
// FIXME: This doesn't work when the DirectionalLight's shadow is enabled.
// It only works when the shadow is disabled.
light_att *= shadowmask;
#endif //use shadow

#endif // SHADOWS_DISABLED
Expand Down
25 changes: 20 additions & 5 deletions drivers/gles3/shaders/scene.glsl
Original file line number Diff line number Diff line change
Expand Up @@ -2022,13 +2022,19 @@ FRAGMENT_SHADER_CODE

#endif //ubershader-runtime

// Defined outside the lightmap block to make the ubershader approach work.
vec4 lightmap_sample = vec4(1.0);

#ifdef USE_LIGHTMAP //ubershader-runtime
// Also store the alpha channel as it's used for shadowmasking further below.
#ifdef USE_LIGHTMAP_LAYERED //ubershader-runtime
ambient_light = LIGHTMAP_TEXTURE_LAYERED_SAMPLE(lightmap_array, vec3(uv2, float(lightmap_layer))).rgb * lightmap_energy;
lightmap_sample = LIGHTMAP_TEXTURE_LAYERED_SAMPLE(lightmap_array, vec3(uv2, float(lightmap_layer)));
#else //ubershader-runtime
ambient_light = LIGHTMAP_TEXTURE_SAMPLE(lightmap, uv2).rgb * lightmap_energy;
#endif //ubershader-runtime
#endif //ubershader-runtime
lightmap_sample = LIGHTMAP_TEXTURE_SAMPLE(lightmap, uv2);
#endif //USE_LIGHTMAP_LAYERED //ubershader-runtime

ambient_light = lightmap_sample.rgb * lightmap_energy;
#endif //USE_LIGHTMAP //ubershader-runtime

#ifdef USE_LIGHTMAP_CAPTURE //ubershader-runtime
{
Expand Down Expand Up @@ -2110,8 +2116,16 @@ FRAGMENT_SHADER_CODE

#ifdef USE_LIGHT_DIRECTIONAL //ubershader-runtime

// No lightmaps, fallback to no shadows in the distance.
vec3 light_attenuation = vec3(1.0);

#ifdef USE_LIGHTMAP //ubershader-runtime
// Take the DirectionalLight's shadow color into account for the shadowmask.
// This applies even if the DirectionalLight shadow is disabled, which can be
// done to improve performance by disabling shadows for dynamic objects only.
light_attenuation = mix(shadow_color_contact.rgb, vec3(1.0), lightmap_sample.a);
#endif //ubershader-runtime

float depth_z = -vertex.z;
#ifdef LIGHT_DIRECTIONAL_SHADOW //ubershader-runtime
#if !defined(SHADOWS_DISABLED)
Expand Down Expand Up @@ -2236,10 +2250,11 @@ FRAGMENT_SHADER_CODE
shadow = min(shadow, contact_shadow);
}
#endif //ubershader-runtime
light_attenuation = mix(mix(shadow_color_contact.rgb, vec3(1.0), shadow), vec3(1.0), pssm_fade);
light_attenuation = mix(mix(shadow_color_contact.rgb, light_attenuation, shadow), light_attenuation, pssm_fade);
}

#endif // !defined(SHADOWS_DISABLED)

#endif //LIGHT_DIRECTIONAL_SHADOW //ubershader-runtime

#ifdef USE_VERTEX_LIGHTING //ubershader-runtime
Expand Down
5 changes: 3 additions & 2 deletions modules/denoise/denoise_wrapper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,9 @@ void *oidn_denoiser_init() {
bool oidn_denoise(void *deviceptr, float *p_floats, int p_width, int p_height) {
OIDNDeviceImpl *device = (OIDNDeviceImpl *)deviceptr;
OIDNFilter filter = oidnNewFilter(device, "RTLightmap");
oidnSetSharedFilterImage(filter, "color", (void *)p_floats, OIDN_FORMAT_FLOAT3, p_width, p_height, 0, 0, 0);
oidnSetSharedFilterImage(filter, "output", (void *)p_floats, OIDN_FORMAT_FLOAT3, p_width, p_height, 0, 0, 0);
// Pass 16-byte pixel stride to preserve the unmodified 32-bit alpha channel.
oidnSetSharedFilterImage(filter, "color", (void *)p_floats, OIDN_FORMAT_FLOAT3, p_width, p_height, 0, 16, 0);
oidnSetSharedFilterImage(filter, "output", (void *)p_floats, OIDN_FORMAT_FLOAT3, p_width, p_height, 0, 16, 0);
oidnSetFilter1b(filter, "hdr", true);
oidnCommitFilter(filter);
oidnExecuteFilter(filter);
Expand Down
4 changes: 4 additions & 0 deletions modules/denoise/denoise_wrapper.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,11 @@
#define DENOISE_WRAPPER_H

void *oidn_denoiser_init();

// Expects RGBAF image data. The alpha channel is configured to be ignored by OpenImageDenoise,
// and is preserved in the output data.
bool oidn_denoise(void *device, float *p_floats, int p_width, int p_height);

void oidn_denoiser_finish(void *device);

#endif // DENOISE_WRAPPER_H
2 changes: 1 addition & 1 deletion modules/denoise/lightmap_denoiser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ void LightmapDenoiserOIDN::make_default_denoiser() {
Ref<Image> LightmapDenoiserOIDN::denoise_image(const Ref<Image> &p_image) {
Ref<Image> img = p_image->duplicate();

img->convert(Image::FORMAT_RGBF);
img->convert(Image::FORMAT_RGBAF);

PoolByteArray data = img->get_data();
{
Expand Down
66 changes: 41 additions & 25 deletions modules/lightmapper_cpu/lightmapper_cpu.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -831,11 +831,16 @@ void LightmapperCPU::_compute_direct_light(uint32_t p_idx, void *r_lightmap) {
}
}

Vector3 final_energy = attenuation * penumbra * light_energy * MAX(0, normal.dot(-light_to_point));
const Vector3 final_energy = attenuation * penumbra * light_energy * MAX(0, normal.dot(-light_to_point));
lightmap[p_idx].direct_light += final_energy * light.indirect_multiplier;
if (light.bake_direct) {
lightmap[p_idx].output_light += final_energy;
}

if (light.type == LIGHT_TYPE_DIRECTIONAL) {
// Store the directional shadowmask, which does not depend on the light color, energy or angle.
lightmap[p_idx].shadowmask = CLAMP(lightmap[p_idx].shadowmask - attenuation * penumbra, 0.0, 1.0);
}
}
}

Expand Down Expand Up @@ -952,7 +957,7 @@ void LightmapperCPU::_post_process(uint32_t p_idx, void *r_output) {

LocalVector<int> &indices = scene_lightmap_indices[p_idx];
LocalVector<LightmapTexel> &lightmap = scene_lightmaps[p_idx];
Vector3 *output = ((LocalVector<Vector3> *)r_output)[p_idx].ptr();
Color *output = ((LocalVector<Color> *)r_output)[p_idx].ptr();
Vector2i size = mesh.size;

// Blit texels to buffer
Expand All @@ -961,7 +966,11 @@ void LightmapperCPU::_post_process(uint32_t p_idx, void *r_output) {
for (int j = 0; j < size.x; j++) {
int idx = indices[i * size.x + j];
if (idx >= 0) {
output[i * size.x + j] = lightmap[idx].output_light;
output[i * size.x + j] = Color(
lightmap[idx].output_light.x,
lightmap[idx].output_light.y,
lightmap[idx].output_light.z,
1.0 - lightmap[idx].shadowmask);
continue; // filled, skip
}

Expand Down Expand Up @@ -993,7 +1002,11 @@ void LightmapperCPU::_post_process(uint32_t p_idx, void *r_output) {
}

if (closest_idx != -1) {
output[i * size.x + j] = lightmap[closest_idx].output_light;
output[i * size.x + j] = Color(
lightmap[closest_idx].output_light.x,
lightmap[closest_idx].output_light.y,
lightmap[closest_idx].output_light.z,
1.0 - lightmap[closest_idx].shadowmask);
}
}
}
Expand Down Expand Up @@ -1033,7 +1046,6 @@ void LightmapperCPU::_post_process(uint32_t p_idx, void *r_output) {
_dilate_lightmap(output, indices, size, margin);
_fix_seams(seams, output, size);
_dilate_lightmap(output, indices, size, margin);

indices.clear();
}

Expand Down Expand Up @@ -1120,25 +1132,25 @@ void LightmapperCPU::_compute_seams(const MeshInstance &p_mesh, LocalVector<UVSe
}
}

void LightmapperCPU::_fix_seams(const LocalVector<UVSeam> &p_seams, Vector3 *r_lightmap, Vector2i p_size) {
LocalVector<Vector3> extra_buffer;
void LightmapperCPU::_fix_seams(const LocalVector<UVSeam> &p_seams, Color *r_lightmap, Vector2i p_size) {
LocalVector<Color> extra_buffer;
extra_buffer.resize(p_size.x * p_size.y);

memcpy(extra_buffer.ptr(), r_lightmap, p_size.x * p_size.y * sizeof(Vector3));
memcpy(extra_buffer.ptr(), r_lightmap, p_size.x * p_size.y * sizeof(Color));

Vector3 *read_ptr = extra_buffer.ptr();
Vector3 *write_ptr = r_lightmap;
Color *read_ptr = extra_buffer.ptr();
Color *write_ptr = r_lightmap;

for (int i = 0; i < 5; i++) {
for (unsigned int j = 0; j < p_seams.size(); j++) {
_fix_seam(p_seams[j].edge0[0], p_seams[j].edge0[1], p_seams[j].edge1[0], p_seams[j].edge1[1], read_ptr, write_ptr, p_size);
_fix_seam(p_seams[j].edge1[0], p_seams[j].edge1[1], p_seams[j].edge0[0], p_seams[j].edge0[1], read_ptr, write_ptr, p_size);
}
memcpy(read_ptr, write_ptr, p_size.x * p_size.y * sizeof(Vector3));
memcpy(read_ptr, write_ptr, p_size.x * p_size.y * sizeof(Color));
}
}

void LightmapperCPU::_fix_seam(const Vector2 &p_pos0, const Vector2 &p_pos1, const Vector2 &p_uv0, const Vector2 &p_uv1, const Vector3 *p_read_buffer, Vector3 *r_write_buffer, const Vector2i &p_size) {
void LightmapperCPU::_fix_seam(const Vector2 &p_pos0, const Vector2 &p_pos1, const Vector2 &p_uv0, const Vector2 &p_uv1, const Color *p_read_buffer, Color *r_write_buffer, const Vector2i &p_size) {
Vector2 line[2];
line[0] = p_pos0 * p_size;
line[1] = p_pos1 * p_size;
Expand Down Expand Up @@ -1187,8 +1199,8 @@ void LightmapperCPU::_fix_seam(const Vector2 &p_pos0, const Vector2 &p_pos1, con
Vector2 current_uv = p_uv0 * (1.0 - t) + p_uv1 * t;
Vector2i sampled_point = (current_uv * p_size).floor();

Vector3 current_color = r_write_buffer[pixel.y * p_size.x + pixel.x];
Vector3 sampled_color = p_read_buffer[sampled_point.y * p_size.x + sampled_point.x];
Color current_color = r_write_buffer[pixel.y * p_size.x + pixel.x];
Color sampled_color = p_read_buffer[sampled_point.y * p_size.x + sampled_point.x];

r_write_buffer[pixel.y * p_size.x + pixel.x] = current_color * 0.6f + sampled_color * 0.4f;

Expand All @@ -1206,7 +1218,7 @@ void LightmapperCPU::_fix_seam(const Vector2 &p_pos0, const Vector2 &p_pos1, con
}
}

void LightmapperCPU::_dilate_lightmap(Vector3 *r_lightmap, const LocalVector<int> p_indices, Vector2i p_size, int margin) {
void LightmapperCPU::_dilate_lightmap(Color *r_lightmap, const LocalVector<int> p_indices, Vector2i p_size, int margin) {
for (int i = 0; i < p_size.y; i++) {
for (int j = 0; j < p_size.x; j++) {
int idx = p_indices[i * p_size.x + j];
Expand Down Expand Up @@ -1248,17 +1260,17 @@ void LightmapperCPU::_dilate_lightmap(Vector3 *r_lightmap, const LocalVector<int
}
}

void LightmapperCPU::_blit_lightmap(const Vector<Vector3> &p_src, const Vector2i &p_size, Ref<Image> &p_dst, int p_x, int p_y, bool p_with_padding) {
void LightmapperCPU::_blit_lightmap(const Vector<Color> &p_src, const Vector2i &p_size, Ref<Image> &p_dst, int p_x, int p_y, bool p_with_padding) {
int padding = p_with_padding ? 1 : 0;
ERR_FAIL_COND(p_x < padding || p_y < padding);
ERR_FAIL_COND(p_x + p_size.x > p_dst->get_width() - padding);
ERR_FAIL_COND(p_y + p_size.y > p_dst->get_height() - padding);

p_dst->lock();
for (int y = 0; y < p_size.y; y++) {
const Vector3 *__restrict src = p_src.ptr() + y * p_size.x;
const Color *__restrict src = p_src.ptr() + y * p_size.x;
for (int x = 0; x < p_size.x; x++) {
p_dst->set_pixel(p_x + x, p_y + y, Color(src->x, src->y, src->z));
p_dst->set_pixel(p_x + x, p_y + y, Color(src->r, src->g, src->b, src->a));
src++;
}
}
Expand All @@ -1268,16 +1280,16 @@ void LightmapperCPU::_blit_lightmap(const Vector<Vector3> &p_src, const Vector2i
int yy = CLAMP(y, 0, p_size.y - 1);
int idx_left = yy * p_size.x;
int idx_right = idx_left + p_size.x - 1;
p_dst->set_pixel(p_x - 1, p_y + y, Color(p_src[idx_left].x, p_src[idx_left].y, p_src[idx_left].z));
p_dst->set_pixel(p_x + p_size.x, p_y + y, Color(p_src[idx_right].x, p_src[idx_right].y, p_src[idx_right].z));
p_dst->set_pixel(p_x - 1, p_y + y, Color(p_src[idx_left].r, p_src[idx_left].g, p_src[idx_left].b, p_src[idx_left].a));
p_dst->set_pixel(p_x + p_size.x, p_y + y, Color(p_src[idx_right].r, p_src[idx_right].g, p_src[idx_right].b, p_src[idx_right].a));
}

for (int x = -1; x < p_size.x + 1; x++) {
int xx = CLAMP(x, 0, p_size.x - 1);
int idx_top = xx;
int idx_bot = idx_top + (p_size.y - 1) * p_size.x;
p_dst->set_pixel(p_x + x, p_y - 1, Color(p_src[idx_top].x, p_src[idx_top].y, p_src[idx_top].z));
p_dst->set_pixel(p_x + x, p_y + p_size.y, Color(p_src[idx_bot].x, p_src[idx_bot].y, p_src[idx_bot].z));
p_dst->set_pixel(p_x + x, p_y - 1, Color(p_src[idx_top].r, p_src[idx_top].g, p_src[idx_top].b, p_src[idx_top].a));
p_dst->set_pixel(p_x + x, p_y + p_size.y, Color(p_src[idx_bot].r, p_src[idx_bot].g, p_src[idx_bot].b, p_src[idx_bot].a));
}
}
p_dst->unlock();
Expand Down Expand Up @@ -1456,7 +1468,7 @@ LightmapperCPU::BakeError LightmapperCPU::bake(BakeQuality p_quality, bool p_use

raycaster.unref(); // Not needed anymore, free some memory.

LocalVector<LocalVector<Vector3>> lightmaps_data;
LocalVector<LocalVector<Color>> lightmaps_data;
lightmaps_data.resize(mesh_instances.size());

for (unsigned int i = 0; i < mesh_instances.size(); i++) {
Expand All @@ -1483,7 +1495,9 @@ LightmapperCPU::BakeError LightmapperCPU::bake(BakeQuality p_quality, bool p_use
for (int i = 0; i < atlas_slices; i++) {
Ref<Image> image;
image.instance();
image->create(atlas_size.x, atlas_size.y, false, Image::FORMAT_RGBH);
// FIXME: Baking alpha channel only works if image format is RGBA8, not RGBAH.
// However, using RGBA8 breaks HDR light baking, so only LDR light baking can be used for now.
image->create(atlas_size.x, atlas_size.y, false, Image::FORMAT_RGBA8);
bake_textures[i] = image;
}
} else {
Expand Down Expand Up @@ -1516,7 +1530,9 @@ LightmapperCPU::BakeError LightmapperCPU::bake(BakeQuality p_quality, bool p_use

Ref<Image> image;
image.instance();
image->create(mesh_instances[i].size.x, mesh_instances[i].size.y, false, Image::FORMAT_RGBH);
// FIXME: Baking alpha channel only works if image format is RGBA8, not RGBAH.
// However, using RGBA8 breaks HDR light baking, so only LDR light baking can be used for now.
image->create(mesh_instances[i].size.x, mesh_instances[i].size.y, false, Image::FORMAT_RGBA8);
image->set_name(mesh_name);
bake_textures[i] = image;
}
Expand Down
9 changes: 5 additions & 4 deletions modules/lightmapper_cpu/lightmapper_cpu.h
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ class LightmapperCPU : public Lightmapper {

Vector3 direct_light;
Vector3 output_light;
float shadowmask = 1.0;

float area_coverage;
};
Expand Down Expand Up @@ -160,11 +161,11 @@ class LightmapperCPU : public Lightmapper {

void _post_process(uint32_t p_idx, void *r_output);
void _compute_seams(const MeshInstance &p_mesh, LocalVector<UVSeam> &r_seams);
void _fix_seams(const LocalVector<UVSeam> &p_seams, Vector3 *r_lightmap, Vector2i p_size);
void _fix_seam(const Vector2 &p_pos0, const Vector2 &p_pos1, const Vector2 &p_uv0, const Vector2 &p_uv1, const Vector3 *p_read_buffer, Vector3 *r_write_buffer, const Vector2i &p_size);
void _dilate_lightmap(Vector3 *r_lightmap, const LocalVector<int> p_indices, Vector2i p_size, int margin);
void _fix_seams(const LocalVector<UVSeam> &p_seams, Color *r_lightmap, Vector2i p_size);
void _fix_seam(const Vector2 &p_pos0, const Vector2 &p_pos1, const Vector2 &p_uv0, const Vector2 &p_uv1, const Color *p_read_buffer, Color *r_write_buffer, const Vector2i &p_size);
void _dilate_lightmap(Color *r_lightmap, const LocalVector<int> p_indices, Vector2i p_size, int margin);

void _blit_lightmap(const Vector<Vector3> &p_src, const Vector2i &p_size, Ref<Image> &p_dst, int p_x, int p_y, bool p_with_padding);
void _blit_lightmap(const Vector<Color> &p_src, const Vector2i &p_size, Ref<Image> &p_dst, int p_x, int p_y, bool p_with_padding);

public:
virtual void add_albedo_texture(Ref<Texture> p_texture);
Expand Down
Loading