Render to texture in Canvas ?

liviucliviuc Posts: 6


Is is possible to render to a texture in Canvas ? I'd like to use InlineDrawing/OGL to render a mesh (with custom shaders) into a GL texture, which I should then be able to export to a file.

If it's not possible at that level, would there be a way to grab the OGL context of Canvas and perform render-to-texture from there ? I'd like to avoid writing a complete OGL renderer and rather reuse as much from InlineDrawing as possible.

Any pointer in the right direction is highly appreciated !


  • borjaborja Administrator, Fabric Employee Posts: 468 admin

    Hi @liviuc

    There is no builtin presets to do this, but @AlexanderM created a canvas graph to allow to save the viewport render to disk and shared it at

    We plan in the future to add this functionality to the builtin toolset of Fabric!

    Borja Morales
    Technical Product Manager
    Fabric Software Inc.

  • liviucliviuc Posts: 6

    Hi Borja - and thanks for your answer.

    Because I need to render many frames, ideally I'd like to be able to render to a framebuffer and the save the result to disk - while reusing InlineDrawing rendering. In the meanwhile I've figured out how to approach this, my code currently looks something like this:


    glBindFramebuffer(GL_FRAMEBUFFER, framebuffers[0]);
    SInt32 aiViewport[];
    glGetIntegerv(GL_VIEWPORT, aiViewport);
    glViewport(0,0,256,256); // Render on the whole framebuffer, complete from the lower left corner to the upper right

    *[render stuff using InlineDrawing API, shapes, etc. - actually, this does not render anything, it just adds shapes to the scene which should be rendered by the central render callback]


    image = Image2DRGBA();
    image.resize(256, 256);
    glReadPixels(0, 0, 256, 256, GL_RGBA, GL_UNSIGNED_BYTE,;

    [save image using OpenImageOutput KL wrappers]

    I am currently stuck trying to trigger rendering the InlineDraw at *. It does look like the setting up the framebuffer and saving it to the disk works fine (I'm clearing it to green and the exported image is green :))

    In the docs I've found this:

    It suggests to use the Fabric C++ API to trigger the rendering. After creating the FabricCore::RTVal of the DrawContext (object) it says:

    "You will then need to fill all of its members accordingly. See the KL snippet below for more detail."

    However, there is no KL snippet below and I am totally puzzled as to what I should fill in. At the moment I call this function from KL right after setting up things, but it does not seem to render anything:

    FabricCore::RTVal drawContext = FabricSplice::constructObjectRTVal("DrawContext");
        printf("Now drawing OGL context \n");

    Could you please help me figure out how to trigger rendering of the InlineDrawing after setting up my framebuffer ?

    Thanks and regards,

  • liviucliviuc Posts: 6

    I really need some help here.

    I can render the content of the GL viewport to a texture as per the above post. But I need to do that after I change the scene/camera and the exact moment when the rendering happens is somewhat non-deterministic.

    So I need a way to tell Canvas: "render whatever there is to render". It looks like FabricSplice::SceneManagement::drawOpenGL(drawContext); has no effect whatsoever :(


  • AlexanderMAlexanderM Posts: 132 ✭✭
    edited June 7

    May be it will help. When I made tests with OpenGL in canvas I found that the best way to manipulate is register my own viewport collback. Inside this collback you can past your program
    Here is an example of my little extension (KL file), it draws my custom pixel data over canvas viewport. You may change code inside invoke to adapt to your task.
    And screenshots of a tree using

    require InlineDrawing;
    require FabricOGL;
    require Math;
    operator set_colors<<<i>>>(io UInt8 rgbaData[], Color colors[])
           rgbaData[i * 4    ] = UInt8(colors[i].r * 255);
           rgbaData[i * 4 + 1] = UInt8(colors[i].g * 255);
           rgbaData[i * 4 + 2] = UInt8(colors[i].b * 255);
           rgbaData[i * 4 + 3] = UInt8(colors[i].a * 255);  
    //custom callback
    object DrawPixels : ViewportCallback {
      Color colors[];
      SInt32 nbx;
      SInt32 nby;
      Boolean enable;
    function Boolean DrawPixels.invoke! ( in ViewportDrawPhase phase, io Viewport viewport, io DrawContext context ) {
        this.colors.resize(this.nbx * this.nby * 4);
        SInt32 viewportNew[];
        glGetIntegerv(UInt32 (GL_VIEWPORT), viewportNew);
        SInt32 nbxV = viewportNew[2];
        SInt32 nbyV = viewportNew[3];
        SInt32 nbx = this.nbx;
        SInt32 nby = this.nby;
        UInt8 rgbaData[];
        UInt32 nbTotal = nbx*nby;
        rgbaData.resize(nbTotal * 4);
      set_colors<<<nbTotal>>>(rgbaData, this.colors);
        glPushMatrix(); //add a new proj m      
        glLoadIdentity();    // modify a new proj m
        glOrtho(0, 1.0, 1.0, -1, 0, 1);
        glPushMatrix(); //add a new transf m
        glLoadIdentity();     // modify a new transf m
        Float32 posX = Float32(nbxV)/2 - Float32(nbx)/2;
        Float32 posY = Float32(nbyV)/2 - Float32(nby)/2;
        glWindowPos2i(posX, posY);
        glDrawPixels(nbx, nby, GL_RGBA, GL_UNSIGNED_BYTE,; //   
        glPopMatrix();// delete 
        glPopMatrix();// delete  
        return true;
    function DrawPixels.setValues! ( in Color colors[], in SInt32 nbx, in SInt32 nby, in Boolean enable, in Float32 blendFactor, Boolean flipW, Boolean flipH) {
      for(Integer i = 0; i < colors.size(); i++)
        this.colors[i].r = colors[i].r;
        this.colors[i].g = colors[i].g;
        this.colors[i].b = colors[i].b;
        this.colors[i].a = colors[i].a * blendFactor;
      this.nbx = nbx;
      this.nby = nby;
      this.enable = enable;
      if (flipW)
        for (UInt32 j = 0; j < this.nby; j++)
          UInt32 offset = j * this.nbx;
          for (UInt32 i = 0;i<this.nbx/2;i++)
            Color mem = this.colors[offset + i];
            this.colors[offset + i] = this.colors[offset + this.nbx - 1 - i];
            this.colors[offset + this.nbx - 1 - i] = mem;
      if (flipH)
        for (UInt32 j = 0; j < this.nby / 2; j++)
          UInt32 offset1 = j * this.nbx;
          UInt32 offset2 = (this.nby - 1 - j) * this.nbx;
          for (UInt32 i = 0; i<this.nbx; i++, offset1++, offset2++)
            Color mem = this.colors[offset1];
            this.colors[offset1] = this.colors[offset2];
            this.colors[offset2] = mem;

    Let's say NO to Autodesk®Fabric®

  • liviucliviuc Posts: 6

    Hi Alexander - and many thanks for your input !

    While I would actually like to avoid writing any GL rendering code, but rather leverage the InlineDrawing rendering, I'll give it a try to see if, with your code, I can trigger the Viewport to trigger the rendering.

    I am, however, not sure this will help because, as I understand it, the callback will be called by Canvas whenever it thinks it needs to redraw. Which is exactly the issue I'm trying to solve (force a redraw programmatically)


  • liviucliviuc Posts: 6

    Alexander (and all)

    I was able to make some progress using your callback idea as below (basically calling viewport to image code in the post-draw callback). This works fine, because by the post-draw time the rendering has been done.

    However, there is still a gotcha: calling renderMeshes() multiple times from KL code (loop) does not work, because the rendering thread only seems to get a chance to render after the node is fully evaluated. I'm still struggling with that. Maybe it works when using a ForLoop node that evaluates my rendering node multiple times (hopefully the render thread would kick in between successive evaluations).

    Or maybe someone knows how to force the render thread to kick in ? I've tried some various Fabric synchronization mechanisms - a SimpleLock to make KL wait until the Viewport callback is called - but it does not work (it waits forever, because the render thread does not call the callback)

    //custom callback
    object RenderCallback : ViewportCallback {
      Boolean enable;
      Renderer renderer;
      String path;
    object Renderer
        DrawingHandle handle;
        RenderCallback renderCallback;
    function Boolean Renderer.renderMeshes!(
      PolygonMesh meshes[]) 
        <here goes your InlineDrawing rendering code>
        DrawContext dc = DrawContext().getInstance();
        this.renderCallback.enable = true;
        dc.viewport.registerCallback(ViewportDrawPhase_PostDraw, this.renderCallback);
        return true;
    function Boolean RenderCallback.invoke!( in ViewportDrawPhase phase, io Viewport viewport, io DrawContext context ) {
        Boolean result = false;
            // save the viewport content to texture
            result = this.renderer.renderViewportToTexture(this.path);
            this.enable = false;
        return result;
Sign In or Register to comment.