1

My task is to produce the plot of a 2-dimensional function in real time using nothing but linear algebra and color (imagine having to compute an image buffer in plain C++ from a function definition, for example f(x,y) = x^2 + y^2). The output should be something like this 3d plot. So far I have tried 3 approaches:

1: Ray tracing:

Divide the (x,y) plane into triangles, find the z-values of each vertex, thus divide the plot into triangles. Intersect each ray with the triangles.

2: Sphere tracing:

a method for rendering implicit surfaces described here.

3: Rasterization:

The inverse of (1). Split the plot into triangles, project them onto the camera plane, loop over the pixels of the canvas and for each one choose the "closest" projected pixel.

All of these are way to slow. Part of my assignment is moving around the camera, so the plot has to be re-rendered in each frame. Please point me towards another source of information/another algorithm/any kind of help. Thank you.

EDIT

As pointed out, here is the pseudocode for my very basic rasterizer. I am aware that this code might not be flawless, but it should resemble the general idea. However, when splitting my plot into 200 triangles (which I do not expect to be enough) it already runs very slowly, even without rendering anything. I am not even using a depth buffer for visibility. I just wanted to test the speed by setting up a frame buffer as follows:

NOTE: In the JavaScript framework I am using, _ denotes array indexing and a..b composes a list from a to b.

/*
 * Raster setup.
 * The raster is a pxH x pxW array.
 * Raster coordinates might be negative or larger than the array dimensions.
 * When rendering (i.e. filling the array) positions outside the visible raster will not be filled (i.e. colored).
 */

pxW := Width of the screen in pixels.
pxH := Height of the screen in pixels.
T := Transformation matrix of homogeneous world points to raster space.

// Buffer setup.
colBuffer = apply(1..pxW, apply(1..pxH, 0)); // pxH x pxW array of black pixels.

// Positive/0 if the point is on the right side of the line (V1,V2)/exactly on the line.
// p2D := point to test.
// V1, V2 := two vertices of the triangle.
edgeFunction(p2D, V1, V2) := (
  det([p2D-V1, V2-V1]);
);

fillBuffer(V0, V1, V2) := (
  // Dehomogenize.
  hV0 = V0/(V0_3);
  hV1 = V1/(V1_3);
  hV2 = V2/(V2_3);
  // Find boundaries of the triangle in raster space.
  xMin = min(hV0.x, hV1.x, hV2.x);
  xMax = max(hV0.x, hV1.x, hV2.x);
  yMin = min(hV0.y, hV1.y, hV2.y);
  yMax = max(hV0.y, hV1.y, hV2.y);
  xMin = floor(if(xMin >= 0, xMin, 0));
  xMax = ceil(if(xMax < pxW, xMax, pxW));
  yMin = floor(if(yMin >= 0, yMin, 0));
  yMax = ceil(if(yMax < pxH, yMax, pxH));
  // Check for all points "close to" the triangle in raster space whether they lie inside it.
  forall(xMin..xMax, x, forall(yMin..yMax, y, (
    p2D = (x,y);
    i = edgeFunction(p2D, hV0.xy, hV1.xy) * edgeFunction(p2D, hV1.xy, hV2.xy) * edgeFunction(p2D, hV2.xy, hV0.xy);
    if (i > 0, colBuffer_y_x = 1); // Fill all points inside the triangle with some placeholder.
  )));
);

mapTrianglesToScreen() := (
  tvRaster = homogVerts * T; // Triangle vertices in raster space.
  forall(1..(length(tvRaster)/3), i, (
    actualI = i / 3 + 1;
    fillBuffer(tvRaster_actualI, tvRaster_(actualI + 1), tvRaster_(actualI + 2));
  ));
);

// After all this, render the colBuffer.

What is wrong about this approach? Why is it so slow?

Thank you.

Spektre
  • 49,595
  • 11
  • 110
  • 380
Gerry
  • 1,938
  • 3
  • 18
  • 25

1 Answers1

4

I would go with #3 it is really not that complex so you should obtain > 20 fps on standard machine with pure SW rasterizer (without any libs) if coded properly. My bet is you are using some slow API like PutPixel or SetPixel or doing some crazy thing. Without seeing code or better description of how you do it is hard to elaborate. All the info you need to do this is in here:

Do look also in the sub-links in each ...

Community
  • 1
  • 1
Spektre
  • 49,595
  • 11
  • 110
  • 380
  • Thank you very much for your answer. I am familiar with projective geometry. I have tried to post a clearer description of my implementation. – Gerry Mar 26 '17 at 15:05
  • @Gerry JAVAScript is not my cup of tea but from quick look at your code your triangle code looks too slow. You are doing too much computations per each pixel (even for not rendered) because it is based on implicit triangle equations. So my bet is that is your main bottleneck. Compare the code from the linked answer of mine. If you do not know math behind the convex polygon filling see the sub-link in there ... – Spektre Mar 26 '17 at 20:29
  • @Gerry To be more specific if I see it right you are doing: determinant computation + some vector math and conditions + function sub calls for each pixel of bbox of each rendered triangle. That is crazy unless you are on dedicated HW that can parallelize it heavily Which is not the case on SW render. For comparison mine approach does only 3x DDA per each triangle + the pixel filling by set of horizontal lines – Spektre Mar 26 '17 at 20:40
  • I have not yet managed to solve the whole problem, but your approach seems significantly faster. Thank you. – Gerry Mar 30 '17 at 13:30