Wrong vertical offset used in SoOffscreenRenderer

Create issue
Issue #74 resolved
Thomas Moeller created an issue

The SoOffscreenRenderer class uses a wrong vertical offset when rendering a scene into a context with a smaller width than the previous one. Coin reuses the previous context when the buffer is large enough. However, it incorrectly computes a vertical offset that should be zero (similar to horizontal offset). The attached image visualizes the issue (IPython), which can be observed accessing the offscreen buffer and also writing images to file.

SoOffscreenRenderer.png

Comments (9)

  1. Tom F

    Not sure about the problem, but can I ask what coin bindings are being used here? Seems like a nice syntax. Doesn't look like pivy to me.

  2. Roy Walmsley

    Thomas,

    I have had a look at your code above and have having some trouble. Please can you tell me where the function iv.image (root, x, y) is in the Coin source files? I want to review the whole process from start to finish.

    Many thanks

    Roy

  3. Thomas Moeller reporter

    Roy, Thomas,

    I was using IPython with the PyInventor binding (see http://thehubbit.github.io/PyInventor) as it shows the issue nicely and also for convenience. But I realize it doesn’t do a good job explaining how this relates to Coin. Therefore I created a small C program to reproduce the issue natively.

    #include <stdint.h>
    #include <Inventor/nodes/SoSeparator.h>
    #include <Inventor/SoOffscreenRenderer.h>
    #include <Inventor/SoDB.h>
    
    
    int wmain(int argc, wchar_t* argv[])
    {
        SoDB::init();
    
        SbViewportRegion viewport;
        SoOffscreenRenderer offscreen(viewport);
        offscreen.setViewportRegion(viewport);
    
        // Create simple scene with a cone in it.
        const char *scene = "#Inventor V2.1 ascii\n\n"
            "Separator { } DirectionalLight { } OrthographicCamera { position 0 0 5 } Cone { }";
    
        SoInput in;
        in.setBuffer(scene, strlen(scene));
        SoSeparator *root = SoDB::readAll(&in);
        root->ref();
    
        // First image has size of 512 x 512.
        viewport.setViewportPixels(0, 0, 512, 512);
        offscreen.setViewportRegion(viewport);
        offscreen.render(root);
        offscreen.writeToFile("cone1.jpg", "jpg");
    
        // After reducing output size to 256 x 512 the vertical 
        // offset is wrong. The offscreen renderer reuses the
        // last OpenGL context as the new image still "fits" 
        // into it but miscomputes the offset.
        viewport.setViewportPixels(0, 0, 256, 512);
        offscreen.setViewportRegion(viewport);
        offscreen.render(root);
        offscreen.writeToFile("cone2.jpg", "jpg");
    
        root->unref();
    
        return 0;
    }
    

    The content of cone1.jpg and cone2.jpg will be similar to the output in IPython above. However, the cone should be in the center for the second image as well.

    Best,

    Thomas

  4. Roy Walmsley

    Thomas,

    My gut feeling is that your change is the right way to go, but I am having trouble understanding why the second image is as it is. In image 1 the cone has a size of 512x512 pixels. I am assuming in image 2 the cone size is 256x256 pixels. Your viewport size is 512x512 pixels. So why does the bottom of the cone in picture 2 appear to be at 384? Perhaps I'm just being paranoid...

    It also seems strange that the original code line should try to set the 'y' origin to be the difference of the 'x' sizes! I could understand that better if the original coding was concerned with flipping of the 'y' axis.

    Maybe I should just ignore all that and go for the change...

    Roy

  5. Thomas Moeller reporter

    Roy,

    Yes, the cone in the second image should be 256x256 pixels. Also, 128 pixels padding should be added at the top and bottom as the camera by default uses ADJUST_CAMERA in the viewportMapping field. The correct output for the second image should look like this:

    coin2.jpg

    Instead we see the cone in the second image at an offset of 384 because the difference between context width (512) and wanted output width (256) is used as vertical origin when rendering:

    region.setViewportPixels(0,glsize[0]-fullsize[0],fullsize[0],fullsize[1]);
    
    this->renderaction->setViewportRegion(region);
    

    I too couldn't understand the intention of the original code with regards to the offset in 'y' direction. An image flip can't be accomplished simply by viewport offsets and also would not explain using the difference in width.

    In my opinion the class should render at the origin (0, 0) regardless of the viewport size in the regular, non-tiled case.

    Best,

    Thomas

  6. Log in to comment