A small depth offset, but a large effect

I don’t seem to see the error in my test environment. I can only speculate as to what the error may be actually, there’s too many variables to consider.

I do not know what your environment has to do with this? However, when you run panda as-is, this shows up.

Haven’t you tried it?

from direct.showbase.ShowBase import ShowBase

class Game(ShowBase):
    def __init__(self):
        ShowBase.__init__(self)

        self.accept("escape", base.userExit)

        self.setFrameRateMeter(True)

        self.mew = loader.loadModel("depthTest")

        collection = self.mew.findAllMatches("**/=depthOffset")
        for np in collection:
            offset = int(float(np.getTag("depthOffset")))
            np.setDepthOffset(offset)

        self.mew.reparentTo(render)
        self.mew.setY(4)


app = Game()
app.run()

This is the result of that exact program on my Ryzen machine:

The issue does not appear even with a self.mew.setY(5) while scrolling around the plane:

The issue begins to appear for me at a self.mew.setY(6), but this could be natural.

I don’t know: given the separation of the models and so low a distance from the camera as six units, it seems quite unexpected to me.

I do find it curious that this doesn’t happen in your test environment. Could you please confirm that this is the case even at similar ranges and angles as it those at which it showed up in the test-code?

If it doesn’t show up at all in your test-environment, perhaps there’s some part of your setup, some difference from the Panda defaults and my own setup, that might tell us why this is happening and what might be done about it…

I was able to catch this error with a screenshot. It does appear, but it is not very noticeable until the camera is at a certain range and angle, I believe. The issue disappears without the depthOffset code.

1 Like

Okay, so it’s pretty universal, it seems. Thank you for testing–it’s appreciated!

As you say, it’s not something that appears from many positions. However, with enough play, it does appear, and it is quite unsightly, I do feel.

(In my case, a particular hallway happens to be lined up such that it appears if you walk down the left side of the passage, as I recall.)

Yeah, it seems to be “intermittent”.

1 Like

It’s weird, because it’s not a like a single-pixel break, it’s a slightly more complex error than that. But it should not be a precision issue at these distances.

It is really weird.

As to the complexity of it, the way it flickers looks an awful lot like depth-fighting.

… Which really doesn’t seem like it should be happening at such distances, as you say. o_0

I don’t see the issue when I pview the egg file, but I can try later on my desktop. Unfortunately, depth offset implementation details can vary somewhat from driver to driver.

Hmm. Not immediately certain what the best solution is here. We could try varying the parameters we pass to the OpenGL depth offset call (there are two, but we expose only one and put it in both parameters), or maybe we need a different trick for decals in this situation.

That’s to be expected: I didn’t know offhand how to apply a depth-offset within an egg-file, and so have relied on doing so in code–specifically, in the test-code that I gave above.

On my end, at least, if you open the egg-file and replace “ depthOffset” with “ depth-offset”, the problem shows up in PView.

Sorry about that. ^^;

I’ll confess that this goes beyond the current bounds of my knowledge, so I’ll defer to the thoughts of you and others on the matter. ^^;

Ah, yes, thank you, changing it to a depth-offset Scalar does reproduce the issue under very steep angles.

What is probably happening is that OpenGL is accepting both a slope-scaled depth bias and a constant offset. Panda is passing the same value to both parameters. Under steep angles, the depth offset then gets amplified, causing it to test in front of the other surface.

I think what we want to do is split out the depth offset into two parameters in Panda, the constant offset and the slope factor, therefore giving more control over the offset. For a decal, you probably want to be able to lower the slope factor and just use a constant value.

Could you file a feature request for this in GitHub? Thanks!

Aah, I see–your explanation makes a lot of sense, thank you! :slight_smile:

Done, I believe!

You should find it here:

(I also included in the report a version of the test-model that should have the proper “depth-offset” scalar applied.)

1 Like

Why don’t you position the flickering object a bit back or the wall a bit front?

This was suggested earlier in the thread, I believe, and having tried it, it doesn’t help to great degree, I’m afraid.

Nevertheless, thank you for the suggestion. :slight_smile:

1 Like

I looked at the .egg file, it looks like you have polys that are about 1 unit height, there is less than that in distance between them (less than .5 or so). With a depthOffset=5, then we’d expect penetration correct? In other words partly WAI (working as intended).

But it is inconsistent, ie, you have to reduce Z precision by moving the camera away.

Regardless, anyone have more info on the depthOffset param (What units it works in?)

My reason to ask, if the scale of the wall polys vs. the decal poly is adjusted, then the effect could still work properly given the right scale (and reducing the depthOffset value relative to both)

I only set it that high to exaggerate the effect–as I recall, it happens even with an offset of 1.

Either way, my understanding is that a value of 1 is the amount that would separate two co-locational surfaces in the depth buffer. From that, I wouldn’t expect an offset of five to correspond to a separation of as much as ~0.4, myself.

Here you go!
https://docs.panda3d.org/1.10/python/reference/panda3d.core.NodePath?#panda3d.core.NodePath.setDepthOffset

Ok, it makes more sense now, ug should have read @rdb 's post more carefully.

Looks like Panda is calling:

glPolygonOffset((GLfloat) -offset, (GLfloat) -offset);

Which invokes:

void glPolygonOffset(	GLfloat factor, 	GLfloat units);

The value of the offset is factor×DZ+r×units, where DZ is a measurement of the change in depth relative to the screen area of the polygon, and r is the smallest value that is guaranteed to produce a resolvable offset for a given implementation. The offset is added before the depth test is performed and before the value is written into the depth buffer.

So that explains why we see the effect exaggerated. We might want to have units=1

Nonetheless - it looks like ‘r’ is flaky, hardware dependent, so even if fixed it might not be reliable.

One quick alternative might be:

node.set_shader_input("fudge", 1.0);

then make a super tiny shader just for your decals that does something like:

... setup parms ...
vec4 v = gl_ModelViewMatrix * vert;
v.w -= fudge;
gl_Position = gl_ProjectionMatrix * v

Seems to me it would give the same effect, almost, just fudge all your depth values by a configurable amount.