[Bf-blender-cvs] [7a5cde30749] temp-image-buffer-rasterizer: Added documentation.

Jeroen Bakker noreply at git.blender.org
Fri Feb 18 12:36:20 CET 2022


Commit: 7a5cde30749e1bbaa587607f5d9fc5ed97c59ffe
Author: Jeroen Bakker
Date:   Fri Feb 18 12:36:07 2022 +0100
Branches: temp-image-buffer-rasterizer
https://developer.blender.org/rB7a5cde30749e1bbaa587607f5d9fc5ed97c59ffe

Added documentation.

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

M	source/blender/imbuf/IMB_rasterizer.hh
M	source/blender/imbuf/intern/rasterizer_test.cc

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

diff --git a/source/blender/imbuf/IMB_rasterizer.hh b/source/blender/imbuf/IMB_rasterizer.hh
index 5531b1e9965..a137631ef9f 100644
--- a/source/blender/imbuf/IMB_rasterizer.hh
+++ b/source/blender/imbuf/IMB_rasterizer.hh
@@ -5,6 +5,60 @@
  * \ingroup imbuf
  *
  * Rasterizer to render triangles onto an image buffer.
+ *
+ * The implementation is template based and follows a (very limited) OpenGL pipeline.
+ *
+ * ## Basic usage
+ *
+ * In order to use it you have to define the data structure for a single vertex.
+ *
+ * \code{.cc}
+ * struct VertexInput {
+ *  float2 uv;
+ * };
+ * \endcode
+ *
+ * A vertex shader is required to transfer the vertices to actual coordinates in the image buffer.
+ * The vertex shader will store vertex specific data in a VertexOutInterface.
+ *
+ * \code{.cc}
+ * class MyVertexShader : public AbstractVertexShader<VertexInput, float> {
+ *   public:
+ *     float4x4 mat;
+ *     void vertex(const VertexInputType &input, VertexOutputType *r_output) override
+ *     {
+ *       float2 coord = float2(mat * float3(input.uv[0], input.uv[1], 0.0));
+ *       r_output->coord = coord * image_size;
+ *       r_output->data = 1.0;
+ *     }
+ * };
+ * \endcode
+ *
+ * A fragment shader is required to actually generate the pixel that will be stored in the buffer.
+ *
+ * \code{.cc}
+ * class FragmentShader : public AbstractFragmentShader<float, float4> {
+ *   public:
+ *     void fragment(const FragmentInputType &input, FragmentOutputType *r_output) override
+ *     {
+ *       *r_output = float4(input, input, input, 1.0);
+ *     }
+ * };
+ * \endcode
+ *
+ * Create a rasterizer with the vertex and fragment shader and start drawing.
+ * It is required to call flush to make sure that all triangles are drawn to the image buffer.
+ *
+ * \code{.cc}
+ * Rasterizer<MyVertexShader, MyFragmentShader> rasterizer(&image_buffer);
+ * rasterizer.get_vertex_shader().mat = float4x4::identity();
+ * rasterizer.draw_triangle(
+ *   VertexInput{float2(0.0, 1.0)},
+ *   VertexInput{float2(1.0, 1.0)},
+ *   VertexInput{float2(1.0, 0.0)},
+ * );
+ * rasterizer.flush();
+ * \endcode
  */
 
 #pragma once
@@ -20,16 +74,27 @@
 
 #include <optional>
 
