[Bf-blender-cvs] [4abb8a14a21] master: Cycles: make 3D texture sampling at boundaries more similar to GPU

Ethan-Hall noreply at git.blender.org
Mon Mar 21 16:43:40 CET 2022


Commit: 4abb8a14a2133f876c7718a8e09284baa62b1cf5
Author: Ethan-Hall
Date:   Thu Mar 17 16:01:39 2022 +0100
Branches: master
https://developer.blender.org/rB4abb8a14a2133f876c7718a8e09284baa62b1cf5

Cycles: make 3D texture sampling at boundaries more similar to GPU

CPU code for cubic interpolation with clip texture extension only performed
texture interpolation inside the range of [0,1]. As a result, even though the
volume's color is sampled using cubic interpolation, the boundary is not
being interpolated. The GPU appears was interpolating samples that span the
clip boundary softening the edge, which the CPU now does also.

This commit also includes refactoring of 2D and 3D texture sampling in
preparation of adding new extension modes.

Differential Revision: https://developer.blender.org/D14295

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

M	intern/cycles/kernel/device/cpu/image.h

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

diff --git a/intern/cycles/kernel/device/cpu/image.h b/intern/cycles/kernel/device/cpu/image.h
index b6bcd19b9ea..c0b6846e5b0 100644
--- a/intern/cycles/kernel/device/cpu/image.h
+++ b/intern/cycles/kernel/device/cpu/image.h
@@ -80,19 +80,115 @@ template<typename T> struct TextureInterpolator {
     return make_float4(r.x * f, r.y * f, r.z * f, r.w * f);
   }
 
