Reputation: 157
I'm trying to find a way to draw, by OpenGL calls in C++, to a HTML element, in such a way that anything behind the canvas (background image, HTML text, ...) is visible where the GL context framebuffer has no opaque color to be drawn, or maybe even with the ability to use blending.
I've tried setting opacity to zero in the glClearColor() call. I use emscripten to compile my C++ code, and when I use it to generate a module loader, I've noticed that there's code in the generated output to make the canvas element's background color black when no background color has explicitly been set. I've tried disabling this behaviour, in the hopes of gaining transparency, to no avail.
I know that Unity's WebGL version supports transparent canvases, as is demonstrated here. But I'm not sure if the Unity of which spoken actually uses WebAssembly, because I do know the JavaScript end to a canvas element can be used to draw on a transparent background.
Is this already possible? Will it ever be?
Upvotes: 1
Views: 1428
Reputation:
Of course it's 100% possible because WebGL can do it. You can always fork the emscripten libraries,change a few lines, and use your fork. Unfortunately I can't give you an answer unless you specify how your are intializing OpenGL in emscripten. SDL? EGL? GLEW? GLUT? Each will have a different answer.
The first thing I would do is go look at the source for these libraries.
For SDL we see this
$SDL: {
defaults: {
width: 320,
height: 200,
// If true, SDL_LockSurface will copy the contents of each surface back to the Emscripten HEAP so that C code can access it. If false,
// the surface contents are captured only back to JS code.
copyOnLock: true,
// If true, SDL_LockSurface will discard the contents of each surface when SDL_LockSurface() is called. This greatly improves performance
// of SDL_LockSurface(). If discardOnLock is true, copyOnLock is ignored.
discardOnLock: false,
// If true, emulate compatibility with desktop SDL by ignoring alpha on the screen frontbuffer canvas. Setting this to false will improve
// performance considerably and enables alpha-blending on the frontbuffer, so be sure to properly write 0xFF alpha for opaque pixels
// if you set this to false!
opaqueFrontBuffer: true
},
and this
var webGLContextAttributes = {
antialias: ((SDL.glAttributes[13 /*SDL_GL_MULTISAMPLEBUFFERS*/] != 0) && (SDL.glAttributes[14 /*SDL_GL_MULTISAMPLESAMPLES*/] > 1)),
depth: (SDL.glAttributes[6 /*SDL_GL_DEPTH_SIZE*/] > 0),
stencil: (SDL.glAttributes[7 /*SDL_GL_STENCIL_SIZE*/] > 0),
alpha: (SDL.glAttributes[3 /*SDL_GL_ALPHA_SIZE*/] > 0)
};
for EGL there's this
var LibraryEGL = {
$EGL__deps: ['$Browser'],
$EGL: {
// This variable tracks the success status of the most recently invoked EGL function call.
errorCode: 0x3000 /* EGL_SUCCESS */,
defaultDisplayInitialized: false,
currentContext: 0 /* EGL_NO_CONTEXT */,
currentReadSurface: 0 /* EGL_NO_SURFACE */,
currentDrawSurface: 0 /* EGL_NO_SURFACE */,
alpha: false,
For GLUT there is this
glutCreateWindow: function(name) {
var contextAttributes = {
antialias: ((GLUT.initDisplayMode & 0x0080 /*GLUT_MULTISAMPLE*/) != 0),
depth: ((GLUT.initDisplayMode & 0x0010 /*GLUT_DEPTH*/) != 0),
stencil: ((GLUT.initDisplayMode & 0x0020 /*GLUT_STENCIL*/) != 0),
alpha: ((GLUT.initDisplayMode & 0x0008 /*GLUT_ALPHA*/) != 0)
};
which seem like they might lead to an answer?
Otherwise if you can't be bothered to read through the source then you can force it. Add this code to the top of your html file before any other scripts
<script>
(function() {
if (typeof HTMLCanvasElement !== "undefined") {
wrapGetContext(HTMLCanvasElement);
}
if (typeof OffscreenCanvas !== "undefined") {
wrapGetContext(OffscreenCanvas);
}
function wrapGetContext(ContextClass) {
const isWebGL = /webgl/i;
ContextClass.prototype.getContext = function(origFn) {
return function(type, attributes) {
if (isWebGL.test(type)) {
attributes = Object.assign({}, attributes || {}, {alpha: true});
}
return origFn.call(this, type, attributes);
};
}(ContextClass.prototype.getContext);
}
}());
</script>
I tested that with this sample, changed this line
SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255);
to this
SDL_SetRenderDrawColor(renderer, 0, 0, 0, 0);
and it worked for me.
Upvotes: 1