Texture asset clone/access

image

    this.brushTex = this.brushTexture.resource;
    console.log(this.brushTex)
    console.log(this.brushTex.clone())

and no data after

let pixels = this.brushTex.lock();

What I do wrong? I need access for copy texture to other texture (drawing like a brush)

    const copyN = (source, dest, posDest, bytes) => {
        let src = source.lock();
        let dst = dest.lock();
        console.log("[]", typeof src[0], typeof dst[0])

[] undefined number if I use in source this.brushTex
Codegen texture works fine. How access to asset texture pixels?

Hi @KpoKec,

There is no clone() method on pc.Texture. Check the docs on that class on how to fill a texture with data:

// Create a 8x8x24-bit texture
var texture = new pc.Texture(graphicsDevice, {
    width: 8,
    height: 8,
    format: pc.PIXELFORMAT_R8_G8_B8
});

// Fill the texture with a gradient
var pixels = texture.lock();
var count = 0;
for (var i = 0; i < 8; i++) {
    for (var j = 0; j < 8; j++) {
        pixels[count++] = i * 32;
        pixels[count++] = j * 32;
        pixels[count++] = 255;
    }
}
texture.unlock();

https://developer.playcanvas.com/en/api/pc.Texture.html

I think his question is how to access that array buffer if a texture was added via attributes, rather than proceduarally generated. I am actually not sure myself. I tried the following:

Script.attributes.add('tex', { type: 'asset', assetType: 'texture' });
Script.prototype.initialize = function() {
    
    var texture = new pc.Texture(this.app.graphicsDevice, {
        width: 8,
        height: 8,
        format: pc.PIXELFORMAT_R8_G8_B8
    }); 
    var texture2 = this.tex.resource;
    
    var pixels = texture.lock();
    var pixels2 = texture2.lock();
    
    console.log(pixels);
    console.log(pixels2);  
};

I was expecting both to return the buffer?

Edit: @Leonidas do you know how to access the binary array buffer of the texture asset added via attributes?

2 Likes

Hmm good question, you will have to use gl.readPixels() directly on the texture colorbuffer (beware this would be super slow if you do it per frame, it stalls the GPU):

Otherwise there are some external decoders for specific formats, like this one for PNG that returns a typed array given a PNG image:

There is an issue on the repository to implement the ImageBitmap API at some point, that might help when it’s in the engine:

But this is not from asset. It’s… not optimized to use framebuffer or directly load file, instead asset.

The asset is loaded as an <img> element and it’s being uploaded to the GPU in the same format.

You will have to use some gl or canvas method to extract the pixel data, like getImageData():

I hope I am wrong though and there is an easier/faster way, I would find that very useful myself :innocent:

1 Like

I will try copy texture by simple shader and use colorbuffer to create texture and rewrite copy function to shader. I think, this is much faster and easier. But not today.

2 Likes

Edit: posted a bit early…

Thank you, @Leonidas for pointing in the right direction. I’ve made an example project to show how it can be done.

var Script = pc.createScript('script');

Script.attributes.add('tex', { type: 'asset', assetType: 'texture' });

Script.prototype.initialize = function() {
    
    var plane = this.app.root.findByName('Plane');
    var device = this.app.graphicsDevice;
    var asset = this.tex.resource;
    
    // get pixel data from the asset added via attributes
    var pixelData  = this.readPixels(asset);
    
    // 
    // you can modify pixel data here, before assigning it to the texture
    // 
    
    // generate texture
    var texture = new pc.Texture(device, {
        width: 1024,
        height: 1024,
        format: pc.PIXELFORMAT_R8_G8_B8_A8,
        // autoMipmap: true,                // adjust mip level if needed
        // minFilter: pc.FILTER_LINEAR,
        // magFilter: pc.FILTER_LINEAR,
    });
    
    // get array buffer of the generated texture
    var pixels = texture.lock();
    
    // copy pixel data to our texture
    for (var i = 0; i < pixels.length; i++) {
        pixels[i] = pixelData[i];
    }
    
    // send to VRAM
    texture.unlock();
    
    // create a standard material with the generated texture
    var material = new pc.StandardMaterial();
    material.diffuseMap = texture;
    material.update();
    
    // assign the material to the Plane model
    plane.model.material = material;
};

Script.prototype.readPixels = function(texture) {
    var target = new pc.RenderTarget({ colorBuffer: texture, depth: false});
    var data = new Uint8Array(texture.width * texture.height * 4);
    var device = texture.device;
    device.setFramebuffer(target._glFrameBuffer);
    device.initRenderTarget(target);
    device.gl.readPixels(0, 0, texture.width, texture.height, device.gl.RGBA, device.gl.UNSIGNED_BYTE, data);
    return data;
};

2 Likes

Good! Half task complete ))

1 Like

As Leonidas mentioned, this method is not really suitable for per-frame rendering, as it is slow. If you ever get to make it to copy in shader, feel free to share.

I will be happy when finded code vault for sharing most typical scripts ))

2 Likes