2

For our multiplatform engine that supports both OpenGL and DirectX9 I am adding support for decals. In OpenGL I can set glPolygonOffset(-1.0f, -1.0f) to fix z-fighting between the wall and the decals. I want the DirectX version to behave exactly the same, so I call this:

float offsetFloat = -1.0f;
DWORD offsetDWord = *((DWORD*)&offsetFloat);
device->SetRenderState(D3DRS_DEPTHBIAS, offsetDWord);
device->SetRenderState(D3DRS_SLOPESCALEDEPTHBIAS, offsetDWord);

However, this gives me an extremely large depth bias. It seems I need to use extremely small values in DirectX9. However, I can't seem to find how small.

I noticed that in the OGRE engine's source they're dividing by 250000, but despite the comment I don't quite see where that number comes from. Also, they only divide the constant by that for some reason?

// D3D also expresses the constant bias as an absolute value, rather than 
// relative to minimum depth unit, so scale to fit
constantBias = -constantBias / 250000.0f;
__SetRenderState(D3DRS_DEPTHBIAS, FLOAT2DWORD(constantBias));

slopeScaleBias = -slopeScaleBias;
__SetRenderState(D3DRS_SLOPESCALEDEPTHBIAS, FLOAT2DWORD(slopeScaleBias));

So my question: what do I need to pass to DirectX9 to get the exact same result as glPolygonOffset?

Nicol Bolas
  • 449,505
  • 63
  • 781
  • 982
Oogst
  • 358
  • 2
  • 14
  • I've noticed that in some cases these particular numbers fix z-fighting in DirectX but don't fix z-fighting in OpenGL, so apparently the bias is stronger in DirectX than in OpenGL this way. I don't know exactly how much though, but multiplying the OpenGL bias by 2 makes them behalve more similarly. – Oogst May 14 '18 at 12:39

2 Answers2

1

I haven't found an exact number anywhere, but by experimenting I have figured out that to get roughly the same effect in OpenGL and DirectX, I need to divide by 3500000, instead of the 250000 mentioned above.

If anyone knows the exact number or why it's this, I'd love to hear that, but for practical purposes I think this conclusion will do for me.

Oogst
  • 358
  • 2
  • 14
  • 1/3500000 is with a small factor of the smallest possible distinguishable difference in value of a float32 near 1, I'd guess that's where it's coming from, but don't know the details. https://calculla.com/float_binary_representation might be useful for understanding. – nmr Jun 09 '22 at 18:55
1

If you look at the equations for OpenGL polygon offset and the equivalent Direct3D 9 renderstates, you'll find that they're identical, other than OpenGL has a term for an implementation-dependent constant value. If we assume that value is 1, the equations do become identical.

The obvious problem here is: what happens when the value is not 1?

Unfortunately OpenGL doesn't seem to provide a way of querying it, so the only thing you can do is twiddle parameters until you find something that works, then twiddle them some more as edge cases arise, and never assume that what works for you will work for anyone else.

Direct3D's assumption that the value is always 1 may not necessarily hold good all the time either.

Bottom line is that polygon offset is not a 100% robust method for fixing z-fighting under any circumstances. Have you tried other methods, such as pushing your decals out an epsilon along the surface normal?

Maximus Minimus
  • 256
  • 1
  • 3
  • "r is the smallest value that is guaranteed to produce a resolvable offset for a given implementation" (https://docs.gl/es2/glPolygonOffset) so while it's implementation dependent, it seems fairly predictable, as the format of the depth buffer is generally known, and we know the default values for glDepthRange are [0, 1]. – nmr Jun 09 '22 at 19:26