[SOLVED] Can't read from a texture locked with TEXTURELOCK_READ, it doesn't seem to do anything

Hey all, I need some help with an issue I’ve noticed when trying to read color data from textures that I’ve rendered into.

I have two textures I’m rendering into using them as render targets and then I want to take the difference between them like in the code bellow:

  postRender() {
    if (this.snapshot) {
      this.snapshot = false;

      const baseMeshBuffer = this.baseMeshTexture.lock({ mode: pc.TEXTURELOCK_READ });
      const variantMeshBuffer = this.variantMeshTexture.lock({ mode: pc.TEXTURELOCK_READ });

      if (!this.deformationMap) {
        this.deformationMap = new pc.Texture(this.app.graphicsDevice, {
          width: 512,
          height: 512,
          format: pc.PIXELFORMAT_R8_G8_B8_A8,
          addressU: pc.ADDRESS_CLAMP_TO_EDGE,
          addressV: pc.ADDRESS_CLAMP_TO_EDGE,
          mipmaps: false,
          minFilter: pc.FILTER_LINEAR,
          magFilter: pc.FILTER_LINEAR,
        });
      }

      const deformationMapBuffer = this.deformationMap.lock();
      let pos = 0;
      for (let x = 0; x < 512; x++) {
        for (let y = 0; y < 512; y++) {
          pos = (x * 512 + y) * 4;
          deformationMapBuffer[pos] = variantMeshBuffer[pos] - baseMeshBuffer[pos];
          deformationMapBuffer[pos + 1] = variantMeshBuffer[pos + 1] - baseMeshBuffer[pos + 1];
          deformationMapBuffer[pos + 2] = variantMeshBuffer[pos + 2] - baseMeshBuffer[pos + 2];
          deformationMapBuffer[pos + 3] = 255.0;
        }
      }

      this.baseMeshTexture.unlock();
      this.variantMeshTexture.unlock();
      this.deformationMap.unlock();
    }
  }

The thing is the returned buffer for both baseMeshTexture and variantMeshTexture is filled with zeroes even though I can clearly see colors inside the textures when I’m rendering to screen:

Looking at the source code it seems that pc.TEXTURELOCK_READ doesn’t do anything and it is misleading.

What’s the proper way of reading pixels from a VRAM texture? it seem that the only way would be with pc.GraphicsDevice.readPixels but it’s not clear how to use it.
I’ve noticed some code in the engine (picker.js) that might be what I’m looking for but I’m not sure:

        // Ready the device for rendering to the pick buffer
        device.setRenderTarget(this.renderTarget);
        device.updateBegin();

        const pixels = new Uint8Array(4 * width * height);
        device.readPixels(x, y, width, height, pixels);

        device.updateEnd();
1 Like

Hi @heretique,

Yes, correct, you should be using readPixels, not aware of any other way. Here is how I do this:

const readPixelsFromTexture = function (texture) {

    const app = this.app;

    // make a framebuffer
    const gl = app.graphicsDevice.gl;
    const fb = gl.createFramebuffer();

    // make this the current frame buffer
    gl.bindFramebuffer(gl.FRAMEBUFFER, fb);

    // attach the texture to the framebuffer.
    gl.framebufferTexture2D(
        gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0,
        gl.TEXTURE_2D, texture._glTexture, 0);

    // read the pixels
    const pixels = new Uint8Array(texture.width * texture.height * 4);
    gl.readPixels(0, 0, texture.width, texture.height, gl.RGBA, gl.UNSIGNED_BYTE, pixels);

    // Unbind the framebuffer
    gl.bindFramebuffer(gl.FRAMEBUFFER, null);

    return pixels;
};
3 Likes

we have a ticket to improve this, but have not had time to work on it yet: https://github.com/playcanvas/engine/issues/2974

3 Likes

Thanks guys. @Leonidas that works for me too, I tested today with the code I posted, it’s basically doing exactly what you listed it is just wrapped underneath the pc.GraphicsDevice API.

1 Like