Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Display different scenes sharing resources on multiple canvases

I'm using three.js to create an interactive web application, and I've run into a little stumbling block:

I have a number of canvases contained in draggable divs on the page. In each canvas I hope to display an unlit 3D object with a different material applied (each material is using custom shaders). All of those materials work off the same texture (one might be blue-tinted, one might be desaturated, etc.).

The number of canvases on the page can vary, but I expect the number to commonly reach/exceed 20 canvases, and as such sharing resources (particularly for large textures) would be very beneficial.

Up until now I have been using multiple renderers, cameras and scenes which has worked fine until I started trying to use the same texture in multiple scenes.

Most of the materials share uniforms and attributes to avoid having to duplicate the information, and also so that all of the materials stay in sync with one another (e.g. when some of the materials change over time they should all change in the same way).

I was wondering if there was a way I would be able to share textures between the scenes/renderers/canvases? When I try I get the following error:

WebGL: INVALID_OPERATION: bindTexture: object not from this context 

In my research trying to find a solution for this problem I came across the suggestion that this could be solved by creating multiple viewports, however I do not know how to display different viewports over different canvases.

TL/DR;

Can I either:

  • Show the same scene across different canvases?
  • Use the same uniforms (including a texture uniform) across different scenes, renderers and/or canvases?

Thanks in advance!

Griffork

like image 933
Griffork Avatar asked Jan 14 '23 18:01

Griffork


1 Answers

Unfortunately you can not (yet) share resources across canvases. A couple of options

  1. Render the different viewports in different parts of the same canvas.

    Example: http://webglsamples.org/multiple-views/multiple-views.html

  2. Make a canvas that covers the entire window, use place holder elements to figure out where to draw, use getClientBoundingRect to set the viewport & scissor settings to draw scenes in each element

    Example: Is it possible to enable unbounded number of renderers in THREE.js?

  3. Render the scene to an offscreen canvas then draw it into a visible canvases.

    <canvas id="c1"></canvas>
    <canvas id="c2"></canvas>
    <script>
    var webglCanvas = document.createElement("canvas");
    var canvas1 = document.getElementById("c1");
    var ctx1 = canvas1.getContext("2d");
    var canvas2 = document.getElementById("c1");
    var ctx2 = canvas1.getContext("2d");
    

    ... draw scene into webglCanvas from one view...

    // copy scene to canvas1
    ctx1.drawImage(webglCanvas, 0, 0);
    

    ... draw scene into webglCanvas from another view...

    // copy scene to canvas2
    ctx2.drawImage(webglCanvas, 0, 0);
    

    Here's a live example (note: It's slow on Windows in Chrome26, FF20, Hopefully that will be fixed in future browsers)

  4. Use OffscreenCanvas, transferToImageBitmap etc... (note: this feature has not shipped in any browser as of 2018/1/8 but it's available behind a flag on Firefox)

const one = document.getElementById("one").getContext("bitmaprenderer"); 
const two = document.getElementById("two").getContext("bitmaprenderer");

const offscreen = new OffscreenCanvas(256, 256);
constr gl = offscreen.getContext('webgl');

// ... some drawing for the first canvas using the gl context ...
gl.clearColor(1,0,0,1);
gl.clear(gl.COLOR_BUFFER_BIT);

// Commit rendering to the first canvas
let bitmapOne = offscreen.transferToImageBitmap();
one.transferImageBitmap(bitmapOne);

// ... some more drawing for the second canvas using the gl context ...
gl.clearColor(0,1,0,1);
gl.clear(gl.COLOR_BUFFER_BIT);

// Commit rendering to the second canvas 
let bitmapTwo = offscreen.transferToImageBitmap();
two.transferImageBitmap(bitmapTwo);
<canvas id="one"></canvas>
<canvas id="two"></canvas>
like image 155
gman Avatar answered Jan 30 '23 22:01

gman