Ray Tracer implementation


#41

if you use no reference in the for loop you should get an object you can also write to


#42

where do you mean?
If i change this:

for (auto &pixel: line.getPixels()) {

to this

for (auto pixel: line.getPixels()) {

I get an error:


#43

can you post the full function or at least all the for loops


#44

Sure. The whole class is here https://github.com/edap/ofxRayTracer/blob/master/src/ofxRayTracer.cpp

The method that loops through the rectangle and cast the ray is:

void ofxRayTracer::traceImage(const ofxRTPinholeCamera& camera, ofRectangle& rectangle, shared_ptr<ofImage>& image){
    const int width = int(rectangle.getWidth());
    const int height = int(rectangle.getHeight());

    auto startAtTime = ofGetElapsedTimeMillis();
    ofPixels_ <unsigned char>pixels = image->getPixels();

    parallel_for( tbb::blocked_range<size_t>(0,pixels.getHeight()),
                 [=](const tbb::blocked_range<size_t>& r) {
                     for(auto line: pixels.getConstLines(r.begin(), r.end() - r.begin())) {
                         auto y = line.getLineNum();
                         auto x = 0;
                         for (auto &pixel: line.getPixels()) {
                             glm::vec3 P;
                             glm::vec3 w;

                             // Find the ray through (x, y) and the center of projection
                             camera.getPrimaryRay(float(x) + 0.5f, float(y) + 0.5f, width, height, P, w);
                             auto color = L_i(ofxRTRay(P, w));
                             //renderPixels.setColor(x,y,color);
                             pixel[0] = color.r;
                             pixel[1] = color.g;
                             pixel[2] = color.b;
                             pixel[3] = color.a;
                             x++;
                         }
                     }
                }
    );

    image->update();
    displayTime(ofGetElapsedTimeMillis() - startAtTime);
}


#45

i think the problem is here:

for(auto line: pixels.getConstLines(r.begin(), r.end() - r.begin())) {

getConstLines returns const lines so you can’t modify anything from there on. Try using getLines instead


#46

I can’t, the method getLines is not available. The error message says: No matching member function for call to getLines
In the post Ray Tracer implementation there is a screenshot that explains the problem and why I went for getConstLines


#47

ah, can you try using:

[&](const tbb::blocked_range<size_t>& r) {

instead of

[=](const tbb::blocked_range<size_t>& r) {

note the & instead of = which means use the context of the lambda function by reference instead of by copy


#48

This fixed the const lines problem, but still, i can’t set the new value of the pixel


#49

And if I try to use another ofPixel object (renderPixel) instead that one where I’m iterating, the program crash with the errors in the post Ray Tracer implementation

    ofPixels renderPixels;
    renderPixels.allocate(width, height, OF_IMAGE_COLOR_ALPHA);
    ofPixels_ <unsigned char>pixels = image->getPixels();

    parallel_for( tbb::blocked_range<size_t>(0,pixels.getHeight()),
                 [&](const tbb::blocked_range<size_t>& r) {
                     for(auto line: pixels.getLines(r.begin(), r.end() - r.begin())) {
                         auto y = line.getLineNum();
                         auto x = 0;
                         for (auto &pixel: line.getPixels()) {
                             glm::vec3 P;
                             glm::vec3 w;

                             // Find the ray through (x, y) and the center of projection
                             camera.getPrimaryRay(float(x) + 0.5f, float(y) + 0.5f, width, height, P, w);
                             auto color = L_i(ofxRTRay(P, w));
                             renderPixels.setColor(x,y,color);
                             //pixel[1] = color.g;
                             //pixel[2] = color.b;
                             //pixel[3] = color.a;
                             x++;
                         }
                     }
                }
    );

    //image->update();
    image->setFromPixels(renderPixels);
    displayTime(ofGetElapsedTimeMillis() - startAtTime);

#50

And, If I use setColor instead pixel[index], like:

    ofPixels_ <unsigned char>pixels = image->getPixels();

    parallel_for( tbb::blocked_range<size_t>(0,pixels.getHeight()),
                 [&](const tbb::blocked_range<size_t>& r) {
                     for(auto line: pixels.getLines(r.begin(), r.end() - r.begin())) {
                         auto y = line.getLineNum();
                         auto x = 0;
                         for (auto &pixel: line.getPixels()) {
                             glm::vec3 P;
                             glm::vec3 w;

                             // Find the ray through (x, y) and the center of projection
                             camera.getPrimaryRay(float(x) + 0.5f, float(y) + 0.5f, width, height, P, w);
                             auto color = L_i(ofxRTRay(P, w));
                             pixels.setColor(x, y, color);
                             //pixel[1] = color.g;
                             //pixel[2] = color.b;
                             //pixel[3] = color.a;
                             x++;
                         }
                     }
                }
    );
    image->update();
    displayTime(ofGetElapsedTimeMillis() - startAtTime);

The error is

So, I do not think that is fine to access the ofPixel object by reference in the loop.


#51

have you tried

 for (auto pixel: line.getPixels()) {

#52

Yes, I have the same error as in the post Ray Tracer implementation

    ofPixels_ <unsigned char>pixels = image->getPixels();

    parallel_for( tbb::blocked_range<size_t>(0,pixels.getHeight()),
                 [&](const tbb::blocked_range<size_t>& r) {
                     for(auto line: pixels.getLines(r.begin(), r.end() - r.begin())) {
                         auto y = line.getLineNum();
                         auto x = 0;
                         //for (auto &pixel: line.getPixels()) {
                         for (auto pixel: line.getPixels()) {
                             glm::vec3 P;
                             glm::vec3 w;

                             // Find the ray through (x, y) and the center of projection
                             camera.getPrimaryRay(float(x) + 0.5f, float(y) + 0.5f, width, height, P, w);
                             auto color = L_i(ofxRTRay(P, w));
                             //pixels.setColor(x, y, color);
                             pixel[1] = color.g;
                             pixel[2] = color.b;
                             pixel[3] = color.a;
                             x++;
                         }
                     }
                }
    );
    image->update();

#53

i think this is just a bug in the static analizer, for me i see the same error but compiling works fine with this code:

    ofPixels & pixels = image->getPixels();

    parallel_for( tbb::blocked_range<size_t>(0,pixels.getHeight()),
                 [&](const tbb::blocked_range<size_t>& r) {
                     for(auto line: pixels.getLines(r.begin(), r.end() - r.begin())) {
                         auto y = line.getLineNum();
                         auto x = 0;
                         for (auto pixel: line.getPixels()) {
                             glm::vec3 P;
                             glm::vec3 w;

                             // Find the ray through (x, y) and the center of projection
                             camera.getPrimaryRay(float(x) + 0.5f, float(y) + 0.5f, width, height, P, w);
                             auto color = L_i(ofxRTRay(P, w));
                             //pixels.setColor(x, y, color);
                             pixel[1] = color.g;
                             pixel[2] = color.b;
                             pixel[3] = color.a;
                             x++;
                         }
                     }
                }
    );
    image->update();

otherwise try explicitly setting the type instead of auto:

    ofPixels & pixels = image->getPixels();

    parallel_for( tbb::blocked_range<size_t>(0,pixels.getHeight()),
                 [&](const tbb::blocked_range<size_t>& r) {
                     for(ofPixels::Line line: pixels.getLines(r.begin(), r.end() - r.begin())) {
                         auto y = line.getLineNum();
                         auto x = 0;
                         for (ofPixels::Pixel pixel: line.getPixels()) {
                             glm::vec3 P;
                             glm::vec3 w;

                             // Find the ray through (x, y) and the center of projection
                             camera.getPrimaryRay(float(x) + 0.5f, float(y) + 0.5f, width, height, P, w);
                             auto color = L_i(ofxRTRay(P, w));
                             //pixels.setColor(x, y, color);
                             pixel[1] = color.g;
                             pixel[2] = color.b;
                             pixel[3] = color.a;
                             x++;
                         }
                     }
                }
    );
    image->update();

also be careful to use a reference when initially calling image->getPixels, otherwise you are making a copy and the last image->update won’t really update the original


#54

Same error, also with your second snippet.
The image is passed by reference, this is the signature of the method:

void ofxRayTracer::traceImage(const ofxRTPinholeCamera& camera, ofRectangle& rectangle, shared_ptr<ofImage>& image)

I compile the project in xcode simply pressing the run button.
I think the only solution that I have now is to use qt creator, right?


#55

tbb works! basically, I was not adding the linker flag into the build. This question in SO solved this problem.

The rendering is very fast, but the image rendered is wrong:
this is the 1x1 resolution

The 120x75 resolution is a black square
The render results are stable and not random.

If I use

pixels.setColor(x, y, color);

Instead of

pixel[0] = color.r;
pixel[1] = color.g;
pixel[2] = color.b;
pixel[3] = color.a;

I obtain totally different results, like:

And these are the results that I was obtaining when x and y go out of range. And it looks like this is what is happening now. If I print the x and y value in the loop, they are completely wrong. and not because of the succession order of x and y, but because sometimes y is 0. This is how I print the values.

     cout <<"x y:"<< endl;
     cout << x << endl;
     cout << y << endl;
     glm::vec3 P;
     glm::vec3 w;

     // Find the ray through (x, y) and the center of projection
     camera.getPrimaryRay(float(x) + 0.5f, float(y) + 0.5f, width, height, P, w);
    /// etc....
}

And this is the result, just a part of it because it is too long

x 0y
:x
 y1:x1
 1y
1:1
01

10x09 

y:0

x1 1y2:x

 y01:
1
2
110x0

#56

I think that the problem is in the x and y value inside the parallel loop. If I do not run the loop in parallel, it works good.

Also, i’ve tried this, to get the y value directly from the line.

cout << line.getLineNum() << endl; //this always print 0!
camera.getPrimaryRay(float(x) + 0.5f, float(line.getLineNum()) + 0.5f, width, height, P, w);

But the y is always equal to zero.


#57

just as a future reference if someone has the same problem, I’ve solved the issue in an easier way. This is the whole method, it contains a version with parallel_for and a version that uses a normal for loop, the switch is the parallel variable.

void ofxRayTracer::traceImage(const ofxRTPinholeCamera& camera, ofRectangle& rectangle, shared_ptr<ofImage>& image, bool& parallel){
    const int width = int(rectangle.getWidth());
    const int height = int(rectangle.getHeight());
    auto startAtTime = ofGetElapsedTimeMillis();
    ofPixels renderPixels;
    renderPixels.allocate(width, height, OF_IMAGE_COLOR_ALPHA);

    if (!parallel){
        for (int y = 0; y < height; ++y) {
            for (int x = 0; x < width; ++x) {
                glm::vec3 P;
                glm::vec3 w;
                // Find the ray through (x, y) and the center of projection
                camera.getPrimaryRay(float(x) + 0.5f, float(y) + 0.5f, width, height, P, w);
                renderPixels.setColor(x, y, L_i(ofxRTRay(P, w)));
            }
        };
    } else {
        tbb::parallel_for(0, height,
                          [&] (int y)
                          {
                              for (int x = 0; x < width; ++x) {
                                  glm::vec3 P;
                                  glm::vec3 w;
                                  // Find the ray through (x, y) and the center of projection
                                  camera.getPrimaryRay(float(x) + 0.5f, float(y) + 0.5f, width, height, P, w);
                                  renderPixels.setColor(x, y, L_i(ofxRTRay(P, w)));
                              }
                          });
    }
    image->setFromPixels(renderPixels);
    displayTime(ofGetElapsedTimeMillis() - startAtTime);
}

As reference, to render a 800x600 pixel image, the traditional for loop takes 17.882 seconds. The parallel_for loop just 7.159.
Thanks @arturo for your help!