[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