+//#define DEBUG_PRINT
+
 namespace blender::imbuf::rasterizer {
 
+/** The default number of rasterlines to buffer before flushing to the image buffer. */
 constexpr int64_t DefaultRasterlinesBufferSize = 4096;
 
 /**
  * Interface data of the vertex stage.
  */
-template<typename Data> class VertexOutInterface {
+template<
+    /**
+     * Data type per vertex generated by the vertex shader and transferred to the fragment shader.
+     *
+     * The data type should implement the +=, =, -, / and * operator.
+     */
+    typename Data>
+class VertexOutInterface {
  public:
   using Self = VertexOutInterface<Data>;
+  /** Coordinate of a vertex inside the image buffer. (0..image_buffer.x, 0..image_buffer.y). */
   float2 coord;
   Data data;
 
@@ -256,16 +321,27 @@ class Rasterizer {
  private:
   void rasterize_triangle(std::array<VertexOutputType, 3> &vertex_out)
   {
+#ifdef DEBUG_PRINT
+    printf("%s 1: (%.4f,%.4f) 2: (%.4f,%.4f) 3: (%.4f %.4f)\n",
+           __func__,
+           vertex_out[0].coord[0],
+           vertex_out[0].coord[1],
+           vertex_out[1].coord[0],
+           vertex_out[1].coord[1],
+           vertex_out[2].coord[0],
+           vertex_out[2].coord[1]);
+#endif
     std::array<VertexOutputType *, 3> sorted_vertices = order_triangle_vertices(vertex_out);
 
-    /* left and right branch. */
-    VertexOutputType left = *sorted_vertices[0];
-    VertexOutputType right = *sorted_vertices[0];
-
+    // TODO: add y coordinate clamping for smoother result.
     const int min_v = sorted_vertices[0]->coord[1];
     const int mid_v = sorted_vertices[1]->coord[1];
     const int max_v = sorted_vertices[2]->coord[1];
 
+    /* left and right branch. */
+    VertexOutputType left = *sorted_vertices[0];
+    VertexOutputType right = *sorted_vertices[0];
+
     VertexOutputType *left_target;
     VertexOutputType *right_target;
     if (sorted_vertices[1]->coord[0] < sorted_vertices[2]->coord[0]) {
@@ -285,6 +361,7 @@ class Rasterizer {
       std::swap(left_add, right_add);
     }
 
+    /* Add rasterlines from min_v to mid_v. */
     int v;
     for (v = min_v; v < mid_v; v++) {
       if (v >= 0 && v < image_buffer_->y) {
@@ -298,11 +375,24 @@ class Rasterizer {
       right += right_add;
     }
 
+    /* When both are the same we should the left/right branches are the same. */
+    if (min_v == mid_v) {
+      /* Use the x coordinate to identify which branch should be modified. */
+      if (sorted_vertices[0]->coord[0] > sorted_vertices[1]->coord[0]) {
+        left = *sorted_vertices[1];
+      }
+      else {
+        right = *sorted_vertices[1];
+      }
+    }
+
+    /* Update the branch adders. */
     left_target = sorted_vertices[2];
     right_target = sorted_vertices[2];
     left_add = calc_vertex_output_data(left, *left_target);
     right_add = calc_vertex_output_data(right, *right_target);
 
+    /* Add rasterlines from mid_v to max_v. */
     for (; v < max_v; v++) {
       if (v >= 0 && v < image_buffer_->y) {
         std::optional<RasterlineType> rasterline = clamped_rasterline(
@@ -319,7 +409,10 @@ class Rasterizer {
   VertexOutputType calc_vertex_output_data(const VertexOutputType &from,
                                            const VertexOutputType &to)
   {
-    return (to - from) / (to.coord[1] - from.coord[1]);
+    // TODO: this should be done without casting to int, but that needs a better pixel clamping
+    // strategic.
+    const int num_rasterlines = (((int)to.coord[1]) - ((int)from.coord[1]));
+    return (to - from) / max_ii(1, num_rasterlines);
   }
 
   std::array<VertexOutputType *, 3> order_triangle_vertices(
diff --git a/source/blender/imbuf/intern/rasterizer_test.cc b/source/blender/imbuf/intern/rasterizer_test.cc
index d0c45eb908f..3cd9aa11b1a 100644
--- a/source/blender/imbuf/intern/rasterizer_test.cc
+++ b/source/blender/imbuf/intern/rasterizer_test.cc
@@ -9,33 +9,34 @@
 
 namespace blender::imbuf::rasterizer::tests {
 
-const uint32_t IMBUF_SIZE = 128;
+const uint32_t IMBUF_SIZE = 256;
 
 struct VertexInput {
   float2 uv;
+  float value;
 
-  VertexInput(float2 uv) : uv(uv)
+  VertexInput(float2 uv, float value) : uv(uv), value(value)
   {
   }
 };
 
-class VertexShader : public AbstractVertexShader<VertexInput, float> {
+class VertexShader : public AbstractVertexShader<VertexInput, float4> {
  public:
   float2 image_size;
   float4x4 vp_mat;
   void vertex(const VertexInputType &input, VertexOutputType *r_output) override
   {
-    float3 t = float3(input.uv[0], input.uv[1], 0.0);
-    r_output->coord = float2(vp_mat * t) * image_size;
-    r_output->data = 1.0f;
+    float2 coord = float2(vp_mat * float3(input.uv[0], input.uv[1], 0.0));
+    r_output->coord = coord * image_size;
+    r_output->data = float4(coord[0], coord[1], input.value, 1.0);
   }
 };
 
-class FragmentShader : public AbstractFragmentShader<float, float4> {
+class FragmentShader : public AbstractFragmentShader<float4, float4> {
  public:
   void fragment(const FragmentInputType &input, FragmentOutputType *r_output) override
   {
-    *r_output = float4(input, input, input, 1.0);
+    *r_output = input;
   }
 };
 
@@ -66,19 +67,14 @@ TEST(imbuf_rasterizer, draw_triangle)
 
   for (int i = 0; i < 1000; i++) {
     BLI_path_sequence_encode(file_name, "/tmp/test_", ".png", 4, i);
-    printf("%s: %s\n", __func__, file_name);
-
-    if (i == 43) {
-      printf("break\n");
-    }
 
     IMB_rectfill(&image_buffer, clear_color);
     rotation[2] = (i / 1000.0) * M_PI * 2;
 
     vertex_shader.vp_mat = float4x4::from_loc_eul_scale(location, rotation, scale);
-    rasterizer.draw_triangle(VertexInput(float2(-0.4, -0.4)),
-                             VertexInput(float2(0.0, 0.4)),
-                             VertexInput(float2(0.3, 0.0)));
+    rasterizer.draw_triangle(VertexInput(float2(-0.4, -0.4), 0.2),
+                             VertexInput(float2(0.0, 0.4), 0.5),
+                             VertexInput(float2(0.3, 0.0), 1.0));
     rasterizer.flush();
 
     IMB_saveiff(&image_buffer, file_name, IB_rectfloat);



More information about the Bf-blender-cvs mailing list