Accessing Depth Buffer of a Render Target?

Hello all. I’ve been beating my head against the brick wall for a few days now and wondered if I could get some pointers.

Example project:
https://playcanvas.com/editor/scene/1029149

I’m trying to access the depth buffer of a render target but it always seems to come out black.

I’m requesting the buffer like this:

    this.texture = new pc.Texture(this.app.graphicsDevice, {
        width: this.app.graphicsDevice.width,
        height: this.app.graphicsDevice.height,
        format: pc.PIXELFORMAT_R8_G8_B8_A8,
        autoMipmap: true,
        minFilter: pc.FILTER_LINEAR,
        magFilter: pc.FILTER_LINEAR,
        addressU: pc.ADDRESS_CLAMP_TO_EDGE,
        addressV: pc.ADDRESS_CLAMP_TO_EDGE
    });
    
    this.depthBuffer = new pc.Texture(this.app.graphicsDevice, {
        width: this.app.graphicsDevice.width,
        height: this.app.graphicsDevice.height,
        format: pc.PIXELFORMAT_DEPTH,
        mipmaps: false,
        addressU: pc.ADDRESS_CLAMP_TO_EDGE,
        addressV: pc.ADDRESS_CLAMP_TO_EDGE
    });
    
    this.renderTarget = new pc.RenderTarget(this.app.graphicsDevice, this.texture, { depth: true, depthBuffer: this.depthBuffer });

In the scene there are 3 cubes, with the red and the blue ones set to render on the “RenderTarget” layer. The camera to draw these is being created in rendertarget-depthbuffer.js.

The two images in the top-left corner should show the render target texture and depth buffer, but at least on my end, the depth buffer stays black no matter what I do. Am I missing something stupidly obvious?

Thanks for reading.

1 Like

Hi @AshMobilePie and welcome,

That’s interesting, I’ve checked your project and you’ve done everything by the book. Not sure what’s the problem, would you like to post this in the engine repo?

The project is simple to reproduce if it’s a bug it will be resolved promptly.

Hi, @Leonidas. Thanks.

You mean re-post it as an issue on their GitHub? I can do that, if you think it’s a bug.

I had a look through the PlayCanvas source though and render target depth buffers are being used (for the depth layer and shadow maps).

			this.defaultLayerDepth = new Layer({
				enabled: false,
				name: "Depth",
				id: LAYERID_DEPTH,
				onEnable: function () {
					if (this.renderTarget) return;
					var depthBuffer = new Texture(self.graphicsDevice, {
						format: PIXELFORMAT_DEPTHSTENCIL,
						width: self.graphicsDevice.width,
						height: self.graphicsDevice.height
					});
					depthBuffer.name = 'rt-depth2';
					depthBuffer.minFilter = FILTER_NEAREST;
					depthBuffer.magFilter = FILTER_NEAREST;
					depthBuffer.addressU = ADDRESS_CLAMP_TO_EDGE;
					depthBuffer.addressV = ADDRESS_CLAMP_TO_EDGE;
					this.renderTarget = new RenderTarget({
						colorBuffer: null,
						depthBuffer: depthBuffer,
						autoResolve: false
					});
					self.graphicsDevice.scope.resolve("uDepthMap").setValue(depthBuffer);
				},

Not really sure what the difference is, besides the scope resolving.

Yes, adding it to the engine repo may result in better visibility to the engine devs.

1 Like

I see these in the console, likely related, but without having a deeper looks I’m not sure what the problem is


[.WebGL-0x7feefd0bf400]RENDER WARNING: texture bound to texture unit 0 is not renderable. It might be non-power-of-2 or have incompatible texture filtering (maybe)?
1 Like

The answer, as we eventually worked out on GitHub, was you need to set the min and mag filters for the Depth Buffer:

    this.depthBuffer = new pc.Texture(this.app.graphicsDevice, {
        width: this.app.graphicsDevice.width,
        height: this.app.graphicsDevice.height,
        format: pc.PIXELFORMAT_DEPTH,
        mipmaps: false,
        minFilter: pc.FILTER_NEAREST,
        magFilter: pc.FILTER_NEAREST,
        addressU: pc.ADDRESS_CLAMP_TO_EDGE,
        addressV: pc.ADDRESS_CLAMP_TO_EDGE
    });

So just a simple omission on my end. Oops. :slight_smile:

4 Likes

Ah nice! Thanks for sharing @AshMobilePie.

Here is a working post effect depth buffer example for 2024: PlayCanvas 3D HTML5 Game Engine
Code:

//--------------- POST EFFECT DEFINITION------------------------//
pc.extend(pc, function () {
    // Constructor - Creates an instance of our post effect
    var ExamplePostEffect = function (graphicsDevice, vs, fs) {
        
        const vertex = `#define VERTEXSHADER\n` + pc.shaderChunks.screenDepthPS + vs;
        const fragment = pc.shaderChunks.screenDepthPS + fs;
        const shader = pc.createShaderFromCode(pc.app.graphicsDevice, vertex, fragment, 'FogShader');
        this.shader = shader;

    };

    // Our effect must derive from pc.PostEffect
    ExamplePostEffect = pc.inherits(ExamplePostEffect, pc.PostEffect);

    ExamplePostEffect.prototype = pc.extend(ExamplePostEffect.prototype, {
    
        render: function (inputTarget, outputTarget, rect) {
            var device = this.device;
            var scope = device.scope;
            scope.resolve("uColorBuffer").setValue(inputTarget.colorBuffer);
            pc.drawFullscreenQuad(device, outputTarget, this.vertexBuffer, this.shader, rect);
        }
    });

    return {
        ExamplePostEffect: ExamplePostEffect
    };
}());

//--------------- SCRIPT DEFINITION------------------------//
var PostEffectDepthShader = pc.createScript('postEffectDepthShader');


PostEffectDepthShader.prototype.initialize = function() {
    this.entity.camera.requestSceneDepthMap(true);
    const vertShader = `
        attribute vec3 vertex_position;
        varying vec2 vUv0;

        void main(void)
        {
            vec2 aPosition = vertex_position.xy;
            gl_Position = vec4(aPosition, 0.0, 1.0);
            vUv0 = (aPosition.xy + 1.0) * 0.5;
            
        }`
    const fragShader = `
        precision highp float;
        varying vec2 vUv0;

        void main() {
            float depth = getLinearScreenDepth(vUv0) * camera_params.x;
            gl_FragColor = vec4(vec3(depth), 1.0);
        }`

    var effect = new pc.ExamplePostEffect(this.app.graphicsDevice, vertShader, fragShader); 
    
    // add the effect to the camera's postEffects queue
    var queue = this.entity.camera.postEffects;
    queue.addEffect(effect);
    
    // when the script is enabled add our effect to the camera's postEffects queue
    this.on('enable', function () {
        queue.addEffect(effect, false); 
    });
    
    // when the script is disabled remove our effect from the camera's postEffects queue
    this.on('disable', function () {
        queue.removeEffect(effect); 
    });
    
    
};