Depth Buffer in material shader

I try to access the depth buffer in a shader, but sadly since the change to the layer system it is not possible to use ‘this.entity.camera.camera.requestDepthMap();’ to autogenerate the depth texture (as in this example).

There is also a thread discussing post processing effects using depth, sadly without a good solution.

Is there any documentation / example showing how to generate and access the depth texture(for the world layer or any other)?

Did you find a way to access the depth buffer in a shader? I need it too!

Hmm. I just consulted the engine source. Seems you can do something like:

var RenderDepth = pc.createScript('renderDepth');

// initialize code called once per entity
RenderDepth.prototype.initialize = function() {
    // Request rendering of the scene to a depth texture
    var depthLayer = this.app.scene.layers.getLayerById(pc.LAYERID_DEPTH);
    depthLayer.incrementCounter();
    
    this.on('enable', function () {
        depthLayer.incrementCounter();
    });
    this.on('disable', function () {
        depthLayer.decrementCounter();
    });
};

And then, if your shader, you can use the depth map with:

uniform sampler2D uDepthMap;

Doesn’t feel very ‘public API’ to me. :wink: But hey, feel free to experiment!

1 Like

Is there also any example of accessing the depth buffer using the new Post Effects system?

Hi @will, your script works just fine, making the uDepthMap uniform available everywhere.

Though it works only while rendering in WebGL2, in WebGL1 it renders in full white. Any idea why?

Here is a small test project (depth is raw, not linearized or unpacked):

https://playcanvas.com/project/642131/

WebGL2:

Digging further on this on webGL1:

  • if you disable the renderDepth script
  • enable realtime shadows on the main scene Light

You can access the shadowmaps depthMap:

(this is unpacked for clarity)

I seem to have stumbled onto this issue as well, playing around with the demo project from a water tutorial:
https://playcanvas.com/project/533435/overview/toon-water--tuts-tutorial

The project uses the depth map to generate a foam line around objects in the water, but that doesn’t work in the editor with the current version of PlayCanvas, only in the published project.
I tried forking the project, adding Will’s RenderDepth script to the scene, but to no avail. Any ideas what else is missing to get the foam line to work again? It appears that accessing the depth map has become a real hazzle – do we have any updated documentation that explains how this is supposed to work?

The GitHub issue posted above, although still open, provides a resolution for the WebGL1 bug.

Here is a sample project implementing that fix:

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

I may have misunderstood something here, but the sample project you’re linking to simply renders a white screen when I press play. I tried in both Chrome and Firefox, both updated to the latest version on a PC running Windows 10.

Yes, because the depth map isn’t linearized and is “unpacked” differently in WebGL2. So don’t test it visually but inside your shader operations.

Here are the methods you should be using on each case:

Hmm… the water tutorial project already appears to include these methods in the fragment shader.
https://playcanvas.com/editor/code/657274?tabs=26543414

This is the project I forked, adding the renderDepth script. I made sure to move it to the top of the script loading order and I tried attaching it to the camera, but nothing appears to make any difference. The foam lines are still not rendering.
https://playcanvas.com/editor/scene/853908

Could you post a link of what is the expected output of this project?

Sure, the tutorial project has a compiled version:

From what I see the shaders of that project have been built taking into account only WebGL1 deployment (check how the depth map is unpacked in the DepthVisualize.js script).

To use this project in WebGL2 you will have to update that part definitely. The depth fix definitely works, change the initialize method in the DepthVisualize.js script to this (and also make sure you add the WebGL1 fix from the Github issue to your project):

DepthVisualize.prototype.initialize = function() {
    //this.entity.camera.camera.requestDepthMap();
    var depthLayer = this.app.scene.layers.getLayerById(pc.LAYERID_DEPTH);
    depthLayer.incrementCounter();    
    
    this.antiCacheCount = 0; // To prevent the engine from caching our shader so we can live-update it 
    
    this.SetupDepthViz();
};

And launch the project in WebGL1 and you will see the foam rendering:

Yes, you’re right. I added the depth fix to water.js and the foam is rendering in WebGL1.

Also, I turned off the depthVisualize script that’s used on the camera. In the tutorial, that script just illustrates what the depth render looks like, it’s not used for the actual water.

So, the water fragment shader does include the methods you posted above. I thought they were supposed to fix the depth render under WebGL2 since they have GL2 defines?
Shouldn’t this work?

#ifdef GL2
    float linearizeDepth(float z) {
        z = z * 2.0 - 1.0;
        return 1.0 / (camera_params.z * z + camera_params.w);
    }
#else
    #ifndef UNPACKFLOAT
    #define UNPACKFLOAT
    float unpackFloat(vec4 rgbaDepth) {
        const vec4 bitShift = vec4(1.0 / (256.0 * 256.0 * 256.0), 1.0 / (256.0 * 256.0), 1.0 / 256.0, 1.0);
        return dot(rgbaDepth, bitShift);
    }
    #endif
#endif

// Retrieves rendered linear camera depth by UV
float getLinearScreenDepth(vec2 uv) {
    #ifdef GL2
        return linearizeDepth(texture2D(uDepthMap, uv).r) * camera_params.y;
    #else
        return unpackFloat(texture2D(uDepthMap, uv)) * camera_params.y;
    #endif
}

From what I see when this project compiles its shaders on runtime the GL2 flag isn’t respected. Even though it runs on a WebGL2 context the shaders always fallback on the else branch of the GL2 defines.

I am not sure why, don’t see anything wrong on the project settings with a first glance, nor something I’ve encountered before. Try submitting an issue about this on the engine repo in case it is a bug to be investigated.

As a temporary workaround you could write two versions of the shader, one for WebGL1 and one for WebGL2 and load the correct file based on this property:

this.app.graphicsDevice.webgl2
1 Like

Ah, thank you very much – that workaround did the trick for now.
I’ll submit a bug report. Seems very strange that the defines aren’t working as intended.

1 Like

I’m back at work now and back on a Mac. Unfortunately, the Mac reports that it’s running Web GL 2, but the shader doesn’t work :frowning: I tested on both Firefox and Chrome. The shader does work if I force it to run Web GL 1.

I guess Macs have general issues with Web GL 2, it’s not just a Safari thing…