Skip to content

Commit 0f3bfa5

Browse files
committed
Transparency support for LightmapGI
1 parent 29b3d9e commit 0f3bfa5

24 files changed

+297
-19
lines changed

doc/classes/ProjectSettings.xml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2513,6 +2513,9 @@
25132513
<member name="rendering/lightmapping/bake_performance/max_rays_per_probe_pass" type="int" setter="" getter="" default="64">
25142514
The maximum number of rays that can be thrown per pass when baking dynamic object lighting in [LightmapProbe]s with [LightmapGI]. Depending on the scene, adjusting this value may result in higher GPU utilization when baking lightmaps, leading to faster bake times.
25152515
</member>
2516+
<member name="rendering/lightmapping/bake_performance/max_transparency_rays" type="int" setter="" getter="" default="64">
2517+
The maximum number of retry rays that can be thrown per pass when hitting a transparent surface when baking lightmaps with [LightmapGI]. Depending on the scene, adjusting this value may result in higher GPU utilization when baking lightmaps, leading to faster bake times.
2518+
</member>
25162519
<member name="rendering/lightmapping/bake_performance/region_size" type="int" setter="" getter="" default="512">
25172520
The region size to use when baking lightmaps with [LightmapGI].
25182521
</member>

modules/lightmapper_rd/lightmapper_rd.cpp

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -341,6 +341,7 @@ Lightmapper::BakeError LightmapperRD::_blit_meshes_into_atlas(int p_max_texture_
341341
}
342342

