Share
Embed
ライトたくさんReSTIRを実装しよう
2020年に発表された論文「Spatiotemporal reservoir resampling for real-time ray tracing with dynamic direct lighting」を実装してみます
これは2024年8月10日に行われた Kernel/VM探検隊@東京 No17 での発表資料です
発表動画:
ソースコード: https://github.com/Fadis/gct/tree/kernelvm_20240810
Fadis
August 10, 2024
Transcript
80 slides
-
ϨϯμϦϯάํఔࣜ p ωi ωo x n ํʹඈΜͰ͘Δޫ ωo Lo (x,
ωo) = Le (x, ωo) + ∫ S2 fs (x, ωi , ωo) Li (x, ωi)|ωi ⋅ n|dωi ମ͔Β ํʹ ൃͤΒΕΔޫ ωo ͔Βೖ͖ͬͯͨޫͷ͏ͪ ํʹ͖Λม͑Δޫͷׂ߹ ωi ωo ͔Βೖͬͯ͘Δޫ ωi Λ͍ͨ໘͕ड͚ΒΕΔ ޫͷׂ߹ n -
p Lo (x, ωo) = Le (x, ωo) + ∫
S2 fs (x, ωi , ωo) Li (x, ωi)|ωi ⋅ n|dωi ωi ωo x n Λத৺ͱ͢Δશͯͷํʹ͍ͭͯͷੵ x ϦΞϧλΠϜϨϯμϦϯάΛ͢Δʹ ͜ͷੵΛճආ͍ͨ͠ ϨϯμϦϯάํఔࣜ -
p Lo (x, ωo) = Le (x, ωo) + ∫
S2 fs (x, ωi , ωo) Li (x, ωi)|ωi ⋅ n|dωi x l Lo (x, ωo) = Le (x, ωo) + fs x, l − x l − x , ωo L l − x l − x ⋅ n །Ұͷޫݯ͕ͩͱ͢ΔͱੵΛճආͰ͖Δ ͔Β ʹಧ͘ޫͷΤωϧΪʔ l x ޫݯ -
p x l0 Lo (x, ωo) = Le (x, ωo)
+ fs x, l0 − x l0 − x , ωo L0 l0 − x l0 − x ⋅ n + fs x, l1 − x l1 − x , ωo L1 l1 − x l1 − x ⋅ n ޫݯ͕ෳ͋ͬͨΒ l1 ͦΕͧΕͷޫݯͷӨڹΛܭࢉͯ͢͠ ܭࢉίετ͕ޫݯͷʹൺྫͯ͠૿͑Δ -
ΤωϧΪʔ: ڑ: ද໘ੵ: ୯Ґ໘ੵ͋ͨΓͷΤωϧΪʔ: L 1 4π L 4π ΤωϧΪʔ:
ڑ: ද໘ੵ: ୯Ґ໘ੵ͋ͨΓͷΤωϧΪʔ: L 2 16π L 16π ΤωϧΪʔ: ڑ: ද໘ੵ: ୯Ґ໘ੵ͋ͨΓͷΤωϧΪʔ: L 3 36π L 36π ޫݯ͔ΒͷޫͷΤωϧΪʔ ڑͷ2ʹൺྫ͢Δ ٯೋͷ๏ଇ -
Benedikt Bitterli, Chris Wyman, Matt Pharr, Peter Shirley, Aaron Lefohn,
and Wojciech Jarosz. 2020. Spatiotemporal reservoir resampling for real-time ray tracing with dynamic direct lighting. ACM Trans. Graph. 39, 4, Article 148 (August 2020), 17 pages. https://doi.org/ 10.1145/3386569.3392481 ReSTIR Reservoir-based Spatio-Temporal Importance Resampling -
Lo (x, ωo) = Le (x, ωo) + ∫ S2
fs (x, ωi , ωo) Li (x, ωi)|ωi ⋅ n|dωi ϞϯςΧϧϩϨΠτϨʔγϯά ͜ͷੵΛϞϯςΧϧϩ๏Ͱܭࢉ͢Δ x ϥϯμϜͳํ ͔ΒඈΜͰ͘Δޫ ΛௐΔ ωi Li (x, ωi) ेͳճ܁Γฦ͢ͱ શͯͷํʹ͍ͭͯͷੵΛ ۙࣅͰ͖Δ -
x n l − x ʹಧ͘ޫͷΤωϧΪʔʹؔΘΔཁૉ x ޫݯ͔Β์ͨΕΔΤωϧΪʔͷେ͖͞ ޫݯ͔Β ·ͰͷڑʹΑΔݮਰ
x l ໘ͷ͖ Ͱड͚ΒΕΔޫͷΤωϧΪʔͷׂ߹ n ఆ 1 l − x 2 max (l − x) l − x ⋅ n,0 -
x n ޫݯ͔Β ·ͰͷڑʹΑΔݮਰ x l ໘ͷ͖ Ͱड͚ΒΕΔޫͷΤωϧΪʔͷׂ߹ n ఆ
1 l − x 2 max (l − x) l − x ⋅ n,0 ःṭͷ༗ແ { 1 0 ( ͱ Λ݁Ϳઢͱަࠩ͢Δ໘͕ແ͍) l x ( ͱ Λ݁Ϳઢͱަࠩ͢Δ໘͕͋Δ) l x l − x -
ྻ BVH raygenγΣʔμ ͜ͷઢͱ ަࠩ͢Δ ࡾ֯ܗΛ͍ͩ͘͞ ަࠩఆ hitγΣʔμ ͜ͷࡾ֯ܗͱ ަࠩ͠·͢
ม γʔϯ͕มܗ͢Δ = ͜ͷม͕සൟʹඞཁ ͍ -
RTSDF Real-Time Signed Distance Field Tan Y.W., Chua N., Koh
C., Bhojan A., RTSDF: Real-time signed distance fields for soft shadow approximation in games, 2022, arXiv preprint https://arxiv.org/abs/ 2210.06160. -
x ൪ͷޫݯ͔Β ʹಧ͘ޫͷΤωϧΪʔΛ ͱ͢Δ n x Ln L0 L1 L2
L3 L4 L5 L6 L7 ൪ͷޫݯ͕બΕΔ֬ 6 = L6 ∑7 i=0 Ln શͯͷ ΛٻΊΔඞཁ͕͋Δ Ln ߴՁ -
Benedikt Bitterli, Chris Wyman, Matt Pharr, Peter Shirley, Aaron Lefohn,
and Wojciech Jarosz. 2020. Spatiotemporal reservoir resampling for real-time ray tracing with dynamic direct lighting. ACM Trans. Graph. 39, 4, Article 148 (August 2020), 17 pages. https://doi.org/ 10.1145/3386569.3392481 ReSTIR Reservoir-based Spatio-Temporal Importance Resampling -
Min-Te Chao. 1982. A General Purpose Unequal Probability Sampling Plan.
Biometrika 69, 3 (Dec. 1982), 653–656. https://doi.org/10/fd87zs WRS Weighted Reservoir Sampling -
Reservoir struct reservoir { unsigned int y; float w_y; float
w_sum; unsigned int m; }; ࠓબΜͰ͍Δཁૉ ࠓબΜͰ͍ΔཁૉͷॏΈ ࠓ·ͰʹબΜͩཁૉͷ ॏΈͷ૯ ࠓ·ͰʹબΜͩཁૉͷ (optional) Ε͊͟ʔ -
const unsigned int candidate = random() * max_candidate_index; const float
weight = calculate_weight( candidate ); res.w_sum += weight; res.m += 1u; if( random() < weight / res.w_sum ) { res.y = candidate; res.w_y = weight; } 0.0Ҏ্1.0ҎԼͷ Ұ༷ͳཚ ۉͳ֬Ͱ৽͍͠ཁૉͷީิΛ1ͭબͼ ͦͷཁૉͷॏΈΛٻΊΔ -
const float weight = calculate_weight( candidate ); res.w_sum += weight;
res.m += 1u; if( random() < weight / res.w_sum ) { res.y = candidate; res.w_y = weight; } ࠓ·Ͱʹࢼͨ͠ީิͷॏΈͷ૯ͱ ࠓ·Ͱʹࢼͨ͠ީิͷݸΛߋ৽ -
res.w_sum += weight; res.m += 1u; if( random() < weight
/ res.w_sum ) { res.y = candidate; res.w_y = weight; } ৽͍͠ީิͷॏΈ ࠓ·ͰͷશͯͷީิͷॏΈͷ૯ ͕ 0.0Ҏ্1.0ҎԼͷཚҎ্ͳΒ ࠓબΜͰ͍ΔཁૉΛߋ৽͢Δ -
#!/usr/bin/env python3 # -*- coding: utf-8 -*- import random class
Reservoir: def __init__(self): self.y = None self.w = 0 self.w_sum = 0 self.m = 0 def update( self, sample_id, weight ): self.w_sum += weight self.m += 1 b_accept = random.random() < weight / self.w_sum; if b_accept: self.y = sample_id self.w_y = weight return b_accept weight = [ random.random() for i in range( 0, 10 ) ] hist = [ 0 for i in range( 0, 10 ) ] res = [ Reservoir() for i in range( 0, 500 ) ] pdf = 1.0/len( weight ) for i in range( 0, 100 ): for j in range( 0, len( res ) ): sample_id = random.randint( 0, len( weight ) - 1 ) p_hat = weight[ sample_id ] w = p_hat / pdf res[ j ].update( sample_id, w ) hist[ res[ j ].y ] += 1 w_sum = sum( weight ) h_sum = sum( hist ) for i in range( 0, len(hist) -1 ): print( ( hist[ i ]/h_sum ) / ( weight[ i ]/w_sum ) ) ࣮ࡍʹͬͯΈΑ͏ 10ݸͷཁૉΛ ͜ͷॏΈͰநબͯ͠΄͍͠ Reservoir 500ݸ 100αΠΫϧ܁Γฦͨ࣌͠Ͱͷ ॏΈ͔ΒٻΊͨ֬ͱ ࣮ࡍʹཁૉ͕બΕׂͨ߹ͷ ൺΛදࣔ -
ϋογϡςʔϒϧʹ ه͢Δ Reservoir Reservoir Reservoir Reservoir Reservoir Reservoir Reservoir Reservoir
Reservoir hash ೋେൊ͕͋Δ εΫϦʔϯͷϐΫηϧຖʹ ه͢Δ εΫϦʔϯ -
ϋογϡςʔϒϧʹ ه͢Δ Reservoir Reservoir Reservoir Reservoir hash εΫϦʔϯͷϐΫηϧຖʹ ه͢Δ ར
ݟ͍͑ͯΔΛඳ͘ͷʹ ඞཁͳ࠷খݶͷReservoir͚͕ͩΔ ܽ ҰॠͰࢹք͔Β֎Εͨ ReservoirϦηοτ͞ΕΔ ར ࠓݟ͍͑ͯͳ͍෦ͷReservoir ͓ͯ͘͜͠ͱ͕Ͱ͖Δ ܽ Reservoir͕ͲΜͲΜ૿͑ΔͷͰ ཁΒͳ͍ΛࣺͯΔॲཧ͕ඞཁ -
ϋογϡςʔϒϧʹ ه͢Δ Reservoir Reservoir Reservoir Reservoir Reservoir Reservoir Reservoir Reservoir
Reservoir hash εΫϦʔϯͷϐΫηϧຖʹ ه͢Δ ࠓճͬͪ͜ͷख๏Ͱ͍͘ -
t − 2 t − 1 t p p p
Reservoirମͷද໘ͷ֤ʹ͍ͭͯͷใ ͋ΔϐΫηϧ ʹө͍ͬͯΔମͷද໘ ࣌ࠁ ͱڞʹมԽ͢Δ p t -
p Optical Flow ͷϩʔΧϧ࠲ඪʹ 1ϑϨʔϜલͷߦྻΛֻ͚ͯ ʹө͍ͬͯΔମͷද໘͕ 1ϑϨʔϜલͲ͜ʹ͔͋ͬͨΛ ٻΊΔ p p
1ϑϨʔϜલͷҐஔʹ͋Δ ReservoirΛ ͷҐஔͷաڈͷReservoirͱ͢Δ p -
// distance field { distance_field.clear( rec ); { auto render_pass_token
= rec.begin_render_pass( distance_field.get_distance_field_image().get_render_pass_begin_info(), vk::SubpassContents::eInline ); rec->setViewport( 0, { distance_field.get_distance_field_image().get_viewport() } ); rec->setScissor( 0, { distance_field.get_distance_field_image().get_scissor() } ); rec->setCullMode( vk::CullModeFlagBits::eNone ); rec->setDepthCompareOp( vk::CompareOp::eAlways ); rec.bind_descriptor_set( vk::PipelineBindPoint::eGraphics, 1u, sg->get_resource()->pipeline_layout, global_descriptor_set ); (*il)( rec, *voxel_csg, false ); } rec.graphics_to_compute_barrier( {}, { distance_field.get_working_image().get_image()->get_factory() } ); distance_field( rec ); } tone.set( rec, 0 ); { if( !update_optflow ) { update_optflow = true; } else { SDFΛੜ gct.cpp -
GόοϑΝΛੜ else { update_optflow = false; } rec.copy( global_data, global_uniform
); rec.transfer_to_graphics_barrier( { global_uniform->get_buffer() }, {} ); { auto render_pass_token = rec.begin_render_pass( gbuffer.get_render_pass_begin_info( 0 ), vk::SubpassContents::eInline ); rec->setViewport( 0, 1, &gbuffer.get_viewport() ); rec->setScissor( 0, 1, &gbuffer.get_scissor() ); rec->setCullMode( vk::CullModeFlagBits::eBack ); rec->setDepthCompareOp( vk::CompareOp::eLessOrEqual ); rec.bind_descriptor_set( vk::PipelineBindPoint::eGraphics, 1u, sg->get_resource()->pipeline_layout, global_descriptor_set ); (*il)( rec, *geometry_csg, true ); } if( walk.get_current_camera() == 0 ) { sg->rotate_visibility( rec ); } rec.convert_image( gbuffer.get_image( 0 ), vk::ImageLayout::eGeneral ); } rec.barrier( {}, gct.cpp -
reservoir->get_factory() } ); hgauss( rec, 0, res.width, res.height, 1u );
rec.barrier( {}, { temporary_diffuse->get_factory(), temporary_specular->get_factory() } ); vgauss( rec, 0, res.width, res.height, 1u ); rec.barrier( {}, { previous_diffuse->get_factory(), previous_specular->get_factory() } ); update_reservoir( rec, 0, res.width, res.height, 1u ); rec.copy( reservoir->get_factory(), previous_reservoir->get_factory() ); rec.fill( direct->get_factory(), gct::color::web::black ); rec.barrier( {}, { direct->get_factory() } ); direct_light( rec, 0, 1000u, 1u, 1u ); lighting( rec, 0, res.width, res.height, 1u ); rtao( rec ); rec.barrier( {}, { direct->get_factory(), diffuse->get_factory(), WRS ReservoirͰબΕ͍ͯΔ ޫݯʹΑΔর໌Λܭࢉ gct.cpp -
void main() { const ivec2 screen_pos = ivec2( gl_GlobalInvocationID.xy );
vec3 normal = imageLoad( gbuffer, ivec3( screen_pos, 2 ) ).xyz; const uint instance = uint( imageLoad( gbuffer, ivec3( screen_pos, 4 ) ).z ); const vec3 pos = imageLoad( gbuffer, ivec3( screen_pos, 0 ) ).xyz; const uint light_count = global_uniforms.light_count; const ivec3 image_size = imageSize( gbuffer ); const vec3 optical_flow = imageLoad( gbuffer, ivec3( screen_pos, 5 ) ).xyz; vec4 r = vec4( 32767, 0.0, 0.0, 0.0 ); const ivec2 previous_screen_pos = screen_pos - ivec2( ( optical_flow.xy * vec2( 0.5, 0.5 ) ) * ( vec2( image_size.xy ) ) ); { ivec2 n = previous_screen_pos; const bool valid_pos = ( n.x >= 0 && n.x < image_size.x && n.y >= 0 && n.y < image_size.y ); const vec4 hist = imageLoad( previous_reservoir, ivec3( n, 1 ) ); const vec3 prev_normal = hist.xyz; const uint prev_instance = uint( hist.w ); const bool history_valid = valid_pos && ( instance == prev_instance ) && ( dot( normal, prev_normal ) >= 0.99 ); r = history_valid ? imageLoad( previous_reservoir, ivec3( n, 0 ) ) : r; } vec2 rand_seed = vec2( screen_pos.xy ) + vec2( global_uniforms.frame_counter.x * 48.0, global_uniforms.frame_counter.x * 37.0 ); const uint y = uint( r.x ); const float w_y = r.y; wrs.comp Optical FlowΛͬͯલͷϑϨʔϜͷReservoirΛҾ͖ܧ͙ -
vec2 rand_seed = vec2( screen_pos.xy ) + vec2( global_uniforms.frame_counter.x *
48.0, global_uniforms.frame_counter.x * 37.0 ); const uint y = uint( r.x ); const float w_y = r.y; const float speed = min( length( optical_flow.xy ), 1.0 ); const float w_sum = r.z * mix( 1.0, 0.01, speed ); const float m = r.w; uint light_index = 0; float weight = 0.0; for( uint retry = 0; retry != 16; retry++ ) { rand_seed = rand1_vec2( rand_seed ); light_index = active_light[ uint( rand_seed.x * light_count ) ]; float recv_ratio = dot( normalize( light[ light_index ].world_position.xyz - pos.xyz ), normal ); const float d = distance( pos.xyz, light[ light_index ].world_position.xyz ); weight = recv_ratio * max( max( light[ light_index ].energy.r, light[ light_index ].energy.g ), light[ light_index ].energy.b ) / ( d * d ) / light_count; if( recv_ratio > 0.0 ) { break; } } rand_seed = rand1_vec2( rand_seed ); const float rand01 = rand_seed.x; bool update = ( rand01 < weight / w_sum + weight ); const vec3 ray_begin = ( matrix_pool[ global_uniforms.voxel ] * vec4( pos, 1.0 ) ).xyz; const vec3 ray_end = wrs.comp ۉͳ֬ͰޫݯΛ1ͭબͼ ःṭ͕ແ͔ͬͨ߹ମͷද໘ʹಧ͘ޫͷΤωϧΪʔΛܭࢉ͢Δ -
), light[ light_index ].energy.b ) / ( d * d
) / light_count; if( recv_ratio > 0.0 ) { break; } } rand_seed = rand1_vec2( rand_seed ); const float rand01 = rand_seed.x; bool update = ( rand01 < weight / w_sum + weight ); const vec3 ray_begin = ( matrix_pool[ global_uniforms.voxel ] * vec4( pos, 1.0 ) ).xyz; const vec3 ray_end = ( matrix_pool[ global_uniforms.voxel ] * vec4( light[ light_index ].world_position.xyz, 1.0 ) ).xyz; const vec3 ray_dir = normalize( ray_end - ray_begin ); const float ray_distance = distance( ray_begin, ray_end ); vec3 ray_cur = ray_begin + ray_dir * 4.0/512.0; float ray_traveled = 4.0/512.0; float prev_r = 0.0; float grad = 4.0/512.0; for( int j = 0; j < 64; j++ ) { float r = texture( distance_field, ray_cur ).r; prev_r = r; grad = r - prev_r; ray_cur += ray_dir * r; ray_traveled += r; } imageStore( reservoir, ivec3( screen_pos, 0 ), ( update && ( ray_traveled >= ray_distance || grad > 0.0 ) ) ? vec4( light_index, weight, w_sum + weight, m + 1 ) : wrs.comp SDFΛͬͯମͷද໘͔Βޫݯ·Ͱͷؒʹ ःṭ͕ͳ͍ࣄΛ֬ೝ͢Δ -
bool update = ( rand01 < weight / w_sum +
weight ); const vec3 ray_begin = ( matrix_pool[ global_uniforms.voxel ] * vec4( pos, 1.0 ) ).xyz; const vec3 ray_end = ( matrix_pool[ global_uniforms.voxel ] * vec4( light[ light_index ].world_position.xyz, 1.0 ) ).xyz; const vec3 ray_dir = normalize( ray_end - ray_begin ); const float ray_distance = distance( ray_begin, ray_end ); vec3 ray_cur = ray_begin + ray_dir * 4.0/512.0; float ray_traveled = 4.0/512.0; float prev_r = 0.0; float grad = 4.0/512.0; for( int j = 0; j < 64; j++ ) { float r = texture( distance_field, ray_cur ).r; prev_r = r; grad = r - prev_r; ray_cur += ray_dir * r; ray_traveled += r; } imageStore( reservoir, ivec3( screen_pos, 0 ), ( update && ( ray_traveled >= ray_distance || grad > 0.0 ) ) ? vec4( light_index, weight, w_sum + weight, m + 1 ) : vec4( y, w_y, y == 32767 ? 0.0 : w_sum + weight, y == 32767 ? 0 : m + 1 ) ); imageStore( reservoir, ivec3( screen_pos, 1 ), vec4( normal, instance ) ); } wrs.comp ཚͰબΜͩޫݯ͕ःṭ͞Ε͓ͯΒͣɺWRSͰߋ৽͖͢ͱஅ͞Εͨ߹ Reservoir͕ࢦ͍ͯ͠ΔޫݯΛߋ৽͢Δ -
); } rec.barrier( {}, { reservoir->get_factory() } ); hgauss( rec,
0, res.width, res.height, 1u ); rec.barrier( {}, { temporary_diffuse->get_factory(), temporary_specular->get_factory() } ); vgauss( rec, 0, res.width, res.height, 1u ); rec.barrier( {}, { previous_diffuse->get_factory(), previous_specular->get_factory() } ); update_reservoir( rec, 0, res.width, res.height, 1u ); rec.copy( reservoir->get_factory(), previous_reservoir->get_factory() ); rec.fill( direct->get_factory(), gct::color::web::black ); rec.barrier( {}, { direct->get_factory() } ); direct_light( rec, 0, 1000u, 1u, 1u ); lighting( rec, 0, res.width, res.height, 1u ); rtao( rec ); gct.cpp લͷϑϨʔϜͰͷ֦ࢄͱࣹʹAdaptive BlurΛ͔͚͓ͯ͘ -
uint light_index = uint( imageLoad( reservoir, ivec3( screen_pos, 0 )
).x ); vec3 diffuse = vec3( 0.0, 0.0, 0.0 ); vec3 specular = vec3( 0.0, 0.0, 0.0 ); const bool light_valid = ( light_index != 32767 ); light_index = light_valid ? light_index : 0; const float shadow_level = 1.0; const vec3 N = normal; const vec3 V = normalize(global_uniforms.eye_pos.xyz-pos); const vec3 L = normalize( light[ light_index ].world_position.xyz-pos); const float d = distance( pos.xyz, light[ light_index ].world_position.xyz ); const vec3 energy = light[ light_index ].energy.rgb / ( d * d ); diffuse = light_valid ? diffuse_with_mask( L, V, N, albedo.rgb, roughness, metallicness, emissive, energy, shadow_level ) : diffuse; specular = light_valid ? specular_with_mask( L, V, N, albedo.rgb, roughness, metallicness, energy, shadow_level ) : specular; const ivec3 image_size = imageSize( gbuffer ); const vec3 optical_flow = imageLoad( gbuffer, ivec3( screen_pos, 5 ) ).xyz; const uint instance = uint( imageLoad( gbuffer, ivec3( screen_pos, 4 ) ).z ); const ivec2 previous_screen_pos = screen_pos - ivec2( ( optical_flow.xy * vec2( 0.5, 0.5 ) ) * ( vec2( image_size.xy ) ) ); lighting.comp Reservoir͕ࢦ͍ͯ͠ΔޫݯʹΑΔࣹͱ֦ࢄΛܭࢉ͢Δ -
specular = light_valid ? specular_with_mask( L, V, N, albedo.rgb, roughness,
metallicness, energy, shadow_level ) : specular; const ivec3 image_size = imageSize( gbuffer ); const vec3 optical_flow = imageLoad( gbuffer, ivec3( screen_pos, 5 ) ).xyz; const uint instance = uint( imageLoad( gbuffer, ivec3( screen_pos, 4 ) ).z ); const ivec2 previous_screen_pos = screen_pos - ivec2( ( optical_flow.xy * vec2( 0.5, 0.5 ) ) * ( vec2( image_size.xy ) ) ); vec4 existing_diffuse = vec4( 0.0, 0.0, 0.0, 1.0 ); vec4 existing_specular = vec4( 0.0, 0.0, 0.0, 1.0 ); uvec2 hist = uvec2( 0, instance ); const bool history_in_range = ( previous_screen_pos.x >= 0 && previous_screen_pos.x < image_size.x && previous_screen_pos.y >= 0 && previous_screen_pos.y < image_size.y ); const uvec2 history_value = imageLoad( previous_history, previous_screen_pos ).xy; const vec4 diffuse_value = imageLoad( previous_diffuse_image, previous_screen_pos ); const vec4 specular_value = imageLoad( previous_specular_image, previous_screen_pos ); const bool history_valid = history_in_range && ( history_value.y == instance ); existing_diffuse = history_valid ? diffuse_value : existing_diffuse; existing_specular = history_valid ? specular_value : existing_specular; hist = history_valid ? history_value : hist; const float speed = length( optical_flow.xy ); hist.x = min( hist.x, uint( mix( 300.0, 1.0, speed ) ) ); imageStore( diffuse_image, screen_pos, ( vec4( diffuse, 1.0 ) + existing_diffuse * hist.x )/( hist.x + 1 ) ); imageStore( specular_image, screen_pos, ( vec4( specular, 1.0 ) + existing_specular * hist.x )/( hist.x + 1 ) ); lighting.comp Optical FlowΛͬͯաڈͷϑϨʔϜͰͷ֦ࢄͱࣹΛҾ͖ܧ͙ -
const ivec3 image_size = imageSize( gbuffer ); const vec3 optical_flow
= imageLoad( gbuffer, ivec3( screen_pos, 5 ) ).xyz; const uint instance = uint( imageLoad( gbuffer, ivec3( screen_pos, 4 ) ).z ); const ivec2 previous_screen_pos = screen_pos - ivec2( ( optical_flow.xy * vec2( 0.5, 0.5 ) ) * ( vec2( image_size.xy ) ) ); vec4 existing_diffuse = vec4( 0.0, 0.0, 0.0, 1.0 ); vec4 existing_specular = vec4( 0.0, 0.0, 0.0, 1.0 ); uvec2 hist = uvec2( 0, instance ); const bool history_in_range = ( previous_screen_pos.x >= 0 && previous_screen_pos.x < image_size.x && previous_screen_pos.y >= 0 && previous_screen_pos.y < image_size.y ); const uvec2 history_value = imageLoad( previous_history, previous_screen_pos ).xy; const vec4 diffuse_value = imageLoad( previous_diffuse_image, previous_screen_pos ); const vec4 specular_value = imageLoad( previous_specular_image, previous_screen_pos ); const bool history_valid = history_in_range && ( history_value.y == instance ); existing_diffuse = history_valid ? diffuse_value : existing_diffuse; existing_specular = history_valid ? specular_value : existing_specular; hist = history_valid ? history_value : hist; const float speed = length( optical_flow.xy ); hist.x = min( hist.x, uint( mix( 300.0, 1.0, speed ) ) ); imageStore( diffuse_image, screen_pos, ( vec4( diffuse, 1.0 ) + existing_diffuse * hist.x )/( hist.x + 1 ) ); imageStore( specular_image, screen_pos, ( vec4( specular, 1.0 ) + existing_specular * hist.x )/( hist.x + 1 ) ); imageStore( history, screen_pos, uvec4( hist.x + 1, hist.y, 0, 0 ) ); } lighting.comp ࠓܭࢉͨ͠1αϯϓϧͱաڈͷϑϨʔϜ͔ΒҾ͖ܧ͍ͩαϯϓϧΛࠞͥΔ