I have a problem with a geometry shader I am using to subdivide triangles for Lonely Mountains: Downhill. I don’t want interpolation across the triangles, since I want to have a uniform color and hard edges per final triangle (imagine a lowpoly mesh). The following GIF explains it I hope, but I will go through the (a bit simplified but hopefully more understandable) process below…
For each triangle three new vertices are created, which lie on the edges of the triangle - something like that:
For each triangle (4 per original tri) I write the data to the OutputStream:
// Create center triangle:
OutputStream.Append(newVertex01);
OutputStream.Append(newVertex12);
OutputStream.Append(newVertex20);
OutputStream.RestartStrip();
// Create triangle connected to vertex[0]
OutputStream.Append(input[0]);
OutputStream.Append(newVertex01);
OutputStream.Append(newVertex20);
OutputStream.RestartStrip();
// Create triangle connected to vertex[1]
OutputStream.Append(input[1]);
OutputStream.Append(newVertex12);
OutputStream.Append(newVertex01);
OutputStream.RestartStrip();
// Create triangle connected to vertex[2]
OutputStream.Append(input[2]);
OutputStream.Append(newVertex20);
OutputStream.Append(newVertex12);
OutputStream.RestartStrip();
Everything works fine so far, except the little one-pixel-sized holes appearing mainly at the original edges of the triangles. As far as I can tell these are cause by floating point imprecisions. But how do I solve this problem?
I tried to extend each new triangle a tiny fraction, which works for most cases. However at low angles and larger distances still quite a lot of hole appear for reasonable values of “dist”. With larger values I get heavy antialias problems and z fighting.
// Push vertex outwards by a tiny fraction of its size (dist < 0.01)
input[0].pos += (input[0].pos * 2 - newVertex01.pos - newVertex20.pos) * dist;
Other possible solutions? Draw the original mesh again with a small zOffset and zTest set to Greater would solve it, but seems overkill for effectively 0-5 pixels per frame.
I wonder how it works that regular meshes with split edges (hard edges) render without these problems and what can be done about that in the geometry shader?
Thanks a lot for reading. I would be grateful for any input!
You run into the same problems when writing tessellation shaders. It’s the shader’s responsibility to ensure that the geometry has no cracks or “T joins” (I forget the real name) where you place a vertex on the edge of another polygon.
Can you modify your output so that adjacent triangles subdivide at the same spot?
Yeah, Unity has no plans to add support for adjacency information, and the problem with hard edges is an issue even with your average tessellation shader. The solution I’ve seen to dealing with seams from hard edges when doing vertex manipulation or tessellation is to store an averaged normal into either the mesh’s tangent or an extra UV set. You can do this either using vertex streams, or creating a new mesh asset, or via an asset preprocessor.
The other solution is to use a compute shader to create your tessellated mesh and draw procedural to render it, but that requires you pass the vertex information of your mesh to the compute shader manually.
@bgolus : I didn’t really understand how storing the normal in the tangent helps with the “seams”. I used that for blending between a hard and smooth mesh which gives some interesting possibilities.
Example here: https://twitter.com/DrWDSo/status/762718292192727040
That might be something I could try when I got more time. Thanks for the input.
I was thinking of the issue being caused by what tessellation is usually used for, which is smoothing out the surface shape, in which case having a split edge will cause a large gap or intersecting geometry. I realize you’re just having problems with floating point accuracy from having the edge split at different points, thus the edges don’t line up perfectly.
There are two solutions to that, one is don’t split the edges at different points, obviously, but since the edges are split and you’re not actually using the same vertices there’s a chance they might still not match. The other, ugly, but used-more-often-than-you-think solution is to add some flange geometry to the edges of your original triangle. Basically in your geometry shader when creating your mesh also “extrude” the geometry edges down. You can actually see something like this being done in the recent Horizon: Zero Dawn gif that’s been going around. https://i.kinja-img.com/gawker-media/image/upload/ucoln8kedwfglsrlxvm5.gif
Notice how all of the terrain tiles seem to have a constant thickness to them, that’s additional geometry around each tile to hide the exact same error you’re seeing when two nearly identical but not actually identical edges leave single pixel holes.
Also if you always want faceted geometry, and never want any smoothed edges, you don’t need to use vertex normals at all and you’re better off just having all of your meshes use welded vertices / smoothed normals stored in the vertices. This will prevent holes from appearing in shadows when using normal biasing as well.
Yes, the first solution does not really bring any big improvements unfortunately.
The second - the extension of the triangle - is what I tried with the third code snippet in the first post. It works, but gives some weird artifacts in the distance, but still better than those holes.
If I use a smooth geometry as input and the geometry shader to generate just facetted normals (calculate normal of the triangle through its position and use that normal for all three vertices), I don’t get these issues at all. Maybe the adjacent information is still valid then (dont know much about that so far)?
For those curious, I uploaded a demo project (currently using Unity 5.6f3):
Yes. It’s a similar, but different issue since normal biasing is actually pushing the vertices apart (though the single pixel holes can still show up occasionally).
That looks like the code you’re taking about just takes the triangle and slightly enlarging it so the edges overlap slightly. I’m talking about adding additional edge geometry that’s extruded down. It’s potentially even less efficient than your idea of drawing the tri a second time with an offset.
Honestly, scaling the triangle up by half a pixel might be the “best” solution for you, but is a little more work than just scaling by dist and some magic number, but it would have fewer artifacts than your current solution.
Subtly different than your original code using the lerpValue and 1-lerpValue, and algebraically identical to your original code if lerpValue = 0.5, but potentially more stable due to floating point accuracies.
I was wrong before, setting the lerpValue to 0.5 fixes the problem (I am also skipping the subdivision for triangles which are further away from the camera, which causes the same issue (T-Junction or what ever it’s called) during the transition). So basically I need to figure out how to get that look without the irregular subdivision…
lerpValue of 0.5 (too regular triangles)
“random” lerpValue (nice broken up look I want to have)
In the fragment shader, I could try that… but actually I am subdividing it twice, so it all gets a bit more complicated. But I will think about it! Thanks a lot for you help!
Is the “precise” keyword supported in Unity? I’ve used “invariant” in GLSL once to fix a similar issue where different primitives would pass vertexes in a different order and get a slightly different result.
Also, I have a GLSL shader laying around somewhere in an experiment project that does barycentric subdivision in the frag shader like @bgolus suggested. It even did some fwidth() stuff to AA the edges. It was sort of recursive in that you could just iterate it to find smaller and smaller triangles that contained the current pixel. I’ll dig around for it tonight when I get home if I remember.
I don’t have too much time right now for the shader, since we are preparing to show the game at the Quo Vadis & A.MAZE in Berlin next week, but here is a little improvement:
By offsetting only the new vertices and keeping the original vertices in place (e.g. lower left & right), the z fighting and overlapping issues become much less noticeable.
Offsetting them only half a pixel seems pretty good, however it has to be as cheep as possible, since we are dealing with about 1 million (subdivided) vertices per scene in the end (original vertex count will be around ~50k max).
I will give that a try and then maybe to some experiments with creating the effect in the vertex shader.
@slembcke2 : if you find anything I would be more than happy to have a look!