• Advertisement
Sign in to follow this  

Holy grail of z-fighting - I thought I found it, why does it not work?

This topic is 2135 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

I thought I had found the holy grail of z-fighting with this shader-code:


l_output.positiondistance = length(l_output.world_position - g_camera_position);
...
...
l_output.position.y += (l_output.positiondistance / 1000.0f); // tiny offset to guard against z-fighting - offset is based on distance, as z-fighting gets worse the further away


However, this had a very unexpected result, that I do not have, when I run a standard offset.
When I turn my camera, it has an effect on the overlay (road), that I am offsetting. In the picture you will see the rendering with the camera at some 50 degrees and and then at some 90 degrees. At 100+ everything will disappear.

z_fighting_holy_grail_fail.jpg

How can the position be influenced by the orientation of the camera? Did I make some other silly mistake, or miss something?
Is there a way to do something like what I attempted?

Share this post


Link to post
Share on other sites
Advertisement
This kind of things are done with glPolygonOffset - look it up.

PS. Position is not view direction dependent, but depth is (screen, aka near plane, is FLAT). Also, my spidery senses say your approach is wrong on more than one level, unfortunately, you have left out all the relevant parts - anyway, use polygon offset instead.

Share this post


Link to post
Share on other sites
Polygon offset is not a holy grail of z-fighting either and is not suitable for use as a general-purpose fix for this. Depth buffer non-linearity and the spec allowing implementation-specific differences mean that polygon offset sucks for this purpose, and needs to be used carefully and with an understanding of these limits, if at all.

The true holy grail of z-fighting is to not use co-planar polygons. Not always possible, but there you go.

Share this post


Link to post
Share on other sites

This kind of things are done with glPolygonOffset - look it up.

PS. Position is not view direction dependent, but depth is (screen, aka near plane, is FLAT). Also, my spidery senses say your approach is wrong on more than one level, unfortunately, you have left out all the relevant parts - anyway, use polygon offset instead.


I measure the distance from the position in world space to the camera's position (also in world space). Is it not possible to calculate the distance from the camera to the position like that? And if not, then why not?

What other information do you want than what I provide? The l_output.world_position is simply the vertex shader a_position transformed with world matrix to world space, where the g_camera_position already is.
I already use l_output.positiondistance successfully when limiting far-view, so I don't understand why I cannot use it like I did to calculate a distance-based offset.
I will provide my full vertex-shader below so you have all information.

I will test depthbuffer, see if it helps me enough (although I foresee some of the same problems with small biases over big distances, but maybe Im wrong).
And mhagain, you are right, it is not always possible to not use co-planar polygons. Shadows is another good example, so I would say that co-planar polygons are quite usual in 3D graphics.


VertexNormalSHADOW VS_overlay_light_normal_shadow(float4 a_position: POSITION, float3 a_normal: NORMAL, float2 a_texcoord0: TEXCOORD0, float3 a_tangent: TANGENT)
{
VertexNormalSHADOW l_output;
l_output.position = mul(a_position, g_world_view_proj);
l_output.texcoord0 = a_texcoord0;

l_output.normal = mul(a_normal, (float3x3)g_world);
l_output.binormal = mul(cross(a_tangent, a_normal), (float3x3)g_world);
l_output.tangent = mul(a_tangent, (float3x3)g_world);

l_output.world_position = mul(a_position, g_world);
l_output.worldview_position = mul(a_position, g_world_view);
l_output.positiondistance = length(l_output.world_position - g_camera_position);

// l_output.position.y += (l_output.positiondistance / 1000.0f); // tiny offset to guard against z-fighting - offset is based on distance, as z-fighting gets worse the further away

float3x3 l_tangentspace = { l_output.tangent, l_output.binormal, l_output.normal };
l_output.tangentspace_view_direction = mul(l_tangentspace, normalize(g_camera_position - l_output.world_position));

return l_output;
}

Share this post


Link to post
Share on other sites

Polygon offset is not a holy grail of z-fighting either and is not suitable for use as a general-purpose fix for this. Depth buffer non-linearity and the spec allowing implementation-specific differences mean that polygon offset sucks for this purpose...

I have to disagree, slightly. Drawing coplanar decals is pretty much exactly what it is there for. It is very well specified (especially for fixed-point depth buffers) - it is just that finding a good factor/unit pair can be maddening indeed.

Still, while it is not a silver bullet - i would say it is still pretty darn good. Especially as there really is no real alternatives to it.

I would recommend some rule-of-thumb for it:
* have a sane "near" (as far as you possible can. GL_DEPTH_CLAMP can help) and "far" (much less important than "near", but still as near as possible) distance.
* as high vertex offset (away from wall. not towards viewer) as one can get away with - to help with "factor" and ... well, everything really :) (ie. be as non-planar as possible).
* glPolygonOffset with ...
* as high an "unit" as one can get away with (based on zero-sloped wall/decal pair furthest from viewer). This is the easiest to choose - do it before starting to fiddle with "factor". While searching for a good value - look out for z-fighting with background-wall (=> "unit" too low, don't bother with values below 1.0) and z-fighting with foreground objects (=> "unit" too high).
* as low a "factor" as one can get away with (based on extremely steeply sloped wall/decal pair nearest to viewer) - this one is a pita. While searching for a good value - look out for z-fighting with background-wall (=> "factor" too low, higher "unit" also helps) and z-fighting with foreground objects (=> "factor" too high, higher "unit" also helps).

Share this post


Link to post
Share on other sites
On OFP we 'solved' the problem by only rendering decals into the 'near' scene (0.2f -> 333.3f) using the same offset method the OP had to 'raise' the polygon as the distance increased. However for roads once you got beyond the 'near' scene they became part of the 'far' scene which just had them baked directly into the texture on the terrain.

Share this post


Link to post
Share on other sites
Ok, it looks like it is me, who has been making silly mistakes. I only alter the transformed position. The problem is, that I use the input position also to calculate the world-view-position, but this does not get changed equally, and that ends up messing up the final result.

Thank you guys for your time, and I apologize for my mistake.
Especially thanks to Phantom, as your story made me realize, that what I did was in fact possible, giving me more zeal in hunting down the problem :)

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement