Webgl context is null if 2d context is called on canvas - javascript

I wrote the following JS code:
var main=function() {
var CANVAS=document.getElementById("your_canvas");
CANVAS.width=window.innerWidth;
CANVAS.height=window.innerHeight;
//ctx=CANVAS.getContext("2d");
//ctx.fillText("Hello World",10,50);
/*========================= GET WEBGL CONTEXT ========================= */
var GL;
try {
GL = CANVAS.getContext("experimental-webgl", {antialias: true});
} catch (e) {
alert("You are not webgl compatible :(") ;
return false;
}
var CUBE_VERTEX= GL.createBuffer ();
};
If I uncomment the two commented lines, then the webgl context is NULL. Is this expected ? It's not possible to use 2D context and webgl context on the same canvas ?

This is expected, as webgl basically is a wrapper around opengl, primarily used for accelerated 3D rendering. getContext("experimental-webgl") is essentially telling the browser, that the defined canvas is to be used exclusively by OpenGL/WebGL. (It is possible to use OpenGL/WebGL for 2D, but unless you know what you are doing, you're giving yourself a hard time in doing so.)
If you are trying to render text on top of a 3D view, you will have to overlay two different DOM-elements (or render the text with the correct projection in 3D, again hard time-territory).

Related

handling WebGL loss in three js

I can't figure out what to do in case of webgl loss in my application (written with electron js) with three js. We have these two functions
// renderer is THREE.WebGLRenderer
renderer.context.canvas.addEventListener("webglcontextlost", contextLostFunction);
renderer.context.canvas.addEventListener("webglcontextrestored", contextRestoredFunction);
When I simulate context loss using something like this
var canvas = document.getElementById("playground").childNodes[0].childNodes[0];
var gl = canvas.getContext("webgl");
var WEBGL_lose_context = gl.getExtension('WEBGL_lose_context');
WEBGL_lose_context.loseContext();
Then webglcontextrestored event fires and everything restores as should be.
When webgl is killed for real or using something like this
renderer.context.getExtension( 'WEBGL_lose_context' ).loseContext();
Then this event webglcontextrestored never has been fired.
What is going ? What to do to catch that context has been lost.
Thanks for any ideas.
You should use same extension reference you use to loose the context to restore the context with the restoreContext() method of the object:
var canvas = document.getElementById("playground").childNodes[0].childNodes[0];
var gl = canvas.getContext("webgl");
var WEBGL_lose_context = gl.getExtension('WEBGL_lose_context');
WEBGL_lose_context.loseContext();
window.setTimeout(()=> {
WEBGL_lose_context.restoreContext();
}, 2000);
you can also do it from the inspector to simulate it in an iterative way...

Show two WebGL animations with the same shader

I need to show two animations using WebGL on the page. Do I need to instantiate multiple shaders, or is there a way to reuse one shader? They are using the same program (so not entirely different animations). They both need to react to mouse events.
something like this
window.onload = function() {
main('canvas1');
main('canvas2');
}
function main(element) {
// Get A WebGL context
var canvas = document.getElementById(element);
var gl = getWebGLContext(canvas);
if (!gl) {
return;
}
// setup GLSL program
vertexShader = createShaderFromScriptElement(gl, "2d-vertex-shader");
fragmentShader = createShaderFromScriptElement(gl, "2d-fragment-shader");
program = createProgram(gl, [vertexShader, fragmentShader]);
gl.useProgram(program);
...
}
I don't understand the derogaratory remarks. I asked a theoretical question about webgl, not particular code.
I ran this code and it works. So now all I need to know is if I can use mouse events on both. I'd be surprised if that didn't work.
This was already answered by someone else:
Is it possible to have two WebGL contexts on the same page?
WebGL cannot share resources over multiple contexts. But you can create an object with its own gl variable, and then have multiple contexts on the page. The only limit I see now is that somehow it's intensive for the system and it won't allow more than 16 concurrent contexts.

Canvas toDataURL() returns blank image

I'm using glfx.js to edit my image but when I'm trying to get that image's data using the toDataURL() function I get a blank image (width the same size as the original image).
The strange thing is that in Chrome the script works perfect.
What I want to mention is that the image is loaded in canvas using the onload event:
img.onload = function(){
try {
canvas = fx.canvas();
} catch (e) {
alert(e);
return;
}
// convert the image to a texture
texture = canvas.texture(img);
// draw and update canvas
canvas.draw(texture).update();
// replace the image with the canvas
img.parentNode.insertBefore(canvas, img);
img.parentNode.removeChild(img);
}
Also my image's path is on the same domain;
The problem (in Firefox) is when i hit the save button. Chrome returns the expected result but Firefox return this:
data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAA7YAAAIWCAYAAABjkRHCAAAHxklEQVR4nO3BMQEAAADCoPVPbQZ/oAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
... [ lots of A s ] ...
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAzwD6aAABkwvPRgAAAABJRU5ErkJggg==
What could cause this result and how can I fix it?
Most likely there's some async event between the time you draw to the canvas and the time you call toDataURL. By default the canvas is cleared after every composite. Either prevent the canvas from being cleared by creating the WebGL context with preserveDrawingBuffer: true as in
var gl = canvas.getContext("webgl", {preserveDrawingBuffer: true});
or make sure toDataURL is called before exiting whatever event you're using to render. For example if you do this
function render() {
drawScene();
requestAnimationFrame(render);
}
render();
And somewhere else do this
someElement.addEventListener('click', function() {
var data = someCanvas.toDataURL();
}, false);
Those 2 events, the animation frame, and the click are not in sync and the canvas may be cleared between calling them. Note: The canvas won't appear cleared as it's double buffered but the buffer toDataURL and other commands that effect that buffer are looking at is cleared.
The solution is either use preserveDrawingBuffer or make your call to toDataURL inside the same event as rendering. For example
var captureFrame = false;
function render() {
drawScene();
if (captureFrame) {
captureFrame = false;
var data = someCanvas.toDataURL();
...
}
requestAnimationFrame(render);
}
render();
someElement.addEventListener('click', function() {
captureFrame = true;
}, false);
What's the point of preserveDrawingBuffer: false which is the default? It can be significantly faster, especially on mobile to not have to preserve the drawing buffer. Another way to look at it is the browser needs 2 copies of your canvas. The one you're drawing to and the one it's displaying. It has 2 ways to deal with these 2 buffers. (A) double buffer. Let you draw to one, display the other, swap the buffers when you're done rendering which is inferred from exiting any event that issued draw commands (B) Copy the contents of the buffer you're drawing to do the buffer that's being displayed. Swapping is much faster than copying. So, swapping is the default. It's up to the browser what actually happens. The only requirement is that if preserveDrawingBuffer is false that the drawing buffer get cleared after a composite (which is yet another async event and therefore unpredictable) if preserveDrawingBuffer is true then it must copy so that the drawingbuffer's contents is preserved.
Note that once a canvas has a context it will always have the same context. So in other words let's say you change the code that initializes the WebGL context but you still want to set preserveDrawingBuffer: true
There are at least 2 ways.
find the canvas first, get a context on it
since the code later will end up with the same context.
<script>
document.querySelector('#somecanvasid').getContext(
'webgl', {preserveDrawingBuffer: true});
</script>
<script src="script/that/will/use/somecanvasid.js"></script>
Because you've already created a context for that canvas whatever script comes after will get the same context.
augment getContext
<script>
HTMLCanvasElement.prototype.getContext = function(origFn) {
return function(type, attributes) {
if (type === 'webgl') {
attributes = Object.assign({}, attributes, {
preserveDrawingBuffer: true,
});
}
return origFn.call(this, type, attributes);
};
}(HTMLCanvasElement.prototype.getContext);
</script>
<script src="script/that/will/use/webgl.js"></script>
In this case any webgl context created after augmenting the getContext will have preserveDrawingBuffer set to true.

Using Processing.js: Can I have multiple canvases with only one data-processing-source sketch.pde?

using Processing.js, I would like to know if what I'm trying to do is even possible. I've looked on Pomax's tutorials, Processing.js the quick start of JS developers page, PJS the Google group, here, and I can't seem to find an answer to the question, "Can you have multiple canvases, such that they all use the same processing sketch (in my example below, engine.pde) each canvas passing variables to the sketch with the result being processing opens different images in each canvas, but edits them the same way.
So to sum up, I would like to use only 1 processing sketch, with many canvases, with each canvas telling the processing sketch a different name, and having a corresponding background image open in the sketch in each canvas.
<!DOCTYPE html><html><head><meta charset="utf-8">
<script src="../../../processingjs/processing.js"></script>
<script>
// Tell sketch what counts as JavaScript per Processing on the Web tutorial
var bound = false;
function bindJavascript(instance) { // Can I pass 'instance' like this?
var pjs = Processing.getInstanceById(instance);
if(pjs!=null) {
pjs.bindJavascript(this);
bound = true; }
if(!bound) setTimeout(bindJavascript, 250); }
bindJavascript('B104');
bindJavascript('B105');
function drawSomeImages(instance) {
// This is where I am trying to tell processing that each canvas has a number, and the number is assigned to a corresponding image.
var pjs = Processing.getInstanceById(instance);
var imageName = document.getElementById(instance);
pjs.setup(instance);
}
drawSomeImages('B104');
drawSomeImages('B105');
// Where is the Mouse?
function showXYCoordinates(x, y) { ... this is working ... }
// Send images back to server
function postAjax(canvasID) { ... AJAX Stuff is working ...}
</script>
</head>
<body>
<canvas id="B104" data-processing-sources="engine.pde" onmouseout="postAjax('B104')"></canvas>
<canvas id="B105" data-processing-sources="engine.pde" onmouseout="postAjax('B105')"></canvas>
</body>
</html>
And on the processing side:
/* #pjs preload=... this is all working ; */
// Tell Processing about JavaScript, straight from the tutorial...
interface JavaScript {
void showXYCoordinates(int x, int y);
}
void bindJavascript(JavaScript js) {
javascript = js;
}
JavaScript javascript;
// Declare Variables
PImage img;
... some other variables related to the functionality ...
void setup(String instance) {
size(300,300);
img = loadImage("data/"+instance+".png");
//img = loadImage("data/B104.png"); Example of what it should open if canvas 104 using engine.pde
background(img);
smooth();
}
void draw() { ... this is fine ... }
void mouseMoved(){ ... just calls draw and checks if mouse is in canvas, fine... }
if(javascript!=null){
javascript.showXYCoordinates(mouseX, mouseY);
}}
Just add a million canvas elements to your page all with the same data-processing-sources attribute, so they all load the same file. Processing.js will build as many sketches as you ask for, it doesn't care that the sketch files are the same for each one =)
(Note that what you described, one sketch instance rendering onto multiple canvases, giving each a different image, is not how sketches work. A sketch is tied to a canvas as its drawing surface. However, you can make a million "slave" sketches whose sole responsibility is to draw images when so instructed from JavaScript, and making the master sketch tell JavaScript to tell slave sketches to draw. Note that this is very, very silly. Just make JavaScript set the image, you don't need Processing if you're just showing images really)

Three.js detect webgl support and fallback to regular canvas

Can anyone who has used three.js tell me if its possible to detect webgl support, and, if not present, fallback to a standard Canvas render?
Yes, it's possible. You can use CanvasRenderer instead of WebGLRenderer.
About WebGL detection:
1) Read this WebGL wiki article: http://www.khronos.org/webgl/wiki/FAQ
if (!window.WebGLRenderingContext) {
// the browser doesn't even know what WebGL is
window.location = "http://get.webgl.org";
} else {
var canvas = document.getElementById("myCanvas");
var context = canvas.getContext("webgl");
if (!context) {
// browser supports WebGL but initialization failed.
window.location = "http://get.webgl.org/troubleshooting";
}
}
2) Three.js already has a WebGL detector:
https://github.com/mrdoob/three.js/blob/master/examples/js/Detector.js
renderer = Detector.webgl? new THREE.WebGLRenderer(): new THREE.CanvasRenderer();
3) Check also the Modernizr detector:
https://github.com/Modernizr/Modernizr/blob/master/feature-detects/webgl.js
Juan Mellado's pointer to the Three.js detector was super useful, but I prefer not to bring the whole file into my project. So here is the extracted Detector.webgl() function.
function webglAvailable() {
try {
var canvas = document.createElement("canvas");
return !!
window.WebGLRenderingContext &&
(canvas.getContext("webgl") ||
canvas.getContext("experimental-webgl"));
} catch(e) {
return false;
}
}
And it is used similar to his example:
renderer = webglAvailable() ? new THREE.WebGLRenderer() : new THREE.CanvasRenderer();
Unfortunatelly, just detecting WebGL support does not automatically mean it will be any good. WebGL can be backed by software renderer like "google swiftshader" or partial emulation like "mesa 3D". Especially with a good 2D renderer like Mesa 2D it makes sense to manually choose canvas even when WebGL seems available.

Categories