343343
void LightmapperRD::_create_acceleration_structures(RenderingDevice *rd, Size2i atlas_size, int atlas_slices, AABB &bounds, int grid_size, uint32_t p_cluster_size, Vector<Probe> &p_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) {
344+
RendererRD::MaterialStorage *material_storage = RendererRD::MaterialStorage::get_singleton();
344345
HashMap<Vertex, uint32_t, VertexHash> vertex_map;
345346

346347
//fill triangles array and vertex array
@@ -458,7 +459,15 @@ void LightmapperRD::_create_acceleration_structures(RenderingDevice *rd, Size2i
458459
t.max_bounds[0] = taabb.position.x + MAX(taabb.size.x, 0.0001);
459460
t.max_bounds[1] = taabb.position.y + MAX(taabb.size.y, 0.0001);
460461
t.max_bounds[2] = taabb.position.z + MAX(taabb.size.z, 0.0001);
461-
t.pad0 = t.pad1 = 0; //make valgrind not complain
462+
463+
t.cull_mode = RendererRD::MaterialStorage::ShaderData::CULL_BACK;
464+
465+
RID material = mi.data.material[i];
466+
if (material.is_valid()) {
467+
RendererRD::MaterialStorage::ShaderData *shader_data = material_storage->material_get_shader_data(mi.data.material[i]);
468+
t.cull_mode = shader_data->get_cull_mode();
469+
}
470+
t.pad1 = 0; //make valgrind not complain
462471
triangles.push_back(t);
463472
slice_triangle_count.write[t.slice]++;
464473
}
@@ -1207,6 +1216,8 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d
12071216
bake_parameters.exposure_normalization = p_exposure_normalization;
12081217
bake_parameters.bounces = p_bounces;
12091218
bake_parameters.bounce_indirect_energy = p_bounce_indirect_energy;
1219+
// Same number of rays for transparency regardless of quality (it's more of a retry rather than shooting new ones).
1220+
bake_parameters.transparency_rays = GLOBAL_GET("rendering/lightmapping/bake_performance/max_transparency_rays");
12101221

12111222
bake_parameters_buffer = rd->uniform_buffer_create(sizeof(BakeParameters));
12121223
rd->buffer_update(bake_parameters_buffer, 0, sizeof(BakeParameters), &bake_parameters);

modules/lightmapper_rd/lightmapper_rd.h

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@
3636
#include "scene/resources/mesh.h"
3737
#include "servers/rendering/rendering_device.h"
3838

39+
#include "servers/rendering/renderer_rd/storage_rd/material_storage.h"
40+
3941
class RDShaderFile;
4042
class LightmapperRD : public Lightmapper {
4143
GDCLASS(LightmapperRD, Lightmapper)
@@ -57,7 +59,8 @@ class LightmapperRD : public Lightmapper {
5759
uint32_t bounces = 0;
5860

5961
float bounce_indirect_energy = 0.0f;
60-
uint32_t pad[3] = {};
62+
uint32_t transparency_rays = 0;
63+
uint32_t pad[2] = {};
6164
};
6265

6366
struct MeshInstance {
@@ -184,7 +187,7 @@ class LightmapperRD : public Lightmapper {
184187
uint32_t indices[3] = {};
185188
uint32_t slice = 0;
186189
float min_bounds[3] = {};
187-
float pad0 = 0.0;
190+
uint32_t cull_mode = 0;
188191
float max_bounds[3] = {};
189192
float pad1 = 0.0;
190193
bool operator<(const Triangle &p_triangle) const {

modules/lightmapper_rd/lm_common_inc.glsl

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ layout(set = 0, binding = 0) uniform BakeParameters {
1818
uint bounces;
1919

2020
float bounce_indirect_energy;
21+
uint transparency_rays;
2122
}
2223
bake_params;
2324

@@ -33,11 +34,15 @@ layout(set = 0, binding = 1, std430) restrict readonly buffer Vertices {
3334
}
3435
vertices;
3536

37+
#define CULL_DISABLED 0
38+
#define CULL_FRONT 1
39+
#define CULL_BACK 2
40+
3641
struct Triangle {
3742
uvec3 indices;
3843
uint slice;
3944
vec3 min_bounds;
40-
uint pad0;
45+
uint cull_mode;
4146
vec3 max_bounds;
4247
uint pad1;
4348
};

modules/lightmapper_rd/lm_compute.glsl

Lines changed: 167 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -238,6 +238,17 @@ uint trace_ray(vec3 p_from, vec3 p_to, bool p_any_hit, out float r_distance, out
238238
}
239239

240240
if (distance < best_distance) {
241+
switch (triangle.cull_mode) {
242+
case CULL_DISABLED:
243+
backface = false;
244+
break;
245+
case CULL_FRONT:
246+
backface = !backface;
247+
break;
248+
case CULL_BACK: // default behavior
249+
break;
250+
}
251+
241252
hit = backface ? RAY_BACK : RAY_FRONT;
242253
best_distance = distance;
243254
r_distance = distance;
@@ -290,6 +301,27 @@ uint trace_ray_closest_hit_triangle(vec3 p_from, vec3 p_to, out uint r_triangle,
290301
return trace_ray(p_from, p_to, false, distance, normal, r_triangle, r_barycentric);
291302
}
292303

304+
uint trace_ray_closest_hit_triangle_albedo_alpha(vec3 p_from, vec3 p_to, out vec4 albedo_alpha, out vec3 hit_position) {
305+
float distance;
306+
vec3 normal;
307+
uint tidx;
308+
vec3 barycentric;
309+
310+
uint ret = trace_ray(p_from, p_to, false, distance, normal, tidx, barycentric);
311+
if (ret != RAY_MISS) {
312+
Vertex vert0 = vertices.data[triangles.data[tidx].indices.x];
313+
Vertex vert1 = vertices.data[triangles.data[tidx].indices.y];
314+
Vertex vert2 = vertices.data[triangles.data[tidx].indices.z];
315+
316+
vec3 uvw = vec3(barycentric.x * vert0.uv + barycentric.y * vert1.uv + barycentric.z * vert2.uv, float(triangles.data[tidx].slice));
317+
318+
albedo_alpha = textureLod(sampler2DArray(albedo_tex, linear_sampler), uvw, 0);
319+
hit_position = barycentric.x * vert0.position + barycentric.y * vert1.position + barycentric.z * vert2.position;
320+
}
321+
322+
return ret;
323+
}
324+
293325
uint trace_ray_closest_hit_distance(vec3 p_from, vec3 p_to, out float r_distance, out vec3 r_normal) {
294326
uint triangle;
295327
vec3 barycentric;
@@ -359,6 +391,8 @@ float get_omni_attenuation(float distance, float inv_range, float decay) {
359391
}
360392

361393
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) {
394+
const float EPSILON = 0.00001;
395+
362396
r_light = vec3(0.0f);
363397

364398
vec3 light_pos;
@@ -417,7 +451,13 @@ void trace_direct_light(vec3 p_position, vec3 p_normal, uint p_light_index, bool
417451

418452
uint hits = 0;
419453
vec3 light_disk_to_point = light_to_point;
454+
455+
float power = 0.0;
456+
int power_accm = 0;
457+
vec3 prev_pos = p_position;
420458
for (uint j = 0; j < shadowing_ray_count; j++) {
459+
p_position = prev_pos;
460+
421461
// Optimization:
422462
// Once already traced an important proportion of rays, if all are hits or misses,
423463
// assume we're not in the penumbra so we can infer the rest would have the same result
@@ -440,16 +480,68 @@ void trace_direct_light(vec3 p_position, vec3 p_normal, uint p_light_index, bool
440480
vec2 disk_sample = (r * vec2(cos(a), sin(a))) * soft_shadowing_disk_size * light_data.shadow_blur;
441481
light_disk_to_point = normalize(light_to_point + disk_sample.x * light_to_point_tan + disk_sample.y * light_to_point_bitan);
442482

443-
if (trace_ray_any_hit(p_position - light_disk_to_point * bake_params.bias, p_position - light_disk_to_point * dist) == RAY_MISS) {
444-
hits++;
483+
float sample_penumbra = 0.0;
484+
bool sample_did_hit = false;
485+
for (uint iter = 0; iter < bake_params.transparency_rays; iter++) {
486+
vec4 hit_albedo = vec4(1.0);
487+
vec3 hit_position;
488+
uint ret = trace_ray_closest_hit_triangle_albedo_alpha(p_position - light_disk_to_point * bake_params.bias, p_position - light_disk_to_point * dist, hit_albedo, hit_position);
489+
if (ret == RAY_MISS) {
490+
if (!sample_did_hit)
491+
sample_penumbra = 1.0;
492+
hits += 1;
493+
break;
494+
} else if (ret == RAY_FRONT || ret == RAY_BACK) {
495+
bool contribute = ret == RAY_FRONT || !sample_did_hit;
496+
if (!sample_did_hit) {
497+
sample_penumbra = 1.0;
498+
sample_did_hit = true;
499+
}
500+
501+
hits += 1;
502+
503+
if (contribute)
504+
sample_penumbra = max(sample_penumbra - hit_albedo.a - EPSILON, 0.0);
505+
p_position = hit_position + r_light_dir * bake_params.bias;
506+
507+
if (sample_penumbra - EPSILON <= 0)
508+
break;
509+
}
445510
}
511+
512+
power += sample_penumbra;
513+
power_accm += 1;
446514
}
447515

448-
penumbra = float(hits) / float(shadowing_ray_count);
516+
penumbra = power / float(power_accm);
449517
} else {
450-
if (trace_ray_any_hit(p_position + r_light_dir * bake_params.bias, light_pos) == RAY_MISS) {
451-
penumbra = 1.0;
518+
bool did_hit = false;
519+
penumbra = 0.0;
520+
for (uint iter = 0; iter < bake_params.transparency_rays; iter++) {
521+
vec4 hit_albedo = vec4(1.0);
522+
vec3 hit_position;
523+
uint ret = trace_ray_closest_hit_triangle_albedo_alpha(p_position + r_light_dir * bake_params.bias, light_pos, hit_albedo, hit_position);
524+
if (ret == RAY_MISS) {
525+
if (!did_hit)
526+
penumbra = 1.0;
527+
break;
528+
} else if (ret == RAY_FRONT || ret == RAY_BACK) {
529+
bool contribute = (ret == RAY_FRONT || !did_hit);
530+
if (!did_hit) {
531+
penumbra = 1.0;
532+
did_hit = true;
533+
}
534+
535+
if (contribute)
536+
penumbra = max(penumbra - hit_albedo.a - EPSILON, 0.0);
537+
p_position = hit_position + r_light_dir * bake_params.bias;
538+
539+
if (penumbra - EPSILON <= 0)
540+
break;
541+
}
452542
}
543+
544+
penumbra = clamp(penumbra, 0.0, 1.0);
453545
}
454546

455547
r_light = light_data.color * light_data.energy * attenuation * penumbra;
@@ -474,6 +566,7 @@ vec3 trace_indirect_light(vec3 p_position, vec3 p_ray_dir, inout uint r_noise) {
474566
vec3 position = p_position;
475567
vec3 ray_dir = p_ray_dir;
476568
uint max_depth = max(bake_params.bounces, 1);
569+
uint transparency_rays_left = bake_params.transparency_rays;
477570
vec3 throughput = vec3(1.0);
478571
vec3 light = vec3(0.0);
479572
for (uint depth = 0; depth < max_depth; depth++) {
@@ -487,6 +580,8 @@ vec3 trace_indirect_light(vec3 p_position, vec3 p_ray_dir, inout uint r_noise) {
487580
vec3 uvw = vec3(barycentric.x * vert0.uv + barycentric.y * vert1.uv + barycentric.z * vert2.uv, float(triangles.data[tidx].slice));
488581
position = barycentric.x * vert0.position + barycentric.y * vert1.position + barycentric.z * vert2.position;
489582

583+
vec3 prev_normal = ray_dir;
584+
490585
vec3 norm0 = vec3(vert0.normal_xy, vert0.normal_z);
491586
vec3 norm1 = vec3(vert1.normal_xy, vert1.normal_z);
492587
vec3 norm2 = vec3(vert2.normal_xy, vert2.normal_z);
@@ -508,13 +603,29 @@ vec3 trace_indirect_light(vec3 p_position, vec3 p_ray_dir, inout uint r_noise) {
508603
direct_light *= bake_params.exposure_normalization;
509604
#endif
510605

511-
vec3 albedo = textureLod(sampler2DArray(albedo_tex, linear_sampler), uvw, 0).rgb;
606+
vec4 albedo_alpha = textureLod(sampler2DArray(albedo_tex, linear_sampler), uvw, 0).rgba;
512607
vec3 emissive = textureLod(sampler2DArray(emission_tex, linear_sampler), uvw, 0).rgb;
513608
emissive *= bake_params.exposure_normalization;
514609

515-
light += throughput * emissive;
516-
throughput *= albedo;
517-
light += throughput * direct_light * bake_params.bounce_indirect_energy;
610+
light += throughput * emissive * albedo_alpha.a;
611+
throughput = mix(throughput, throughput * albedo_alpha.rgb, albedo_alpha.a);
612+
light += throughput * direct_light * bake_params.bounce_indirect_energy * albedo_alpha.a;
613+
614+
if (albedo_alpha.a < 1.0) {
615+
transparency_rays_left -= 1;
616+
depth -= 1;
617+
if (transparency_rays_left <= 0) {
618+
break;
619+
}
620+
621+
// Either bounce off the transparent surface or keep going forward
622+
float pa = albedo_alpha.a * albedo_alpha.a;
623+
if (randomize(r_noise) > pa) {
624+
normal = prev_normal;
625+
}
626+
627+
position += normal * bake_params.bias;
628+
}
518629

519630
// Use Russian Roulette to determine a probability to terminate the bounce earlier as an optimization.
520631
// <https://computergraphics.stackexchange.com/questions/2316/is-russian-roulette-really-the-answer>
@@ -532,9 +643,53 @@ vec3 trace_indirect_light(vec3 p_position, vec3 p_ray_dir, inout uint r_noise) {
532643
// Look for the environment color and stop bouncing.
533644
light += throughput * trace_environment_color(ray_dir);
534645
break;
535-
} else {
536-
// Ignore any other trace results.
537-
break;
646+
} else if (trace_result == RAY_BACK) {
647+
Vertex vert0 = vertices.data[triangles.data[tidx].indices.x];
648+
Vertex vert1 = vertices.data[triangles.data[tidx].indices.y];
649+
Vertex vert2 = vertices.data[triangles.data[tidx].indices.z];
650+
vec3 uvw = vec3(barycentric.x * vert0.uv + barycentric.y * vert1.uv + barycentric.z * vert2.uv, float(triangles.data[tidx].slice));
651+
position = barycentric.x * vert0.position + barycentric.y * vert1.position + barycentric.z * vert2.position;
652+
653+
vec4 albedo_alpha = textureLod(sampler2DArray(albedo_tex, linear_sampler), uvw, 0).rgba;
654+
655+
if (albedo_alpha.a > 1.0)
656+
break;
657+
658+
transparency_rays_left -= 1;
659+
depth -= 1;
660+
if (transparency_rays_left <= 0) {
661+
break;
662+
}
663+
664+
vec3 norm0 = vec3(vert0.normal_xy, vert0.normal_z);
665+
vec3 norm1 = vec3(vert1.normal_xy, vert1.normal_z);
666+
vec3 norm2 = vec3(vert2.normal_xy, vert2.normal_z);
667+
vec3 normal = barycentric.x * norm0 + barycentric.y * norm1 + barycentric.z * norm2;
668+
669+
vec3 direct_light = vec3(0.0f);
670+
#ifdef USE_LIGHT_TEXTURE_FOR_BOUNCES
671+
direct_light += textureLod(sampler2DArray(source_light, linear_sampler), uvw, 0.0).rgb;
672+
#else
673+
// Trace the lights directly. Significantly more expensive but more accurate in scenarios
674+
// where the lightmap texture isn't reliable.
675+
for (uint i = 0; i < bake_params.light_count; i++) {
676+
vec3 light;
677+
vec3 light_dir;
678+
trace_direct_light(position, normal, i, false, light, light_dir, r_noise);
679+
direct_light += light * lights.data[i].indirect_energy;
680+
}
681+
682+
direct_light *= bake_params.exposure_normalization;
683+
#endif
684+
685+
vec3 emissive = textureLod(sampler2DArray(emission_tex, linear_sampler), uvw, 0).rgb;
686+
emissive *= bake_params.exposure_normalization;
687+
688+
light += throughput * emissive * albedo_alpha.a;
689+
throughput = mix(mix(throughput, throughput * albedo_alpha.rgb, albedo_alpha.a), vec3(0.0), albedo_alpha.a);
690+
light += throughput * direct_light * bake_params.bounce_indirect_energy * albedo_alpha.a;
691+
692+
position += ray_dir * bake_params.bias;
538693
}
539694
}
540695

modules/lightmapper_rd/register_types.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ void initialize_lightmapper_rd_module(ModuleInitializationLevel p_level) {
5252
GLOBAL_DEF(PropertyInfo(Variant::INT, "rendering/lightmapping/bake_quality/ultra_quality_ray_count", PROPERTY_HINT_RANGE, "1,4096,1,or_greater"), 2048);
5353
GLOBAL_DEF(PropertyInfo(Variant::INT, "rendering/lightmapping/bake_performance/max_rays_per_pass", PROPERTY_HINT_RANGE, "1,256,1,or_greater"), 32);
5454
GLOBAL_DEF(PropertyInfo(Variant::INT, "rendering/lightmapping/bake_performance/region_size", PROPERTY_HINT_RANGE, "1,4096,1,or_greater"), 512);
55+
GLOBAL_DEF(PropertyInfo(Variant::INT, "rendering/lightmapping/bake_performance/max_transparency_rays", PROPERTY_HINT_RANGE, "1,256,1,or_greater"), 64);
5556

5657
GLOBAL_DEF(PropertyInfo(Variant::INT, "rendering/lightmapping/bake_quality/low_quality_probe_ray_count", PROPERTY_HINT_RANGE, "1,4096,1,or_greater"), 64);
5758
GLOBAL_DEF(PropertyInfo(Variant::INT, "rendering/lightmapping/bake_quality/medium_quality_probe_ray_count", PROPERTY_HINT_RANGE, "1,4096,1,or_greater"), 256);

scene/3d/lightmap_gi.cpp

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -791,7 +791,7 @@ LightmapGI::BakeError LightmapGI::bake(Node *p_from_node, String p_image_data_pa
791791
w_albedo[i + 0] = uint8_t(CLAMP(float(r_aa[i + 0]) * (1.0 - float(r_orm[i + 2] / 255.0)), 0, 255));
792792
w_albedo[i + 1] = uint8_t(CLAMP(float(r_aa[i + 1]) * (1.0 - float(r_orm[i + 2] / 255.0)), 0, 255));
793793
w_albedo[i + 2] = uint8_t(CLAMP(float(r_aa[i + 2]) * (1.0 - float(r_orm[i + 2] / 255.0)), 0, 255));
794-
w_albedo[i + 3] = 255;
794+
w_albedo[i + 3] = r_aa[i + 3];
795795
}
796796

797797
md.albedo_on_uv2.instantiate();
@@ -812,6 +812,11 @@ LightmapGI::BakeError LightmapGI::bake(Node *p_from_node, String p_image_data_pa
812812
continue;
813813
}
814814
Array a = mf.mesh->surface_get_arrays(i);
815+
Ref<Material> mat = mf.mesh->surface_get_material(i);
816+
RID mat_rid = RID();
817+
if (mat.is_valid()) {
818+
mat_rid = mat->get_rid();
819+
}
815820

816821
Vector<Vector3> vertices = a[Mesh::ARRAY_VERTEX];
817822
const Vector3 *vr = vertices.ptr();
@@ -861,6 +866,7 @@ LightmapGI::BakeError LightmapGI::bake(Node *p_from_node, String p_image_data_pa
861866

862867
md.uv2.push_back(uvr[vidx[k]]);
863868
md.normal.push_back(normal_xform.xform(nr[vidx[k]]).normalized());
869+
md.material.push_back(mat_rid);
864870
}
865871
}
866872
}

scene/3d/lightmapper.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,7 @@ class Lightmapper : public RefCounted {
170170
Vector<Vector3> points;
171171
Vector<Vector2> uv2;
172172
Vector<Vector3> normal;
173+
Vector<RID> material;
173174
Ref<Image> albedo_on_uv2;
174175
Ref<Image> emission_on_uv2;
175176
Variant userdata;

0 commit comments

Comments
 (0)