[Bf-blender-cvs] [6e74a8b69f2] master: Fix T78881: Cycles OpenImageDenoise not using albedo and normal correctly

Brecht Van Lommel noreply at git.blender.org
Mon Jul 13 19:39:13 CEST 2020


Commit: 6e74a8b69f215e63e136cb4c497e738371ac798f
Author: Brecht Van Lommel
Date:   Mon Jul 13 16:45:15 2020 +0200
Branches: master
https://developer.blender.org/rB6e74a8b69f215e63e136cb4c497e738371ac798f

Fix T78881: Cycles OpenImageDenoise not using albedo and normal correctly

Properly normalize buffers now. Also expose option to not use albedo and normal
just like OptiX.

===================================================================

M	intern/cycles/blender/addon/properties.py
M	intern/cycles/blender/addon/ui.py
M	intern/cycles/blender/blender_sync.cpp
M	intern/cycles/device/device_cpu.cpp
M	intern/cycles/device/device_optix.cpp
M	intern/cycles/device/device_task.h

===================================================================

diff --git a/intern/cycles/blender/addon/properties.py b/intern/cycles/blender/addon/properties.py
index 6d28c14e12a..45d25720aff 100644
--- a/intern/cycles/blender/addon/properties.py
+++ b/intern/cycles/blender/addon/properties.py
@@ -212,7 +212,7 @@ def enum_denoiser(self, context):
     items += enum_openimagedenoise_denoiser(self, context)
     return items
 
