@@ -241,6 +241,15 @@ class ImageBuffer {
241241 return v;
242242 }
243243
244+ auto read_effective_spp (Expr<uint2> p) noexcept {
245+ auto spp = def (0 .f );
246+ if (_image) {
247+ auto index = p.y * _resolution.x + p.x ;
248+ spp = _image->read (index * 4u + 3u );
249+ }
250+ return spp;
251+ }
252+
244253 void write (Expr<uint2> p, Expr<float3> value, Expr<float > effective_spp = 1 .f) noexcept {
245254 if (_image) {
246255 auto index = p.y * _resolution.x + p.x ;
@@ -687,6 +696,7 @@ luisa::unique_ptr<Integrator::Instance> GradientPathTracing::build(
687696 };
688697 }
689698 $else {
699+ // TODO: Check if this is correct
690700 shift_successful = false ;
691701 };
692702 };
@@ -916,6 +926,7 @@ luisa::unique_ptr<Integrator::Instance> GradientPathTracing::build(
916926 };
917927 }
918928 $else {
929+ // TODO: Check if this is correct
919930 shifted.alive = false ;
920931 };
921932 }
@@ -928,16 +939,12 @@ luisa::unique_ptr<Integrator::Instance> GradientPathTracing::build(
928939 // Deny shifts between Dirac and non-Dirac BSDFs. TODO
929940
930941 // TODO check if wo is wo
931- // Combined closures
932942 auto shifted_bsdf_eta = def (0 .f );// eta at previous main it
933- auto outgoing_direction = shifted.it .shading ().local_to_world (tangent_space_outgoing_direction);
934- auto eval = Surface::Evaluation::zero (swl.dimension ());
935943 pipeline ().surfaces ().dispatch (shifted.it .shape ().surface_tag (), [&](auto surface) noexcept {
936944 PolymorphicCall<Surface::Closure> shifted_call;
937945 surface->closure (shifted_call, shifted.it , swl, -shifted.ray ->direction (), 1 .f , time);
938946 shifted_call.execute ([&](auto closure) noexcept {
939947 shifted_bsdf_eta = closure->eta ().value_or (1 .f );
940- eval = closure->evaluate (-shifted.ray ->direction (), outgoing_direction);
941948 });
942949 });
943950
@@ -963,6 +970,16 @@ luisa::unique_ptr<Integrator::Instance> GradientPathTracing::build(
963970 shift_failed_flag = true ;
964971 };
965972
973+ auto outgoing_direction = shifted.it .shading ().local_to_world (tangent_space_outgoing_direction);
974+ auto eval = Surface::Evaluation::zero (swl.dimension ());
975+ pipeline ().surfaces ().dispatch (shifted.it .shape ().surface_tag (), [&](auto surface) noexcept {
976+ PolymorphicCall<Surface::Closure> shifted_call;
977+ surface->closure (shifted_call, shifted.it , swl, -shifted.ray ->direction (), 1 .f , time);
978+ shifted_call.execute ([&](auto closure) noexcept {
979+ eval = closure->evaluate (-shifted.ray ->direction (), outgoing_direction);
980+ });
981+ });
982+
966983 $if (!shift_failed_flag) {
967984 $if (eval.pdf <= 0 .f ) {
968985 // invalid path
@@ -1107,8 +1124,12 @@ void GradientPathTracingInstance::_render_one_camera(
11071124
11081125 luisa::unordered_map<luisa::string, luisa::unique_ptr<ImageBuffer>> image_buffers;
11091126 if (!node<GradientPathTracing>()->central_radiance ()) {
1127+ image_buffers.emplace (" gradient_x_single_frame" , luisa::make_unique<ImageBuffer>(pipeline (), resolution));
1128+ image_buffers.emplace (" gradient_y_single_frame" , luisa::make_unique<ImageBuffer>(pipeline (), resolution));
11101129 image_buffers.emplace (" gradient_x" , luisa::make_unique<ImageBuffer>(pipeline (), resolution));
11111130 image_buffers.emplace (" gradient_y" , luisa::make_unique<ImageBuffer>(pipeline (), resolution));
1131+ image_buffers.emplace (" gradient_x_variance" , luisa::make_unique<ImageBuffer>(pipeline (), resolution));
1132+ image_buffers.emplace (" gradient_y_variance" , luisa::make_unique<ImageBuffer>(pipeline (), resolution));
11121133 }
11131134 // This is sum(x^2)/spp. if Var(mean(x)) is need, it is sum(x^2)/(n(n-1)) - mean(x)^2/(n-1)
11141135 image_buffers.emplace (" variance" , luisa::make_unique<ImageBuffer>(pipeline (), resolution));
@@ -1161,12 +1182,14 @@ void GradientPathTracingInstance::_render_one_camera(
11611182 for (int i = 0 ; i < 4 ; i++) {
11621183 // right/bottom -> center, left/top -> left/top
11631184 auto current_pixel = pixel_id + (i < 2 ? make_uint2 (0 ) : pixel_shifts[i]);
1164- auto key = i % 2 == 0 ? " gradient_x" : " gradient_y" ;
1185+ string key = i % 2 == 0 ? " gradient_x" : " gradient_y" ;
11651186 auto sign = i < 2 ? 1 .f : -1 .f ;
11661187 $if (all (current_pixel >= 0u && current_pixel < resolution)) {
11671188 // Multiplied by 2 because MIS is used here
11681189 auto L = pipeline ().spectrum ()->srgb (eval.swl , sign * 2 .f * (eval.gradients [i] - eval.very_direct ));
1169- image_buffers.at (key)->accumulate (current_pixel, shutter_weight * L, 1 .f );
1190+ image_buffers.at (key + " _single_frame" )->accumulate (current_pixel, shutter_weight * L, 1 .f );
1191+ // image_buffers.at(key)->accumulate(current_pixel, shutter_weight * L, 1.f);
1192+ // image_buffers.at(key + "_variance")->accumulate(current_pixel, shutter_weight * L * L);
11701193 };
11711194 }
11721195 }
@@ -1184,6 +1207,16 @@ void GradientPathTracingInstance::_render_one_camera(
11841207 auto strength = max (max (max (abs_L.x , abs_L.y ), abs_L.z ), 0 .f );
11851208 auto clamp_L = L * (threshold / max (strength, threshold));
11861209 image_buffers.at (" variance" )->accumulate (pixel_id, clamp_L * clamp_L);
1210+
1211+ // Gradient buffer accumulation
1212+ if (!node<GradientPathTracing>()->central_radiance ()) {
1213+ for (int i = 0 ; i < 2 ; i++) {
1214+ string key = i == 0 ? " gradient_x" : " gradient_y" ;
1215+ auto L = image_buffers.at (key + " _single_frame" )->read (pixel_id);
1216+ image_buffers.at (key)->accumulate (pixel_id, L);
1217+ image_buffers.at (key + " _variance" )->accumulate (pixel_id, L * L);
1218+ }
1219+ }
11871220 };
11881221
11891222 Kernel2D finalize_var_kernel = [&]() noexcept {
@@ -1192,14 +1225,30 @@ void GradientPathTracingInstance::_render_one_camera(
11921225 auto n = static_cast <float >(spp);
11931226 auto mean_x = camera->film ()->read (pixel_id).average ; // mean(x) = 1/n * sum(x)
11941227 auto mean_x2 = image_buffers.at (" variance" )->read (pixel_id);// mean(x^2) = 1/n * sum(x^2)
1195- auto var = (mean_x2 - mean_x * mean_x) / (n - 1 .f ); // var(x) = 1/(n-1) * (sum(x^2) - n * mean(x)^2)
1228+ auto var = n * (mean_x2 - mean_x * mean_x) / (n - 1 .f ); // var(x) = 1/(n-1) * (sum(x^2) - n * mean(x)^2)
11961229 image_buffers.at (" variance" )->write (pixel_id, max (var, 0 .f ));
11971230 };
11981231
1232+ Kernel2D finalize_gradient_var_kernel = [&]() noexcept {
1233+ set_block_size (16u , 16u , 1u );
1234+ auto pixel_id = dispatch_id ().xy ();
1235+ if (!node<GradientPathTracing>()->central_radiance ()) {
1236+ for (int i = 0 ; i < 2 ; i++) {
1237+ string key = i == 0 ? " gradient_x" : " gradient_y" ;
1238+ auto mean_x = image_buffers.at (key)->read (pixel_id);
1239+ auto mean_x2 = image_buffers.at (key + " _variance" )->read (pixel_id);
1240+ auto n = static_cast <float >(spp);
1241+ auto var = n * (mean_x2 - mean_x * mean_x) / (n - 1 .f );
1242+ image_buffers.at (key + " _variance" )->write (pixel_id, max (var, 0 .f ));
1243+ }
1244+ }
1245+ };
1246+
11991247 Clock clock_compile;
12001248 auto render = pipeline ().device ().compile (render_kernel);
12011249 auto accumulate = pipeline ().device ().compile (accumulate_kernel);
12021250 auto finalize_var = pipeline ().device ().compile (finalize_var_kernel);
1251+ auto finalize_gradient_var = pipeline ().device ().compile (finalize_gradient_var_kernel);
12031252 auto integrator_shader_compilation_time = clock_compile.toc ();
12041253 LUISA_INFO (" Integrator shader compile in {} ms." , integrator_shader_compilation_time);
12051254 auto shutter_samples = camera->node ()->shutter_samples ();
@@ -1216,6 +1265,10 @@ void GradientPathTracingInstance::_render_one_camera(
12161265 pipeline ().update (command_buffer, s.point .time );
12171266 for (auto i = 0u ; i < s.spp ; i++) {
12181267 current_frame_buffer.clear (command_buffer);
1268+ if (!node<GradientPathTracing>()->central_radiance ()) {
1269+ image_buffers.at (" gradient_x_single_frame" )->clear (command_buffer);
1270+ image_buffers.at (" gradient_y_single_frame" )->clear (command_buffer);
1271+ }
12191272 command_buffer << render (sample_id++, s.point .time , s.point .weight )
12201273 .dispatch (resolution)
12211274 << accumulate ().dispatch (resolution);
@@ -1232,8 +1285,11 @@ void GradientPathTracingInstance::_render_one_camera(
12321285 auto parent_path = camera->node ()->file ().parent_path ();
12331286 auto filename = camera->node ()->file ().stem ().string ();
12341287 auto ext = camera->node ()->file ().extension ().string ();
1235- command_buffer << finalize_var ().dispatch (resolution)
1236- << synchronize ();
1288+ command_buffer << finalize_var ().dispatch (resolution);
1289+ if (!node<GradientPathTracing>()->central_radiance ()) {
1290+ command_buffer << finalize_gradient_var ().dispatch (resolution);
1291+ }
1292+ command_buffer << synchronize ();
12371293 for (auto &[key, buffer] : image_buffers) {
12381294 auto path = parent_path / fmt::format (" {}_{}{}" , filename, key, ext);
12391295 command_buffer << buffer->save (command_buffer, path, key == " effective" );
0 commit comments