+  /* Read 2D Texture Data
+   * Does not check if data request is in bounds. */
   static ccl_always_inline float4 read(const T *data, int x, int y, int width, int height)
   {
-    if (x < 0 || y < 0 || x >= width || y >= height) {
+    return read(data[y * width + x]);
+  }
+
+  /* Read 2D Texture Data Clip
+   * Returns transparent black if data request is out of bounds. */
+  static ccl_always_inline float4 read_clip(const T *data, int x, int y, int width, int height)
+  {
+    if (x < 0 || x >= width || y < 0 || y >= height) {
       return make_float4(0.0f, 0.0f, 0.0f, 0.0f);
     }
     return read(data[y * width + x]);
   }
 
+  /* Read 3D Texture Data
+   * Does not check if data request is in bounds. */
+  static ccl_always_inline float4
+  read(const T *data, int x, int y, int z, int width, int height, int depth)
+  {
+    return read(data[x + y * width + z * width * height]);
+  }
+
+  /* Read 3D Texture Data Clip
+   * Returns transparent black if data request is out of bounds. */
+  static ccl_always_inline float4
+  read_clip(const T *data, int x, int y, int z, int width, int height, int depth)
+  {
+    if (x < 0 || x >= width || y < 0 || y >= height || z < 0 || z >= depth) {
+      return make_float4(0.0f, 0.0f, 0.0f, 0.0f);
+    }
+    return read(data[x + y * width + z * width * height]);
+  }
+
+  /* Trilinear Interpolation */
+  static ccl_always_inline float4
+  trilinear_lookup(const T *data,
+                   float tx,
+                   float ty,
+                   float tz,
+                   int ix,
+                   int iy,
+                   int iz,
+                   int nix,
+                   int niy,
+                   int niz,
+                   int width,
+                   int height,
+                   int depth,
+                   float4 read(const T *, int, int, int, int, int, int))
+  {
+    float4 r;
+    r = (1.0f - tz) * (1.0f - ty) * (1.0f - tx) * read(data, ix, iy, iz, width, height, depth);
+    r += (1.0f - tz) * (1.0f - ty) * tx * read(data, nix, iy, iz, width, height, depth);
+    r += (1.0f - tz) * ty * (1.0f - tx) * read(data, ix, niy, iz, width, height, depth);
+    r += (1.0f - tz) * ty * tx * read(data, nix, niy, iz, width, height, depth);
+
+    r += tz * (1.0f - ty) * (1.0f - tx) * read(data, ix, iy, niz, width, height, depth);
+    r += tz * (1.0f - ty) * tx * read(data, nix, iy, niz, width, height, depth);
+    r += tz * ty * (1.0f - tx) * read(data, ix, niy, niz, width, height, depth);
+    r += tz * ty * tx * read(data, nix, niy, niz, width, height, depth);
+    return r;
+  }
+
+  /** Tricubic Interpolation */
+  static ccl_always_inline float4
+  tricubic_lookup(const T *data,
+                  float tx,
+                  float ty,
+                  float tz,
+                  const int xc[4],
+                  const int yc[4],
+                  const int zc[4],
+                  int width,
+                  int height,
+                  int depth,
+                  float4 read(const T *, int, int, int, int, int, int))
+  {
+    float u[4], v[4], w[4];
+
+    /* Some helper macros to keep code size reasonable.
+     * Lets the compiler inline all the matrix multiplications.
+     */
+#define DATA(x, y, z) (read(data, xc[x], yc[y], zc[z], width, height, depth))
+#define COL_TERM(col, row) \
+  (v[col] * (u[0] * DATA(0, col, row) + u[1] * DATA(1, col, row) + u[2] * DATA(2, col, row) + \
+             u[3] * DATA(3, col, row)))
+#define ROW_TERM(row) \
+  (w[row] * (COL_TERM(0, row) + COL_TERM(1, row) + COL_TERM(2, row) + COL_TERM(3, row)))
+
+    SET_CUBIC_SPLINE_WEIGHTS(u, tx);
+    SET_CUBIC_SPLINE_WEIGHTS(v, ty);
+    SET_CUBIC_SPLINE_WEIGHTS(w, tz);
+    /* Actual interpolation. */
+    return ROW_TERM(0) + ROW_TERM(1) + ROW_TERM(2) + ROW_TERM(3);
+
+#undef COL_TERM
+#undef ROW_TERM
+#undef DATA
+  }
+
   static ccl_always_inline int wrap_periodic(int x, int width)
   {
     x %= width;
-    if (x < 0)
+    if (x < 0) {
       x += width;
+    }
     return x;
   }
 
@@ -105,7 +201,6 @@ template<typename T> struct TextureInterpolator {
 
   static ccl_always_inline float4 interp_closest(const TextureInfo &info, float x, float y)
   {
-    const T *data = (const T *)info.data;
     const int width = info.width;
     const int height = info.height;
     int ix, iy;
@@ -117,10 +212,11 @@ template<typename T> struct TextureInterpolator {
         iy = wrap_periodic(iy, height);
         break;
       case EXTENSION_CLIP:
-        if (x < 0.0f || y < 0.0f || x > 1.0f || y > 1.0f) {
+        /* No samples are inside the clip region. */
+        if (ix < 0 || ix >= width || iy < 0 || iy >= height) {
           return make_float4(0.0f, 0.0f, 0.0f, 0.0f);
         }
-        ATTR_FALLTHROUGH;
+        break;
       case EXTENSION_EXTEND:
         ix = wrap_clamp(ix, width);
         iy = wrap_clamp(iy, height);
@@ -129,93 +225,121 @@ template<typename T> struct TextureInterpolator {
         kernel_assert(0);
         return make_float4(0.0f, 0.0f, 0.0f, 0.0f);
     }
-    return read(data[ix + iy * width]);
+
+    const T *data = (const T *)info.data;
+    return read((const T *)data, ix, iy, width, height);
   }
 
   static ccl_always_inline float4 interp_linear(const TextureInfo &info, float x, float y)
   {
-    const T *data = (const T *)info.data;
     const int width = info.width;
     const int height = info.height;
-    int ix, iy, nix, niy;
+
+    /* A -0.5 offset is used to center the linear samples around the sample point. */
+    int ix, iy;
+    int nix, niy;
     const float tx = frac(x * (float)width - 0.5f, &ix);
     const float ty = frac(y * (float)height - 0.5f, &iy);
+
     switch (info.extension) {
       case EXTENSION_REPEAT:
         ix = wrap_periodic(ix, width);
-        iy = wrap_periodic(iy, height);
         nix = wrap_periodic(ix + 1, width);
+
+        iy = wrap_periodic(iy, height);
         niy = wrap_periodic(iy + 1, height);
         break;
       case EXTENSION_CLIP:
+        /* No linear samples are inside the clip region. */
+        if (ix < -1 || ix >= width || iy < -1 || iy >= height) {
+          return make_float4(0.0f, 0.0f, 0.0f, 0.0f);
+        }
         nix = ix + 1;
         niy = iy + 1;
         break;
       case EXTENSION_EXTEND:
         nix = wrap_clamp(ix + 1, width);
-        niy = wrap_clamp(iy + 1, height);
         ix = wrap_clamp(ix, width);
+        niy = wrap_clamp(iy + 1, height);
         iy = wrap_clamp(iy, height);
         break;
       default:
         kernel_assert(0);
         return make_float4(0.0f, 0.0f, 0.0f, 0.0f);
     }
-    return (1.0f - ty) * (1.0f - tx) * read(data, ix, iy, width, height) +
-           (1.0f - ty) * tx * read(data, nix, iy, width, height) +
-           ty * (1.0f - tx) * read(data, ix, niy, width, height) +
-           ty * tx * read(data, nix, niy, width, height);
+
+    const T *data = (const T *)info.data;
+    return (1.0f - ty) * (1.0f - tx) * read_clip(data, ix, iy, width, height) +
+           (1.0f - ty) * tx * read_clip(data, nix, iy, width, height) +
+           ty * (1.0f - tx) * read_clip(data, ix, niy, width, height) +
+           ty * tx * read_clip(data, nix, niy, width, height);
   }
 
   static ccl_always_inline float4 interp_cubic(const TextureInfo &info, float x, float y)
   {
-    const T *data = (const T *)info.data;
     const int width = info.width;
     const int height = info.height;
-    int ix, iy, nix, niy;
+
+    /* A -0.5 offset is used to center the cubic samples around the sample point. */
+    int ix, iy;
     const float tx = frac(x * (float)width - 0.5f, &ix);
     const float ty = frac(y * (float)height - 0.5f, &iy);
-    int pix, piy, nnix, nniy;
+
+    int pix, piy;
+    int nix, niy;
+    int nnix, nniy;
+
     switch (info.extension) {
       case EXTENSION_REPEAT:
         ix = wrap_periodic(ix, width);
-        iy = wrap_periodic(iy, height);
         pix = wrap_periodic(ix - 1, width);
-        piy = wrap_periodic(iy - 1, height);
         nix = wrap_periodic(ix + 1, width);
-        niy = wrap_periodic(iy + 1, height);
         nnix = wrap_periodic(ix + 2, width);
+
+        iy = wrap_periodic(iy, height);
+        piy = wrap_periodic(iy - 1, height);
+        niy = wrap_periodic(iy + 1, height);
         nniy = wrap_periodic(iy + 2, height);
         break;
       case EXTENSION_CLIP:
+        /* No cubic samples are inside the clip region. */
+        if (ix < -2 || ix > width || iy < -2 || iy > height) {
+          return make_float4(0.0f, 0.0f, 0.0f, 0.0f);
+        }
+
         pix = ix - 1;
-        piy = iy - 1;
         nix = ix + 1;
-        niy = iy + 1;
         nnix = ix + 2;
+
+        piy = iy - 1;
+        niy = iy + 1;
         nniy = iy + 2;
         break;
       case EXTENSION_EXTEND:
         pix = wrap_clamp(ix - 1, width);
-        piy = wrap_clamp(iy - 1, height);
         nix = wrap_clamp(ix + 1, width);
-        niy = wrap_clamp(iy + 1, height);
         nnix = wrap_clamp(ix + 2, width);
-        nniy = wrap_clamp(iy + 2, height);
         ix = wrap_clamp(ix, width);
+
+        piy = wrap_clamp(iy - 1, height);
+        niy = wrap_clamp(iy + 1, height);
+        nniy = wrap_clamp(iy + 2, height);
         iy = wrap_clamp(iy, height);
         break;
       default:
         kernel_assert(0);
         return make_float4(0.0f, 0.0f, 0.0f, 0.0f);
     }
+
+    const T *data = (const T *)info.data;
     const int xc[4] = {pix, ix, nix, nnix};
     const int yc[4] = {piy, iy, niy, nniy};
     float u[4], v[4];
-    /* Some helper macro to keep code reasonable size,
-     * let compiler to inline all the matrix multiplications.
+
+    /* Some helper macros to keep code size reasonable.
+     * Lets the compiler inline all the matrix multiplications.
      */
-#define DATA(x, y) (read(data, xc[x], yc[y], width, height))
+#define DATA(x, y) (read_cl

@@ Diff output truncated at 10240 characters. @@



More information about the Bf-blender-cvs mailing list