Postprocessing with normal and depth information


We are currently experimenting with Cesium's rendering capabilities and we are having a few problems.
We successfully integrated the postprocessing-hook branch to our project, but in order to make the most out of it, we need to gather additional information from the initial shader pass : normals, depth, etc. What would be the best way to transfer this information to the postprocessing shaders ?


Jérémy Gaillard

Hi Jérémy,

Sounds interesting. What kind of project are you working on?

If you want depth for just the globe/terrain, check out czm_globeDepthTexture:

If you want the depth for the entire scene, you’ll have to change Cesium’s renderer (it will be similar to czm_globeDepthTexture). The initial work we did on shadows may be useful. You’ll want to take a similar approach with normals (I would use multiple render targets when available).

This will be a fair amount of work and you’ll have to handle Cesium’s use of multiple frustums. Eventually, depth and normal buffers will be part of Cesium, but if you want to go for it now, here’s a few resources:


Thanks for the pointers.

If you want the depth for the entire scene, you'll have to change Cesium's renderer (it will be similar to czm_globeDepthTexture). The initial work we did on shadows may be useful. You'll want to take a similar approach with normals (I would use multiple render targets when available).

For ou purpose (first a "cell shader":Cel shading - Wikipedia and then ssao maybe) we need the depth and normals of the entire scene, not just the globe.

I was considering adding a loop for a least a second render pass just around the loop over frustrums in Scene.executeCommands and overriding the framebuffer of the draw commands to compute the necessary framebuffer. I would also need to override the appearances for the normal+depth render pass, but I don't see yet how to do that last bit cleanly.

Does that make sense for you ?

I see that the approach you took for shadow casting is different, but in our cases we don't need a second point of view on the scene.

(I would use multiple render targets when available)

That is another approach I was considering. If we could use 3 render targets, then no need to loop, we could use the normal+depth additional textures in the custom post-process as it stands and do what needs be done.

As a first step, for the cell-shading) only our 'special' appearance would need to know about the additional render targets. It would also be less intrusive and faster to implement I guess. All shader would write to gl_FragData[0] without even knowing that there are more RT to render to, until we want all primitives to write their depth an normals there.

Any thought on that ?



For SSAO, check out this (very old) branch:

I see that the approach you took for shadow casting is different, but in our cases we don’t need a second point of view on the scene.

This is the general-purpose approach we will take for shadow maps, environment maps, and laying out g-buffers when MRT is not supported. You are welcome to bend the renderer however you need in the meantime, but I just want to give you a heads up that merging in future versions of Cesium may be challenging as we update the renderer for shadows and WebGL 2.


Trying to use czm_globeDepthTexture in a custom primitive, getting:

TypeError: Cannot read property '_target' of undefined
TypeError: Cannot read property '_target' of undefined
    at UniformSampler.set
    at ShaderProgram._setUniforms
    at continueDraw
    at Context.draw
    at DrawCommand.execute
    at executeCommand
    at executeTranslucentCommandsSortedMRT
    at OIT.executeCommands
    at scene._executeOITFunction
    at executeCommands

    gl_FragColor.rgb= texture2D(czm_globeDepthTexture, vec2(0.,0.)).xxx;

Do you have support for depth textures? Check Context.depthTexture in Cesium or check for WEBGL_depth_texture at

Also, to avoid haveing to also require floating point textures, the depth is packed into an RGBA texture. To get the floating point depth:

float depth = czm_unpackDepth(texture2D(czm_globeDepthTexture, textureCoordinates));

Report says I have support for depth textures, I'd hope as I've a GTX980.
Also the issue is not from being in the translucent pass, opaque pass is same result.

I just realized that the globed depth texture is disabled by default. It’ll be enabled by default in the future when we need it, for example GroundPrimitive texture coordinates. You can enable it with the scene.copyGlobeDepth = true.

Works C:

Sorry for bumping an old thread but I just tried to use czm_globeDepthTexture and I get the error mentioned earlier (Cannot read properties of undefined (reading ‘_target’)).

Scene object does not appear to had a copyGlobeDepth property anymore.

Is it still possible to use the globeDepthTexture only?




I believe that it is still possible to use czm_globeDepthTexture. However, czm_globeDepthTexture only accesses the depth of the globe/terrain.


Terrain depth is exactly what I need. However, I get the aforementioned exception when trying to sample the texture.


Understood :+1: To be candid, I am somewhat unfamiliar with this part of our API. I can take some time later this week or possibly early next week to investigate this further. Two things in the meantime.

  1. Input from the rest of the CesiumJS community would be very helpful here! Any insights into this issue would be appreciated
  2. Have you tried out the suggestion outlined by @Dan_Bagnell? scene.copyGlobeDepth = true

I really appreciate your patience regarding this issue. I’m sorry that I can’t provide a quick and easy solution.


Hi @sam.rothstein, thanks for the support. Yes I tried to set scene.copyGlobeDepth = true but as far as I can see, the scene object does not have such a property anymore.

Just to update this thread, I now appear to be able to use the czm_globeDepthTexture uniform without any exception (and without setting copyGlobeDepth) which I still fail to explain at the moment. I will comment again if I manage to understand what happened. Thank you for your help.

@Xavier_Tassin I’m seeing the same problem if czm_globeDepthTexture is accessed before any other commands have been issued. I put together a sancastle showing that behavior. If you set the timeout to 0 it will crash, but if you set it to 3000 that’s enough time for globe tiles to stream in.

I was stepping through a code bit to understand what was going on and it looks executeCopyDepth is only called if there is at least one frustum (there will be zero frustums if there are no commands). So something needs to be rendering already in order to use czm_globeDepthTexture.

1 Like

Thank you @sean_lilley: it makes sense now. I’ve had random exceptions depending on the time it takes for the Globe to render. Is this something that will be considered a bug and will be addressed (by feeding an empty texture until Globe is ready) or shall I just implement a work around ? (A timeout seems unreliable but waiting for some “ready” event could be a possibility)

One workaround would be to wait until globe tiles are loaded like in this sandcastle. I’ll have to think about the proper fix in the code.

1 Like

Thank you Sean. I use the tileLoadProgressEvent in order to attach post processing stage as soon as “some” tiles have been loaded. This is enough to make czm_globeDepthTexture available.

However, the uniform becomes unavailable again (and Cesium throws an exception) as soon as the depth buffer is unused/empty (when nothing is in view while looking upward for example). I know this is a rare occurrence when using Cesium “normally” but it can be a problem for, say, a flight simulator :wink:

1 Like