How can I access the Depth Buffer in a post shader?
I found a line of code in an old post effect that should work scope.resolve(“uDepthBuffer”).setValue(this.depthMap);
But where could I find this info in the docs?
It’s not public API but this should work (although it’s subject to change!):
this.entity.camera.camera.requestDepthMap();
Call it in the initialize function of a script on your camera to request a depth map to be generated.
It is indeed. Being packed into rgba8 looks funny indeed.
OK, meanwhile I found out that I have to unpack the Depth Buffer via
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); }
I’m trying to make a simple Post Shader to visualize the Depth Pass, but I can’t seem to get it to work. Any help is greatly appreciated!
I pass a far and near variable to the shader but no matter what I set I never see any grayscale in the distance, I can only ever see the object and the background.
I’m guessing the error is somewhere in this line?
f = (depth - uFar) / (uFar - uNear);
Anyway, here goes:
//--------------- POST EFFECT DEFINITION------------------------//
pc.extend(pc, function () {
// Constructor - Creates an instance of our post effect
var vizDOFPostEffect = function (graphicsDevice, vs, fs) {
this.needsDepthBuffer = true;
// this is the shader definition for our effect
this.shader = new pc.Shader(graphicsDevice, {
attributes: {
aPosition: pc.SEMANTIC_POSITION
},
vshader: [
"attribute vec2 aPosition;",
"varying vec2 vUv0;",
"void main(void)",
"{",
" gl_Position = vec4(aPosition, 0.0, 1.0);",
" vUv0 = (aPosition.xy + 1.0) * 0.5;",
"}"
].join("\n"),
fshader: [
"precision " + graphicsDevice.precision + " float;",
"uniform sampler2D uColorBuffer;",
"uniform sampler2D uDepthBuffer;",
"uniform float uNear;",
"uniform float uFar;",
"varying vec2 vUv0;",
"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);",
"}",
"void main() {",
" float f;",
" vec4 packedDepth = texture2D(uDepthBuffer, vUv0);",
" float depth = unpackFloat(packedDepth);",
" f = (depth - uFar) / (uFar - uNear);",
" gl_FragColor = vec4(f, f, f, 1);",
"}"
].join("\n")
});
this.uNear = 0;
this.uFar = 100;
};
// Our effect must derive from pc.PostEffect
vizDOFPostEffect = pc.inherits(vizDOFPostEffect, pc.PostEffect);
vizDOFPostEffect.prototype = pc.extend(vizDOFPostEffect.prototype, {
// Every post effect must implement the render method which
// sets any parameters that the shader might require and
// also renders the effect on the screen
render: function (inputTarget, outputTarget, rect) {
var device = this.device;
var scope = device.scope;
// Set the input render target to the shader. This is the image rendered from our camera
scope.resolve("uColorBuffer").setValue(inputTarget.colorBuffer);
scope.resolve("uDepthBuffer").setValue(this.depthMap);
scope.resolve("uNear").setValue(this.near);
scope.resolve("uFar").setValue(this.far);
// Draw a full screen quad on the output target. In this case the output target is the screen.
// Drawing a full screen quad will run the shader that we defined above
pc.drawFullscreenQuad(device, outputTarget, this.vertexBuffer, this.shader, rect);
}
});
return {
vizDOFPostEffect: vizDOFPostEffect
};
}());
//--------------- SCRIPT DEFINITION------------------------//
var vizDOFPostEffect = pc.createScript('vizDOFPostEffect');
vizDOFPostEffect.attributes.add('near', {
type: 'number',
min: 0,
max: 256,
step: 1,
default: 100
});
vizDOFPostEffect.attributes.add('far', {
type: 'number',
min: 0,
max: 256,
step: 1,
default: 100
});
// initialize code called once per entity
vizDOFPostEffect.prototype.initialize = function() {
var effect = new pc.vizDOFPostEffect(this.app.graphicsDevice);
// 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);
});
this.on('attr:near', function (value, prev) {
effect.near = value;
});
this.on('attr:far', function (value, prev) {
effect.far = value;
});
};
Currently screen depth is
(gl_FragCoord.z / gl_FragCoord.w) / camera_far
0 is at near, 1 is at far. Some ranges might look barely distinguishable for the eye, and you might want to to scale/power it in some way to see better.
The formula can change in the future.
Here’s a little example of depth visualization: https://playcanvas.com/project/413550/overview/depthvisexample
really cool! thanks so much!
did you just make this, or is there somewhere I could have founds that example?
Thanks!
Just made it 5 minutes ago
really cool, so all I had to do was scale depth. nice.
@Mr_F what would be the equivalent of your sample to get it working with the new Layers system? To get access to the depthMap from a shader for a given render pass.
Any idea how to access the depthMap of a render pass?
Still unable to figure this out!
Is your renderTarget.depthBuffer
empty?
Trying to grab the depthBuffer of a render pass from a postProcess effect, using the new system.
Indeed the layer.renderTarget property of a given layer, even for the default ones (e.g. World) are empty.
Thinking I might be missing something here, it used to be super easy
this.app.scene.layers.getLayerByName("World").onPostRender = (cameraIndex) => {
// grab them by the buffer
};
Try that. But i’m almost sure it’s gonna be clear too.
You can grab the colorBuffer by doing that, thanks. Although the depthBuffer is always empty. Tried to override the clear settings for the World layer but … it is still empty.
var layerWorld = this.app.scene.layers.getLayerByName("World");
layerWorld.overrideClear = true;
layerWorld.clearDepthBuffer = false;
layerWorld.onPostRender = (cameraIndex) => {
console.log(layerWorld.renderTarget.colorBuffer); // Grabs the colorBuffer
console.log(layerWorld.renderTarget.depthBuffer); // Always empty
};
Are you sure your renderTarget is using depthBuffer though?..
Also, if you have colorBuffer it doesn’t mean that it’s not empty.
Try to read it’s pixels.
It seems no, this is from the PlayCanvas source code, application.js:
this.defaultLayerWorld = new pc.Layer({
name: "World",
id: pc.LAYERID_WORLD
});
It doesn’t create a special depth render target, like the defaultLayerDepth does. It renders directly to the screen, which is fine with me, as long as I can grab it
But the question still remains, how do we get the depth map from the World layer?
The colorBuffer is fine and is passed by default as a uniform on the postEffectPass shader.