Texture created from off screen canvas is black

I’m trying to create a texture from a canvas image and I’m getting some odd results.

I have a plane entity in a scene with this code attached. The canvas element is created in another function and looks correct. Once it’s drawn the following code is called. Currently the plane turns black. There’s several steps I’m not sure about specifically how what to pass to the pc.Texture() command. Below I’m pass the canvas element itself.

 var canvas= document.getElementById('textureCanvas');
    
    var texture = new pc.Texture(canvas, {
        width: 192,
        height: 192,
       format: pc.PIXELFORMAT_R8_G8_B8
    });
    

    //add it to the registry and assign
    var dynamicAsset = new pc.Asset("portraitSheet", "texture", {
    url: "", // not sure exactly what you want to put in here, empty string might be fine
    });
    dynamicAsset.resource = texture;
    dynamicAsset.loaded = true;

    dynamicAsset.on("error",  (message) =>{
    console.log(message);
    });
    
    this.app.assets.add(dynamicAsset);

    console.log("putting this bad boy on the plane");

    var material = this.entity.model.material;
    material.diffuseMap = dynamicAsset.resource;
    material.update();
    
    dynamicAsset.on("load", (asset)=> {
        //load isn't called
    });

Hi @noah_mizrahi,

You can take a look at following project on how to grab the image data from an HTML canvas and create a pc.Texture:

https://playcanvas.com/project/605131/overview/capturing-screenshot-from-camera

Thanks for the project link. I don’t see an example here of how to create a texture asset once the image has been drawn to the canvas. I wasn’t clear in my previous post that I have a canvas with a drawn image. Here is the rest of my example:

//create a canvas element
var tempCanvas = document.createElement('canvas');
tempCanvas.id="textureCanvas";
document.body.appendChild(tempCanvas);

///draw multiple images into the canvas
var ctx = document.getElementById('textureCanvas').getContext('2d');
    var img = new Image();
    img.onload = ()=> {
    ctx.drawImage(img, 0, 0, 64, 64, 0, 0, 64, 64);
    };
    img.src = this.portraitArray[0];

    var img2 = new Image();
    img2.onload = ()=>  {
    ctx.drawImage(img2, 0, 0, 64, 64, 64, 0, 64, 64);
    };
    img2.src = this.portraitArray[1];
    
    var img3 = new Image();
    img3.onload = ()=>  {
    ctx.drawImage(img3, 0, 0, 64, 64, 128, 0, 64, 64);
    };
    img3.src = this.portraitArray[4];
    
    var img4 = new Image();
    img4.onload = ()=>  {
    ctx.drawImage(img4, 0, 0, 64, 64, 0, 64, 64, 64);
    };
    img4.src = this.portraitArray[3];
    
    var img5 = new Image();
    img5.onload = ()=>  {
    ctx.drawImage(img5, 0, 0, 64, 64, 64, 64, 64, 64);
    };
    img5.src = this.portraitArray[5];
    
    var img6 = new Image();
    img6.onload = ()=>  {
        console.log("drawing image");
    ctx.drawImage(img6, 0, 0, 64, 64, 128, 64, 64, 64);

//create a texture after the canvas is drawn
        setTimeout( this.createTexture.bind(this), 1000 );

    };
    img6.src = this.portraitArray[2];
   
};

DynamicTexture.prototype.createTexture = function(dt) {
    console.log("creating texture");
    //create dynamic texture
    //
    var canvas= document.getElementById('textureCanvas');
    
    var texture = new pc.Texture(canvas, {
        width: 8,
        height: 8,
        format: pc.PIXELFORMAT_R8_G8_B8
    });
    
    
    //add it to the registry and assign
    var dynamicAsset = new pc.Asset("portraitSheet", "texture", {
    url: "", // not sure exactly what you want to put in here, empty string might be fine
    });
    dynamicAsset.resource = texture;
    dynamicAsset.loaded = true;

    dynamicAsset.on("error",  (message) =>{
    console.log(message);
    });
    
    this.app.assets.add(dynamicAsset);

    dynamicAsset.on("load", (asset)=> {
    //load isn't called
    });
    
    var material = this.entity.model.material;
    material.diffuseMap = dynamicAsset.resource;
    material.update();`

I dug through some examples and found what I was doing wrong.

this project shows how to setSource of the texture which I wasn’t doing. I should have read all the docs.

https://playcanvas.com/editor/scene/440131

Thanks for the help!

I then encountered a “Tainted canvas” error which I solved by setting the crossorigin attribute when I draw to the canvas.

  //set cross origin when drawing to the canvas
var img6 = new Image();
    img6.setAttribute('crossorigin', 'anonymous'); // works for me
    img6.src = "https://i.imgur.com/bPXiQOe.png";

    img6.onload = ()=>  {
        console.log("drawing image");
    ctx.drawImage(img6, 0, 0, 64, 64, 128, 64, 64, 64);
        setTimeout( this.createTexture.bind(this), 1000 );

    };

//create texture from the canvas

   var canvas= document.getElementById('textureCanvas');
    const img = new Image();
    img.src = canvas.toDataURL();
    img.width = 192;
    img.height = 192;
    
    var texture = new pc.Texture(this.app.graphicsDevice, {
        width: 192,
        height: 192,
        format: pc.PIXELFORMAT_R8_G8_B8
    });
    
    texture.setSource(img);

//apply texture to material
    var material = this.entity.model.material;
    material.diffuseMap = texture;
    material.update();
2 Likes

Many thanks @noah_mizrahi for sharing your solution.