device.getRenderTarget always returns null

According to documentation, we can get renderTarget, but it always null.

So my question is - how can I get render target? Camera has null too.
Okay, I can set it by hands, but is there a default one?

And, the second question. Is it acceptable to get colorBuffer from renderTarget and pass it into a shader as a texture. Will it slow down my performance a lot?

And how can I do that?

I can’t even lock inputTarget.colorBuffer in post-effect in order to get it’s pixels.

If I do


in pc.PostEffect.prototype.render I get just a black screen. Bug?

Okay, I see, nobody loves me here =\

1 Like

I’d be glad to help but I don’t know about it, and I’m interested too to see a solution for this.


I guess, core team guys could help with that, but I think they are too busy with new features which were announced last week. Probably, this features even can solve my problem as well.

That’s what I hope for.

Looking at the documentation, if the renderTarget is null, than the backbuffer is the render target.

What are you looking to do?

I want to get last frame as a texture. is it possible? How can I get backBuffer?

You would have to set the renderTarget as a new pc.RenderTarget, render the frame and unset it effectively.

The pc.Picker has an example of this in getSelection (you may have to look at an older revision as GitHub has been updated to use the layer system)

Older revision below:

pc.extend(pc, function () {

    function sortDrawCalls(drawCallA, drawCallB) {

        if (drawCallA.layer === drawCallB.layer) {
            if (drawCallA.drawOrder && drawCallB.drawOrder) {
                return drawCallA.drawOrder - drawCallB.drawOrder;

        return drawCallB.key - drawCallA.key;

     * @constructor
     * @name pc.Picker
     * @classdesc Picker object used to select mesh instances from screen coordinates.
     * @description Create a new instance of a Picker object
     * @param {pc.GraphicsDevice} device Graphics device used to manage internal graphics resources.
     * @param {Number} width The width of the pick buffer in pixels.
     * @param {Number} height The height of the pick buffer in pixels.
     * @property {Number} width Width of the pick buffer in pixels (read-only).
     * @property {Number} height Height of the pick buffer in pixels (read-only).
     * @property {pc.RenderTarget} renderTarget The render target used by the picker internally (read-only).
    var Picker = function(device, width, height) {
        this.device = device;
        this.library = device.getProgramLibrary();

        this.pickColor = new Float32Array(4);

        this.scene = null;
        this.drawCalls = [ ];

        this.clearOptions = {
            color: [1, 1, 1, 1],
            depth: 1,
            flags: pc.CLEARFLAG_COLOR | pc.CLEARFLAG_DEPTH
        this.resize(width, height);

        this._ignoreOpacityFor = null; // meshInstance

     * @function
     * @name pc.Picker#getSelection
     * @description Return the list of mesh instances selected by the specified rectangle in the
     * previously prepared pick buffer.The rectangle using top-left coordinate system.
     * @param {Number} x The left edge of the rectangle
     * @param {Number} y The top edge of the rectangle
     * @param {Number} [width] The width of the rectangle
     * @param {Number} [height] The height of the rectangle
     * @returns {pc.MeshInstance[]} An array of mesh instances that are in the selection
     * @example
     * // Get the selection at the point (10,20)
     * var selection = picker.getSelection(10, 20);
     * // Get all models in rectangle with corners at (10,20) and (20,40)
     * var selection = picker.getSelection(10, 20, 10, 20);
    Picker.prototype.getSelection = function (x, y, width, height) {
        var device = this.device;

        if (typeof x === 'object') {
            // #ifdef DEBUG
            console.warn("Picker.getSelection:param 'rect' is deprecated, use 'x, y, width, height' instead.");
            // #endif

            var rect = x;
            x = rect.x;
            y = rect.y;
            width = rect.width;
            height = rect.height;
        } else {
            y = this._pickBufferTarget.height - (y + (height || 1));

        width = width || 1;
        height = height || 1;

        // Cache active render target
        var prevRenderTarget = device.renderTarget;

        // Ready the device for rendering to the pick buffer

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


        // Restore render target

        var selection = [];

        for (var i = 0; i < width * height; i++) {
            var r = pixels[4 * i + 0];
            var g = pixels[4 * i + 1];
            var b = pixels[4 * i + 2];
            var index = r << 16 | g << 8 | b;
            // White is 'no selection'
            if (index !== 0xffffff) {
                var selectedMeshInstance = this.drawCalls[index];
                if (selection.indexOf(selectedMeshInstance) === -1) {

        return selection;

     * @function
     * @name pc.Picker#prepare
     * @description Primes the pick buffer with a rendering of the specified models from the point of view
     * of the supplied camera. Once the pick buffer has been prepared, pc.Picker#getSelection can be
     * called multiple times on the same picker object. Therefore, if the models or camera do not change
     * in any way, pc.Picker#prepare does not need to be called again.
     * @param {pc.Camera} camera The camera used to render the scene, note this is the CameraNode, not an Entity
     * @param {pc.Scene} scene The scene containing the pickable mesh instances.
    Picker.prototype.prepare = function (camera, scene) {
        var device = this.device;

        this.scene = scene;

        // Cache active render target
        var prevRenderTarget = device.renderTarget;

        // Ready the device for rendering to the pick buffer
        device.setViewport(0, 0, this._pickBufferTarget.width, this._pickBufferTarget.height);
        device.setScissor(0, 0, this._pickBufferTarget.width, this._pickBufferTarget.height);

        // Build mesh instance list (ideally done by visibility query)
        var i;
        var mesh, meshInstance, material;
        var type;
        var shader;
        var scope = device.scope;
        var modelMatrixId = scope.resolve('matrix_model');
        var boneTextureId = scope.resolve('texture_poseMap');
        var boneTextureSizeId = scope.resolve('texture_poseMapSize');
        var poseMatrixId = scope.resolve('matrix_pose[0]');
        var pickColorId = scope.resolve('uColor');
        var projId = scope.resolve('matrix_projection');
        var viewProjId = scope.resolve('matrix_viewProjection');
        var opacityMapId = scope.resolve('texture_opacityMap');
        var alphaTestId = scope.resolve('alpha_ref');
        var nineOuterScaleId = scope.resolve('outerScale');

        var wtm = camera._node.getWorldTransform();
        var projMat = camera.getProjectionMatrix();
        var viewMat = wtm.clone().invert();
        var viewProjMat = new pc.Mat4();
        viewProjMat.mul2(projMat, viewMat);


        // copy scene drawCalls
        this.drawCalls = scene.drawCalls.slice(0);
        // sort same as forward renderer

        for (i = 0; i < this.drawCalls.length; i++) {
            if (this.drawCalls[i].command) {
            } else {
                if (!this.drawCalls[i].pick) continue;
                meshInstance = this.drawCalls[i];
                mesh = meshInstance.mesh;
                if (! mesh) continue;
                material = meshInstance.material;

                type = mesh.primitive[pc.RENDERSTYLE_SOLID].type;
                var isSolid = (type === pc.PRIMITIVE_TRIANGLES) || (type === pc.PRIMITIVE_TRISTRIP) || (type === pc.PRIMITIVE_TRIFAN);
                var isPickable = (material instanceof pc.StandardMaterial) || (material instanceof pc.BasicMaterial);
                if (isSolid && isPickable) {


                    if (meshInstance.skinInstance) {
                        if (device.supportsBoneTextures) {
                            var w = meshInstance.skinInstance.boneTexture.width;
                            var h = meshInstance.skinInstance.boneTexture.height;
                            boneTextureSizeId.setValue([w, h]);
                        } else {

                    if (material.opacityMap) {
                        alphaTestId.setValue(meshInstance===this._ignoreOpacityFor? 0 : material.alphaTest);

                    if (meshInstance.nineSlice) {

                    this.pickColor[0] = ((i >> 16) & 0xff) / 255;
                    this.pickColor[1] = ((i >> 8) & 0xff) / 255;
                    this.pickColor[2] = (i & 0xff) / 255;
                    this.pickColor[3] = 1;

                    shader = meshInstance._shader[pc.SHADER_PICK];
                    if (!shader) {
                        shader = this.library.getProgram('pick', {
                                skin: !!meshInstance.skinInstance,
                                screenSpace: meshInstance.screenSpace,
                                nineSlice: meshInstance.nineSlice,
                                opacityMap: !!material.opacityMap,
                                opacityChannel: material.opacityMap? (material.opacityMapChannel || 'r') : null
                        meshInstance._shader[pc.SHADER_PICK] = shader;

                    device.setVertexBuffer((meshInstance.morphInstance && meshInstance.morphInstance._vertexBuffer) ?
                        meshInstance.morphInstance._vertexBuffer : mesh.vertexBuffer, 0);

        device.setViewport(0, 0, device.width, device.height);
        device.setScissor(0, 0, device.width, device.height);

        // Restore render target

     * @function
     * @name pc.Picker#resize
     * @description Sets the resolution of the pick buffer. The pick buffer resolution does not need
     * to match the resolution of the corresponding frame buffer use for general rendering of the
     * 3D scene. However, the lower the resolution of the pick buffer, the less accurate the selection
     * results returned by pc.Picker#getSelection. On the other hand, smaller pick buffers will
     * yield greater performance, so there is a trade off.
     * @param {Number} width The width of the pick buffer in pixels.
     * @param {Number} height The height of the pick buffer in pixels.
    Picker.prototype.resize = function (width, height) {
        var colorBuffer = new pc.Texture(this.device, {
            format: pc.PIXELFORMAT_R8_G8_B8_A8,
            width: width,
            height: height,
            mipmaps: false,
            minFilter: pc.FILTER_NEAREST,
            magFilter: pc.FILTER_NEAREST
        this._pickBufferTarget = new pc.RenderTarget(this.device, colorBuffer, { depth: true });

    Object.defineProperty(Picker.prototype, 'renderTarget', {
        get: function() { return this._pickBufferTarget; }

    Object.defineProperty(Picker.prototype, 'width', {
        get: function() { return this._pickBufferTarget.width; }

    Object.defineProperty(Picker.prototype, 'height', {
        get: function() { return this._pickBufferTarget.height; }

    return {
        Picker: Picker

Yes, but it renders scene one more time. At least, specified objects.

But I think it should be possible to get already rendered scene. It sounds quite simple - just copy texture from renderTarget before clean up and that’s all.

I can get colorBuffer (that texture) in post-effect. But when I lock-unlock it, screen gets black. I don’t know why.

There is another method where you set the flag for preserving the drawing buffer in rendering options. Project example of capturing a screenshot here:

This may have an impact on performance though.

Nope, doesn’t help.

I found a dirty way to do that directly via webGL render context.

var pixels = null;

var gl =;
gl.readPixels(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight, gl.RGBA, gl.UNSIGNED_BYTE, pixels);
if (pixels) {
    var pixels = this.transparentTexture.lock();

But I guess it harms performance a lot, maybe even more than double render… I don’t know how can I check it?

Anyway, Im gonna wait for pc.Layer because now I can’t control draw order.

My idea was to render whole scene without one mesh. Then get current renderBuffer as a texture and pass it into a shader, and then render this left mesh.