Pixel depth offset

Godot Version

4.2.1

Question

I’m trying to push each pixel’s depth back with an offset value, like some hair shaders do.

Is it possible to use DEPTH in Godot’s fragment shader like Pixel Depth Offset in Unreal / SV_DEPTH in Unity?

Just writing to DEPTH doesn’t work without disabling depth_draw.
So i tried reading the depth texture and setting DEPTH, then adding the code I would send to Pixel Depth Offset but I couldn’t get the correct depth even without the additional offset.

Does anyone know how to do this?
I know it’s used quite a bit for hair shaders and such, so I imagine someone has encountered this already.

are you enabling the render mode depth_draw_opaque or depth_draw_always I know you said you disabled the depth_draw and it can be modified, so I assume you tried this already?

since the camera sets the range of depth with the near and far render distance, are you setting appropriate values in the 0…1 depth scale?

also there is a caveat to setting the DEPTH variable

There is also some interesting usecase here in the docs, but I’m not sure it applies to your usecase. They retrieve a depth texture of the screen and get a transformed version of the depth texture into linear space by multiplying it with the inverse projection.

1 Like

Normally in my shader when initially defining DEPTH I use depth_draw_always, and I’m trying to just add an offset to that.
But when doing so when depth_draw_always is defined I get an error, which is what the mentioned caveat is I think, so not defining any depth mode in the render mode line and setting the DEPTH manually (with the depth texture I suppose) seems to be the only option.

I tried the code used in the doc before, but there are no examples I could find of actually writing to DEPTH. When using the depth texture and passing it back into DEPTH it doesn’t get me the same results as defining depth_draw_always.

i found this old issue and they modifed the depth with this

shader_type spatial;
render_mode depth_draw_always;

void fragment() {
  vec4 ndc = PROJECTION_MATRIX * vec4(VERTEX, 1.0);
 DEPTH = (ndc.z / ndc.w);
}

The output is strange though. it seems to work a little different using compatibility render as well.
maybe forward + has an issue.

2 Likes

if i use DEPTH = FRAGCOORD.z it seems to work too but if a modify the value it goes black. to me it seems like there is some DEPTH branch somewhere internal not being updated and it all goes to 0.0.

vec4 ndc = PROJECTION_MATRIX * vec4(VERTEX, 1.0);
DEPTH = ndc.z / ndc.w;

These lines do actually seem to work for me (using Forward +) to get a working depth, BUT writing to ALPHA then messes with the output again (same goes for DEPTH = FRAGCOORD.z).

I’m going to spend a day or two trying to work with it and check the branches then post my findings, thanks for the suggestions so far!

1 Like

As far as getting a working depth value, DEPTH = FRAGCOORD.z works as expected for me. I found that setting depth draw modes in the render mode line still works while writing to DEPTH in the frag shader, and by defining one of those modes I solved the ALPHA issue in my previous post.

Ironically the code I’m trying to add as an offset is a PS1 like depth error effect, which I’m not sure works since I’m currently trying to port it over. So I might have to try something easier in order to figure out how to add the offset correctly.

I found that clamping the DEPTH input to 0.99 will allow you to set a workable offset that won’t clip the entire mesh. Though I noticed inconsistencies, adding by small increments will push the depth backwards, subtracting pulls it forwards.

DEPTH = clamp(FRAGCOORD.z + 0.01, 0.00, 0.99);

It seems to work allright when setting everything manually but it’s not perfect.

float offset = 0.01;
if (offset != 0.0){
DEPTH = clamp(FRAGCOORD.z + offset, 0.00, 0.99);
}
else{
DEPTH = clamp(FRAGCOORD.z, 0.00, 0.99);
}

I have to work on it some more but it looks to be possible at least.

I feel like Godot needs an official implementation for pixel depth offset. As far as I know changing the depth only changes the clipping. It does not affect stuff like lighting (shadow sampling).

An official implementation would be nice, but I have found the Pixel Depth Offset in Unreal to be pretty cryptic as I haven’t figured out yet what it exactly does. In particular what it does that’s different from other engines, since I haven’t been able to pull it off in Unity either.

For starters the Pixel Depth Offset takes very large numbers instead of directly 0 to 1, and the actual depth data is -1 to 1 in Unreal anyway I believe.

The shader i’m trying to port does only involves clipping though so I think that should be possible.

As far as I know, the pixel depth offset in ue offsets the depth of said pixel with the input amount in unreal units (cm) in world space. It can be used for mesh blending with dithering and taa, and for better lighting on impostors.

In godot I want to use depth offset to make an imposter (image of 3d mesh) look more like the 3d mesh by faking the depth with a texture. The biggest problem I’m having right now is that self shadowing looks wrong on impostors. As far as I know the only fix is to set up pixel depth offset.

It sounds like this PR addresses that: Add `LIGHT_VERTEX` to fragment shader by basicer · Pull Request #91136 · godotengine/godot · GitHub

1 Like

Thanks! I’ve seen that pr in the change list for dev 6, but thought it was per vertex and not per pixel (because of the name), and that it wouldn’t be useful for billboards because they only consist of 4 vertices. The docs don’t say it’s per pixel either.

For anyone here in the future: this is how you can get pixel depth offset for lighting. (Godot 4.3 dev6 and newer)

void fragment() {
	LIGHT_VERTEX += vec3(0.,0.,offset);
}