-enum_denoising_optix_input_passes = (
+enum_denoising_input_passes = (
     ('RGB', "Color", "Use only color as input", 1),
     ('RGB_ALBEDO', "Color + Albedo", "Use color and albedo data as input", 2),
     ('RGB_ALBEDO_NORMAL', "Color + Albedo + Normal", "Use color, albedo and normal data as input", 3),
@@ -1451,11 +1451,18 @@ class CyclesRenderLayerSettings(bpy.types.PropertyGroup):
 
     denoising_optix_input_passes: EnumProperty(
         name="Input Passes",
-        description="Passes handed over to the OptiX denoiser (this can have different effects on the denoised image)",
-        items=enum_denoising_optix_input_passes,
+        description="Passes used by the denoiser to distinguish noise from shader and geometry detail",
+        items=enum_denoising_input_passes,
         default='RGB_ALBEDO',
     )
 
+    denoising_openimagedenoise_input_passes: EnumProperty(
+        name="Input Passes",
+        description="Passes used by the denoiser to distinguish noise from shader and geometry detail",
+        items=enum_denoising_input_passes,
+        default='RGB_ALBEDO_NORMAL',
+    )
+
     use_pass_crypto_object: BoolProperty(
         name="Cryptomatte Object",
         description="Render cryptomatte object pass, for isolating objects in compositing",
diff --git a/intern/cycles/blender/addon/ui.py b/intern/cycles/blender/addon/ui.py
index 3d0b2f721b4..03b1675c309 100644
--- a/intern/cycles/blender/addon/ui.py
+++ b/intern/cycles/blender/addon/ui.py
@@ -1008,6 +1008,7 @@ class CYCLES_RENDER_PT_denoising(CyclesButtonsPanel, Panel):
             col.prop(cycles_view_layer, "denoising_optix_input_passes")
             return
         elif denoiser == 'OPENIMAGEDENOISE':
+            col.prop(cycles_view_layer, "denoising_openimagedenoise_input_passes")
             return
 
         col.prop(cycles_view_layer, "denoising_radius", text="Radius")
diff --git a/intern/cycles/blender/blender_sync.cpp b/intern/cycles/blender/blender_sync.cpp
index ee2445e44c4..d509f2fc786 100644
--- a/intern/cycles/blender/blender_sync.cpp
+++ b/intern/cycles/blender/blender_sync.cpp
@@ -954,7 +954,13 @@ DenoiseParams BlenderSync::get_denoise_params(BL::Scene &b_scene,
       denoising.strength = get_float(clayer, "denoising_strength");
       denoising.feature_strength = get_float(clayer, "denoising_feature_strength");
       denoising.relative_pca = get_boolean(clayer, "denoising_relative_pca");
-      denoising.optix_input_passes = get_enum(clayer, "denoising_optix_input_passes");
+
+      denoising.input_passes = (DenoiserInput)get_enum(
+          clayer,
+          (denoising.type == DENOISER_OPTIX) ? "denoising_optix_input_passes" :
+                                               "denoising_openimagedenoise_input_passes",
+          DENOISER_INPUT_NUM,
+          DENOISER_INPUT_RGB_ALBEDO_NORMAL);
 
       denoising.store_passes = get_boolean(clayer, "denoising_store_passes");
     }
diff --git a/intern/cycles/device/device_cpu.cpp b/intern/cycles/device/device_cpu.cpp
index af8db5b75b8..76d4d7945f7 100644
--- a/intern/cycles/device/device_cpu.cpp
+++ b/intern/cycles/device/device_cpu.cpp
@@ -951,12 +951,13 @@ class CPUDevice : public Device {
 
   void denoise_openimagedenoise_buffer(DeviceTask &task,
                                        float *buffer,
-                                       size_t offset,
-                                       size_t stride,
-                                       size_t x,
-                                       size_t y,
-                                       size_t w,
-                                       size_t h)
+                                       const size_t offset,
+                                       const size_t stride,
+                                       const size_t x,
+                                       const size_t y,
+                                       const size_t w,
+                                       const size_t h,
+                                       const float scale)
   {
 #ifdef WITH_OPENIMAGEDENOISE
     assert(openimagedenoise_supported());
@@ -982,31 +983,65 @@ class CPUDevice : public Device {
     }
 
     /* Set images with appropriate stride for our interleaved pass storage. */
-    const struct {
+    struct {
       const char *name;
-      int offset;
-    } passes[] = {{"color", task.pass_denoising_data + DENOISING_PASS_COLOR},
-                  {"normal", task.pass_denoising_data + DENOISING_PASS_NORMAL},
-                  {"albedo", task.pass_denoising_data + DENOISING_PASS_ALBEDO},
-                  {"output", 0},
+      const int offset;
+      const bool scale;
+      const bool use;
+      array<float> scaled_buffer;
+    } passes[] = {{"color", task.pass_denoising_data + DENOISING_PASS_COLOR, false, true},
+                  {"albedo",
+                   task.pass_denoising_data + DENOISING_PASS_ALBEDO,
+                   true,
+                   task.denoising.input_passes >= DENOISER_INPUT_RGB_ALBEDO},
+                  {"normal",
+                   task.pass_denoising_data + DENOISING_PASS_NORMAL,
+                   true,
+                   task.denoising.input_passes >= DENOISER_INPUT_RGB_ALBEDO_NORMAL},
+                  {"output", 0, false, true},
                   { NULL,
                     0 }};
 
     for (int i = 0; passes[i].name; i++) {
+      if (!passes[i].use) {
+        continue;
+      }
+
       const int64_t pixel_offset = offset + x + y * stride;
-      const int64_t buffer_offset = (pixel_offset * task.pass_stride + passes[i].offset) *
-                                    sizeof(float);
-      const int64_t pixel_stride = task.pass_stride * sizeof(float);
+      const int64_t buffer_offset = (pixel_offset * task.pass_stride + passes[i].offset);
+      const int64_t pixel_stride = task.pass_stride;
       const int64_t row_stride = stride * pixel_stride;
 
-      oidn_filter.setImage(passes[i].name,
-                           (char *)buffer + buffer_offset,
-                           oidn::Format::Float3,
-                           w,
-                           h,
-                           0,
-                           pixel_stride,
-                           row_stride);
+      if (passes[i].scale && scale != 1.0f) {
+        /* Normalize albedo and normal passes as they are scaled by the number of samples.
+         * For the color passes OIDN will perform autoexposure making it unnecessary. */
+        array<float> &scaled_buffer = passes[i].scaled_buffer;
+        scaled_buffer.resize(w * h * 3);
+
+        for (int y = 0; y < h; y++) {
+          const float *pass_row = buffer + buffer_offset + y * row_stride;
+          float *scaled_row = scaled_buffer.data() + y * w * 3;
+
+          for (int x = 0; x < w; x++) {
+            scaled_row[x * 3 + 0] = pass_row[x * pixel_stride + 0] * scale;
+            scaled_row[x * 3 + 1] = pass_row[x * pixel_stride + 1] * scale;
+            scaled_row[x * 3 + 2] = pass_row[x * pixel_stride + 2] * scale;
+          }
+        }
+
+        oidn_filter.setImage(
+            passes[i].name, scaled_buffer.data(), oidn::Format::Float3, w, h, 0, 0, 0);
+      }
+      else {
+        oidn_filter.setImage(passes[i].name,
+                             buffer + buffer_offset,
+                             oidn::Format::Float3,
+                             w,
+                             h,
+                             0,
+                             pixel_stride * sizeof(float),
+                             row_stride * sizeof(float));
+      }
     }
 
     /* Execute filter. */
@@ -1021,6 +1056,7 @@ class CPUDevice : public Device {
     (void)y;
     (void)w;
     (void)h;
+    (void)scale;
 #endif
   }
 
@@ -1037,7 +1073,8 @@ class CPUDevice : public Device {
                                       rtile.x,
                                       rtile.y,
                                       rtile.w,
-                                      rtile.h);
+                                      rtile.h,
+                                      1.0f / rtile.sample);
 
       /* todo: it may be possible to avoid this copy, but we have to ensure that
        * when other code copies data from the device it doesn't overwrite the
@@ -1047,6 +1084,9 @@ class CPUDevice : public Device {
     else {
       /* Per-tile denoising. */
       rtile.sample = rtile.start_sample + rtile.num_samples;
+      const float scale = 1.0f / rtile.sample;
+      const float invscale = rtile.sample;
+      const size_t pass_stride = task.pass_stride;
 
       /* Map neighboring tiles into one buffer for denoising. */
       RenderTileNeighbors neighbors(rtile);
@@ -1075,22 +1115,24 @@ class CPUDevice : public Device {
         const int ymax = min(ntile.y + ntile.h, rect.w);
 
         const size_t tile_offset = ntile.offset + xmin + ymin * ntile.stride;
-        const float *tile_buffer = (float *)ntile.buffer + tile_offset * task.pass_stride;
+        const float *tile_buffer = (float *)ntile.buffer + tile_offset * pass_stride;
 
         const size_t merged_stride = rect_size.x;
         const size_t merged_offset = (xmin - rect.x) + (ymin - rect.y) * merged_stride;
-        float *merged_buffer = merged.data() + merged_offset * task.pass_stride;
+        float *merged_buffer = merged.data() + merged_offset * pass_stride;
 
         for (int y = ymin; y < ymax; y++) {
-          memcpy(merged_buffer, tile_buffer, sizeof(float) * task.pass_stride * (xmax - xmin));
-          tile_buffer += ntile.stride * task.pass_stride;
-          merged_buffer += merged_stride * task.pass_stride;
+          for (int x = 0; x < pass_stride * (xmax - xmin); x++) {
+            merged_buffer[x] = tile_buffer[x] * scale;
+          }
+          tile_buffer += ntile.stride * pass_stride;
+          merged_buffer += merged_stride * pass_stride;

@@ Diff output truncated at 10240 characters. @@



More information about the Bf-blender-cvs mailing list