@@ -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+
293325uint 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
361393void 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
0 commit comments