From a70c8f48355f30a9f487e05b464919a9a53a207b Mon Sep 17 00:00:00 2001 From: Eric Wasylishen Date: Wed, 7 Dec 2022 00:15:21 -0700 Subject: [PATCH] light: fix artifacting with translucent shadows Embree is allowed to call the filter function more than necessary. The previous implementation had side effects that would cause the translucent shadows to get darker in this case. As a limitation, we're back to only allowing 1 layer of glass per ray. --- include/light/trace_embree.hh | 26 +++++- light/trace_embree.cc | 8 +- testmaps/q2_light_translucency.map | 87 ++++++++++++++++++ .../q2_wal_metadata/textures/e1u1/test.wal | Bin 0 -> 5540 bytes tests/test_ltface.cc | 14 +++ 5 files changed, 128 insertions(+), 7 deletions(-) create mode 100644 testmaps/q2_light_translucency.map create mode 100644 testmaps/q2_wal_metadata/textures/e1u1/test.wal diff --git a/include/light/trace_embree.hh b/include/light/trace_embree.hh index 05f73115..d1335973 100644 --- a/include/light/trace_embree.hh +++ b/include/light/trace_embree.hh @@ -42,6 +42,10 @@ public: std::vector _ray_colors; std::vector _ray_normalcontribs; + std::vector _ray_hit_glass; + std::vector _ray_glass_color; + std::vector _ray_glass_opacity; + // This is set to the modelinfo's switchshadstyle if the ray hit // a dynamic shadow caster. (note that for rays that hit dynamic // shadow casters, all of the other hit data is assuming the ray went @@ -63,6 +67,9 @@ public: _point_indices.resize(size); _ray_colors.resize(size); _ray_normalcontribs.resize(size); + _ray_hit_glass.resize(size); + _ray_glass_color.resize(size); + _ray_glass_opacity.resize(size); _ray_dynamic_styles.resize(size); } @@ -74,10 +81,23 @@ public: return _point_indices[j]; } - inline qvec3f &getPushedRayColor(size_t j) + inline qvec3f getPushedRayColor(size_t j) { Q_assert(j < _maxrays); - return _ray_colors[j]; + qvec3f result = _ray_colors[j]; + + if (_ray_hit_glass[j]) { + const qvec3f glasscolor = _ray_glass_color[j]; + const float opacity = _ray_glass_opacity[j]; + + // multiply ray color by glass color + const qvec3f tinted = result * glasscolor; + + // lerp ray color between original ray color and fully tinted by the glass texture color, based on the glass opacity + result = mix(result, tinted, opacity); + } + + return result; } inline qvec3d &getPushedRayNormalContrib(size_t j) @@ -210,6 +230,7 @@ public: if (normalcontrib) { _ray_normalcontribs[_numrays] = *normalcontrib; } + _ray_hit_glass[_numrays] = false; _ray_dynamic_styles[_numrays] = 0; _numrays++; } @@ -296,6 +317,7 @@ public: if (normalcontrib) { _ray_normalcontribs[_numrays] = *normalcontrib; } + _ray_hit_glass[_numrays] = false; _ray_dynamic_styles[_numrays] = 0; _numrays++; } diff --git a/light/trace_embree.cc b/light/trace_embree.cc index 65766ead..1f74a6db 100644 --- a/light/trace_embree.cc +++ b/light/trace_embree.cc @@ -678,11 +678,9 @@ static void AddGlassToRay(RTCIntersectContext *context, unsigned rayIndex, float Q_assert(rayIndex < rs->_numrays); - // multiply ray color by glass color - qvec3f tinted = rs->_ray_colors[rayIndex] * glasscolor; - - // lerp ray color between original ray color and fully tinted by the glass texture color, based on the glass opacity - rs->_ray_colors[rayIndex] = mix(rs->_ray_colors[rayIndex], tinted, opacity); + rs->_ray_hit_glass[rayIndex] = true; + rs->_ray_glass_color[rayIndex] = glasscolor; + rs->_ray_glass_opacity[rayIndex] = opacity; } static void AddDynamicOccluderToRay(RTCIntersectContext *context, unsigned rayIndex, int style) diff --git a/testmaps/q2_light_translucency.map b/testmaps/q2_light_translucency.map new file mode 100644 index 00000000..84653c6e --- /dev/null +++ b/testmaps/q2_light_translucency.map @@ -0,0 +1,87 @@ +// Game: Quake 2 +// Format: Quake2 (Valve) +// entity 0 +{ +"mapversion" "220" +"classname" "worldspawn" +"_tb_textures" "textures/e1u1" +"_bounce" "0" +"_tb_def" "builtin:Quake2.fgd" +// brush 0 +{ +( -160 -256 16 ) ( -160 -255 16 ) ( -160 -256 17 ) e1u1/floor1_1 [ 0 -1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 80 -384 16 ) ( 80 -384 17 ) ( 81 -384 16 ) e1u1/floor1_1 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 80 -256 16 ) ( 81 -256 16 ) ( 80 -255 16 ) e1u1/floor1_1 [ -1 0 0 0 ] [ 0 -1 0 0 ] 0 1 1 +( 496 -32 32 ) ( 496 -31 32 ) ( 497 -32 32 ) e1u1/floor1_1 [ 1 0 0 0 ] [ 0 -1 0 0 ] 0 1 1 +( 496 368 32 ) ( 497 368 32 ) ( 496 368 33 ) e1u1/floor1_1 [ -1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 496 -32 32 ) ( 496 -32 33 ) ( 496 -31 32 ) e1u1/floor1_1 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +} +// brush 1 +{ +( -160 -384 32 ) ( -160 -383 32 ) ( -160 -384 33 ) e1u1/floor1_1 [ 0 -1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( -160 -384 32 ) ( -160 -384 33 ) ( -159 -384 32 ) e1u1/floor1_1 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( -160 -384 32 ) ( -159 -384 32 ) ( -160 -383 32 ) e1u1/floor1_1 [ -1 0 0 0 ] [ 0 -1 0 0 ] 0 1 1 +( 256 -320 288 ) ( 256 -319 288 ) ( 257 -320 288 ) e1u1/floor1_1 [ 1 0 0 0 ] [ 0 -1 0 0 ] 0 1 1 +( 256 -320 48 ) ( 257 -320 48 ) ( 256 -320 49 ) e1u1/floor1_1 [ -1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 496 -320 48 ) ( 496 -320 49 ) ( 496 -319 48 ) e1u1/floor1_1 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +} +// brush 2 +{ +( -160 304 32 ) ( -160 305 32 ) ( -160 304 33 ) e1u1/floor1_1 [ 0 -1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( -160 304 32 ) ( -160 304 33 ) ( -159 304 32 ) e1u1/floor1_1 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( -160 304 32 ) ( -159 304 32 ) ( -160 305 32 ) e1u1/floor1_1 [ -1 0 0 0 ] [ 0 -1 0 0 ] 0 1 1 +( 256 368 288 ) ( 256 369 288 ) ( 257 368 288 ) e1u1/floor1_1 [ 1 0 0 0 ] [ 0 -1 0 0 ] 0 1 1 +( 256 368 48 ) ( 257 368 48 ) ( 256 368 49 ) e1u1/floor1_1 [ -1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 496 368 48 ) ( 496 368 49 ) ( 496 369 48 ) e1u1/floor1_1 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +} +// brush 3 +{ +( -160 240 32 ) ( -160 241 32 ) ( -160 240 33 ) e1u1/floor1_1 [ 0 -1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( -784 -320 32 ) ( -784 -320 33 ) ( -783 -320 32 ) e1u1/floor1_1 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( -784 240 32 ) ( -783 240 32 ) ( -784 241 32 ) e1u1/floor1_1 [ -1 0 0 0 ] [ 0 -1 0 0 ] 0 1 1 +( -368 304 288 ) ( -368 305 288 ) ( -367 304 288 ) e1u1/floor1_1 [ 1 0 0 0 ] [ 0 -1 0 0 ] 0 1 1 +( -368 304 48 ) ( -367 304 48 ) ( -368 304 49 ) e1u1/floor1_1 [ -1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( -128 304 48 ) ( -128 304 49 ) ( -128 305 48 ) e1u1/floor1_1 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +} +// brush 4 +{ +( -160 -256 288 ) ( -160 -255 288 ) ( -160 -256 289 ) e1u1/floor1_1 [ 0 -1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 80 -320 288 ) ( 80 -320 289 ) ( 81 -320 288 ) e1u1/floor1_1 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 80 -256 288 ) ( 81 -256 288 ) ( 80 -255 288 ) e1u1/floor1_1 [ -1 0 0 0 ] [ 0 -1 0 0 ] 0 1 1 +( 496 -32 304 ) ( 496 -31 304 ) ( 497 -32 304 ) e1u1/floor1_1 [ 1 0 0 0 ] [ 0 -1 0 0 ] 0 1 1 +( 496 304 304 ) ( 497 304 304 ) ( 496 304 305 ) e1u1/floor1_1 [ -1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 496 -32 304 ) ( 496 -32 305 ) ( 496 -31 304 ) e1u1/floor1_1 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +} +// brush 5 +{ +( 496 240 32 ) ( 496 241 32 ) ( 496 240 33 ) e1u1/floor1_1 [ 0 -1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( -128 -320 32 ) ( -128 -320 33 ) ( -127 -320 32 ) e1u1/floor1_1 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( -128 240 32 ) ( -127 240 32 ) ( -128 241 32 ) e1u1/floor1_1 [ -1 0 0 0 ] [ 0 -1 0 0 ] 0 1 1 +( 288 304 288 ) ( 288 305 288 ) ( 289 304 288 ) e1u1/floor1_1 [ 1 0 0 0 ] [ 0 -1 0 0 ] 0 1 1 +( 288 304 48 ) ( 289 304 48 ) ( 288 304 49 ) e1u1/floor1_1 [ -1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 528 304 48 ) ( 528 304 49 ) ( 528 305 48 ) e1u1/floor1_1 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +} +// brush 6 +{ +( 64 -176 176 ) ( 64 -175 176 ) ( 64 -176 177 ) e1u1/skip [ 0 -1 0 0 ] [ 0 0 -1 16 ] 0 1 1 32 128 0 +( 24 -176 176 ) ( 24 -176 177 ) ( 25 -176 176 ) e1u1/skip [ -1.0000000000000002 0 0 16 ] [ 0 0 -1.0000000000000002 16 ] 0 1 1 32 128 0 +( 24 -176 208 ) ( 25 -176 208 ) ( 24 -175 208 ) e1u1/skip [ 0 -1.0000000000000002 0 0 ] [ -1.0000000000000002 0 0 0 ] 0 1 1 32 128 0 +( 336 48 224 ) ( 336 49 224 ) ( 337 48 224 ) e1u1/test [ 1 0 0 0 ] [ 0 -1 0 32 ] 0 2 2 32 16 0 +( 336 0 184 ) ( 337 0 184 ) ( 336 0 185 ) e1u1/skip [ 1.0000000000000002 0 0 0 ] [ 0 0 -1.0000000000000002 16 ] 0 1 1 32 128 0 +( 256 48 184 ) ( 256 48 185 ) ( 256 49 184 ) e1u1/skip [ 0 -1 0 0 ] [ 0 0 -1 16 ] 0 1 1 32 128 0 +} +} +// entity 1 +{ +"classname" "info_player_start" +"origin" "152 -256 96" +"angle" "90" +} +// entity 2 +{ +"classname" "light" +"origin" "152 -96 248" +"light" "150" +"delay" "3" +"_anglesense" "0" +} diff --git a/testmaps/q2_wal_metadata/textures/e1u1/test.wal b/testmaps/q2_wal_metadata/textures/e1u1/test.wal new file mode 100644 index 0000000000000000000000000000000000000000..9d4c88ab2d2a69e8bf55b2e46cf0d981e7aac2ca GIT binary patch literal 5540 zcmeIuu?>JQ3`Idkpn!c?B@$U6Sjw6iDNdh;{=W1IyS!eawy0km`#t$yy{j|3vOA9- e8%BTt0RjXF5FkK+009C72oNAZfB*pkTY(82^-C-O literal 0 HcmV?d00001 diff --git a/tests/test_ltface.cc b/tests/test_ltface.cc index 8ebb0304..edb45ab0 100644 --- a/tests/test_ltface.cc +++ b/tests/test_ltface.cc @@ -222,3 +222,17 @@ TEST_CASE("q2_dirt") { CHECK(sample == qvec3b(96)); }); } + +TEST_CASE("q2_light_translucency") { + INFO("liquids cast translucent colored shadows (sampling texture) by default"); + + auto [bsp, bspx] = LoadTestmap("q2_light_translucency.map", {}); + + auto *face_under_water = BSP_FindFaceAtPoint(&bsp, &bsp.dmodels[0], {152, -96, 32}); + REQUIRE(face_under_water); + + CheckFaceLuxels(bsp, *face_under_water, [](qvec3b sample){ + INFO("green color from the texture"); + CHECK(sample == qvec3b(100, 150, 100)); + }); +}