Better control over tessellation in OpenGL?
I spent the day working on an OpenGL application that will tessellate the mesh and apply lens distortion. The goal is to be able to take wide-angle shots with a variety of lenses. So far, I have the shaders applying distortion correctly, but I had problems controlling the tessellation the way I want. Right now, my Tessellation Control Shader is just splitting one triangle into many smaller triangles, then I apply lens distortion in the evaluation shader.
The problem I run into is that when I have really big triangles in the scene, they tend to need more warping. This means that they need tessellation more in order to provide good results. Unfortunately, I cannot calculate the size of the triangle (in screen space) in the Vertex Shader or Tessellation Control Shader, but I need to figure out the amount of tessellation in the tessellation control shader.
My question is, is there a way to keep the whole primitive in an OpenGL programmable pipeline, calculate some metrics about it, and then use that information to drive tessellation?
Here are some sample images for clarity ...
Figure 1 (Above): Each red or green square was originally two triangles, this example looks good because the triangles were small.
Figure 2 (Above): Each red or green region was originally two triangles, this example looks bad because the triangles were small.
Figure 3 (Above): Another example with small triangles, but with a much larger mesh. Notice how many curves there are at the edges. Still looks good with tessellation level 4.
Figure 4 (Above): Another example with large triangles showing only center 4 columns, because the image is not clear if there are more columns present. This shows how very large triangles do not tessellate well. If I set tessellation really really high then it works well. But then I do a crazy amount of tessellation on smaller triangles.
source to share
In a shader control shader (TCS), you have read access to every vertex in the input patch of the primitive. While this sounds good on paper, if you are trying to figure out the maximum edge length of a patch, it actually means repeating each vertex in the patch on every TCS call, and this is not particularly efficient.
Instead, it may be more appropriate to pre-compute the center of the patch in object space and determine the radius of the sphere that tightly constrains the patch. Store this restrictive information as an additional vec4
per-vertex attribute , packaged as shown below.
Pseudocode for TCS that calculates the longest patch length in NDC space
#version 420
uniform mat4 model_view_proj;
in vec4 bounding_sphere []; // xyz = center (object-space), w = radius
void main (void)
{
vec4 center = vec4 (bounding_sphere [0].xyz, 1.0f);
float radius = bounding_sphere [0].w;
// Transform object-space X extremes into clip-space
vec4 min_0 = model_view_proj * (center - vec4 (radius, 0.0f, 0.0f, 0.0f));
vec4 max_0 = model_view_proj * (center + vec4 (radius, 0.0f, 0.0f, 0.0f));
// Transform object-space Y extremes into clip-space
vec4 min_1 = model_view_proj * (center - vec4 (0.0f, radius, 0.0f, 0.0f));
vec4 max_1 = model_view_proj * (center + vec4 (0.0f, radius, 0.0f, 0.0f));
// Transform object-space Z extremes into clip-space
vec4 min_2 = model_view_proj * (center - vec4 (0.0f, 0.0f, radius, 0.0f));
vec4 max_2 = model_view_proj * (center + vec4 (0.0f, 0.0f, radius, 0.0f));
// Transform from clip-space to NDC
min_0 /= min_0.w; max_0 /= max_0.w;
min_1 /= min_1.w; max_1 /= max_1.w;
min_2 /= min_2.w; max_2 /= max_2.w;
// Calculate the distance (ignore depth) covered by all three pairs of extremes
float dist_0 = distance (min_0.xy, max_0.xy);
float dist_1 = distance (min_1.xy, max_1.xy);
float dist_2 = distance (min_2.xy, max_2.xy);
// A max_dist >= 2.0 indicates the patch spans the entire screen in one direction
float max_dist = max (dist_0, max (dist_1, dist_2));
// ...
}
If you run a chart 4 th through the TCS, you have to come up with a value for the max_dist
almost 2.0 , which means that you need the same number of sub-sections as possible. Meanwhile, many of the patches on the periphery of the sphere in the 3 rd diagram will be close to 0.0 ; they don't need much partitioning.
This does not apply to situations where part of the patch is off-screen. To properly handle these situations, you need to pin the NDC extremes to [ -1.0 , 1.0 ]. It seemed like more problems than it was worth.
source to share