EffekseerForWebGL
EffekseerForWebGL copied to clipboard
Offscreen Canvas Support
Hello,
I tried to use Effekseer in a three.js app with an Offscreen Canvas on a web worker today, but failed to make it happen.
First there are multiple usages of new Image, e.g. here and here which is not available on web workers. I replaced those with createImageBitMap(), but there is still an error thrown somewhere in a promise, followed by [.WebGL-0x11f423912900] GL_INVALID_OPERATION: Must have element array buffer bound..
Would it be possible to make Effekseer for WebGL compatible with offscreen canvas rendering? 🙏 Kind Regards
Update:
I did some more digging and the createImageBitMap replacements work in principal, but this line
this.nativeptr = Core.LoadEffect(this.context.nativeptr, memptr, buffer.byteLength, this.scale);
fails for some effects. For example I tried some of the demo effects from here:
Simple_Ring_Shape1.efk - works ✅
Laser01.efk- fails ❌
Laser02.efk- fails ❌
Simple_Ribbon_Parent.efk- fails ❌
Simple_Ribbon_Sword.efk- fails ❌
Simple_Sprite_FixedYAxis.efk- fails ❌
Simple_Track1.efk- works ✅
block.efk- fails ❌
I'm not sure what exactly is different on these effects, but maybe someone with Effekseer internals knowledge will know what's wrong.
These are the changes that I made to effekseer.src.js to get initial worker support:
Replace this:
// original code with new Image()
const arrayBufferView = new Uint8Array( arrayBuffer );
Promise.resolve(new Blob([arrayBufferView], { type: 'image/png' }))
.then(blob => {
return Promise.resolve(URL.createObjectURL(blob))
})
.then(url => {
const img = new Image();
img.onload = () => {
res.image = img;
res.isLoaded = true;
effect._update();
};
img.src = url;
});
With:
// use createImageBitmap()
var arrayBufferView = new Uint8Array(arrayBuffer);
Promise.resolve(new Blob([arrayBufferView], { type: 'image/png' }))
.then(blob => createImageBitmap(blob))
.then(imageBitmap => {
res.image = imageBitmap;
res.isLoaded = true;
effect._update();
}).catch(err => {
console.error('Error loading image:', err);
if (typeof onerror !== "undefined") onerror(err.message || 'error', path);
});
and replace this:
// original code with new Image()
const image = new Image();
image.onload = () => {
let converted_image = _convertPowerOfTwoImage(image);
onload(converted_image);
};
image.onerror = () => {
if (!(typeof onerror === "undefined")) onerror('not found', path);
};
image.crossOrigin = _imageCrossOrigin;
image.src = path;
with:
// use createImageBitmap()
fetch(path, { mode: 'cors' })
.then(response => {
if (!response.ok) throw new Error('not found');
return response.blob();
})
.then(blob => createImageBitmap(blob))
.then(imageBitmap => {
var converted_image = _convertPowerOfTwoImage(imageBitmap);
onload(converted_image);
})
.catch(err => {
if (typeof onerror !== "undefined") onerror(err.message || 'error', path);
});