vtk.js off-screen rendering

I have a need to capture a set of renderings from one of my viz app’s renderWindow, but at a different resolution.
My current approach is to attempt to use offscreen canvas via RenderWindow’s useOffscreen=true property.
I also use a div with hidden attribute
as the html element containing the renderwindow.
With this approach i am getting [.WebGL-0xblabla] GL_INVALID_FRAME_BUFFER_OPERATION: Framebuffer is incomplete Attachment has zero size.

Is there any example that uses the useOffscreen property?

I may change my approach to displaying a temporary window for the purpose of rendering if useOffscreen doesn’t work out. Any other approaches are welcome.

I will have to investigate why the offscreen property causes a webgl issue. In the meantime, I’ve done this via createElement('div') and not adding the element to the node tree. Using this approach, you don’t need to use the offscreen property at all.

I’ll try your suggestion not adding the div to the dom tree.
I looked briefly at the code, maybe using an OffscreenCanvas rather than a canvas with display:none will work. It appears OffscreenCanvas supports webgl contexts from what i read on mdn.

Could you provide a minimum proof-of-concept (i.e. via CodeSandbox) that demonstrates the error?

OffscreenCanvas can also work. However, what you are trying to do should just work, and so I would like to have a repro example so I can see if it’s an actual bug.

I created a proof-of-concept that demonstrates incorrect results, although not the same issue as initially reported. It is pretty small, and somewhere along the way, and unfortunately have trouble to explain why the initial GL_INVALID_FRAME_BUFFER_OPERATION no longer appears for me. Now the issue is that the capture image is incorrect in size and content when trying to capture from off-screen rendering. With just 2 small changes, you can re-run the proof-of-concept to see correct capture when capturing ON-screen rendering.

I am having a bit of a learning curve with codeSandbox where I get the web page that just says ‘Invalid Host Header’ when running the npm run test. Maybe just a standard git clone will be acceptable for helping me.

git clone GitHub - John-Skinner/vtkjsproto: general recreating issues with vtk.js

the readme.me for the git project describes how to switch from off-screen rendering/capturing to on-screen rendering/capturing.

Thanks for the repo. Here are a few notes:

  • useOffscreen only exists on the vtkOpenGLRenderWindow, not the vtkRenderWindow.
  • The problem you’re encountering is that the hidden attribute sets the element’s clientHeight and clientWidth to 0. This results in the OpenGLRenderWindow’s height/width being set to zero as well, hence why you are rendering nothing.

Thanks Forrest,
Your suggestion works and I can now do offscreen rendering.

Summarizing

  1. I defined a div element that is hidden and occupies no UI space:
<div id="renderDiv" style="width:512px;height:512px" hidden>
  1. created a render window and opengl render window as follows:
        // using the global window for simplicity, better to avoid this in practice.
        window.rw = vtkRenderWindow.newInstance();
        window.oglrw = vtkOpenGLRenderWindow.newInstance({useOffScreen:true});
        window.oglrw.setContainer(container); // container is the HTMLDivElement with id=renderDiv
        window.oglrw.setSize(512,512); // must explicitly set dimensions and not set to the div's dimensions
        window.rw.addView(window.oglrw);

  1. I render the scene to the hidden window
    let renderer = vtkRenderer.newInstance();
    renderer.setBackground([0.1,0.1,0.2]);
    renderer.setViewport(0,0,1,1);
    window.rw.addRenderer(renderer);
 renderer.getRenderWindow().render();
  1. Capture the hidden rendering and pasted to an element (copyDiv)
        console.log(" button clicked");
        let capturePromises = window.rw.captureImages();
        if (capturePromises.length > 0)
        {
            capturePromises[0].then((imageURL)=>
            {
                console.log(" url:" + imageURL);
                let img = new Image();
                img.src = imageURL;
                img.addEventListener('load',(e)=>
                {
                    console.log(' image loaded');
                    console.log('size:' + img.width + 'x' + img.height);
                    let copyDiv = document.getElementById('captureDiv');
                    document.body.appendChild(copyDiv);
                    copyDiv.appendChild(img);

                })

            })
        }
1 Like