Tessellation Update

Trying to improve

I have realized I have performance issues while tessellation is on together with Anaglyph rendering(stereo) and shadows. Anaglyph obviously means I draw everything twice, so it is not a big surprise.

Even without Anaglyph on there were performance issues. It seem I was tessellating too much. I have updated the tessellation shader and in addition I have split the level into smaller pieces so I won’t draw the whole level.
This is possible because the character never sees the whole level at once.

Splitting the Level

For the splitting I used voxelization.
At first I have split the level into non intersecting pieces and then I have added the line of sight visible vertices.

I am not sure how much this has contributed to performance. It might not have made such a big difference compared to just drawing the whole level but its nice to have and might prove useful in the future.

Updating the Tessellation Shader

In my previous post I suggested to tessellate according to how noticeable the displacement would be. Surfaces which face the camera will have a less noticeable effect when displacing, or so I assumed.

This is partly correct. The contour is less visible, but there are other factors. For instance shadow maps will look flat if the rocky surface has a flat geometry.
There is also a big difference between a completely flat surface and a surface with geometry that comes out at you, even if the surface is facing you.

There is another issue with tessellating based on the normal of the surface. If you stand in an open field, the ground all around you will be perpendicular to the camera so the method will make all the ground have high tessellation.
This proved to be bad for performance.

What I have decided to do is to tone down tessellating with how much the surface is facing the camera and give a bigger weight to the distance from the view.

These two factors gave better results in both quality and performance.

Last Word

I am in need of playtesters. I actually have a few playtesters and I don’t need to play test that much, but with all the variety of machines, different time zones and different connections I find myself without any tester at times

(Hopefully these testers will turn into avid players when I released the first gamey version)

If you are interested, email me at “support pompipompi net”

Tessellation Simplified

I have implemented tessellation + displacement map for my game Shoe String Shooter.
To decide how much to tessellate a triangle I calculated it’s screen space area. The more screen space area it takes, the more I tessellated it.

This would theoretically make the triangles uniformly sized across the screen space. It made sense to me, but it has some major issues.

The displacement occurs along the triangle’s normal. If the triangle is facing the camera, its normal is facing the camera as well.
There is very little visible effect when the geometry is displaced towards the viewer. A flat triangle with a normal mapped texture would be just as good(almost).

Another thing is that triangles which  are 90 degrees from the camera will almost disappear in screen space and will have a very small area in screen space.
However, when displacing those triangles the geometry is very much visible since the normal is tangential to the screen space.

The simpler approach

The new approach I have taken is simpler and give better results. It also requires only 3 control points instead of 6.

The first step is to tessellate every edge of the triangle according to how long the maximum displacement vector(at the direction of the normal) is on the screen space.
This metric will have the facing and farther triangles tessellate less, and the conspicuous and closer triangles tessellate more.

This is not enough. Some triangles are very big in world space and we don’t modulate the tessellation with the triangle’s area any more. So large triangles will appear more coarse.

The solution is to modulate the displacement vector tessellation with world space edge length. This will achieve spatial uniformity in world space.
Tessellating according to edge length has some advantages compared to tessellating according to triangle area.
First, we only need 3 control points instead of 6.
Second, we tessellate more along the longer edges and tessellate less along the shorter edges. Area calculation will not differ between a golden triangle and a very narrow but long triangle.

The last step is to bound the tessellation amount since we don’t want unnecessary triangles on the geometry that is up close. We don’t set a constant tessellation bound, but instead set a bound modulated with the world space edge length.

Code

PatchTess ScreenSpaceTessellator(float3 w[3], float4 p[3], float4 q[3])
{
	PatchTess pt;
	float Res = 768.0;
	float Cell1 = 16.0;
	float Cell2 = 8.0;
	float MaxTes = 10.0;

	unsigned int i=0;
	for (i=0; i<3; i++)
		pt.EdgeTess[i] = 1;
	pt.InsideTess = 1;
	float Tess[3] = {0, 0, 0};
	if (IsScreenCull (p[0], p[1], p[2]))
		return pt;
	for (i=0; i<3; i++)
		p[i]/=p[i].w;
	for (i=0; i<3; i++)
		q[i]/=q[i].w;
	for (i=0; i<3; i++)
	{
		float3 a1 = (w[(i+1)%3]-w[(i+2)%3]);
		Tess[i] = length(a1)/Cell1;
//		Tess[i] = max(Tess[i], 1);
		float2 a2 = (q[(i+1)%3].xy-p[(i+1)%3].xy)*Res*0.5;
		float2 b2 = (q[(i+2)%3].xy-p[(i+2)%3].xy)*Res*0.5;
		Tess[i] *= 0.5*(length(a2)+length(b2))/Cell2;
		Tess[i] = min(max(Tess[i], 1), MaxTes*length(a1)/Cell1);
	}
	for (i=0; i<3; i++)
		pt.EdgeTess[i] = Tess[i];
	pt.InsideTess = (Tess[0]+Tess[1]+Tess[2])/3.0;
	return pt;
}