更好地控制OpenGL中的Tessellation?

时间:2014-12-26 04:14:01

标签: c opengl graphics glsl tessellation

我花了一天时间研究一个OpenGL应用程序,它将对网格进行细分并应用镜头失真。目标是能够为各种不同的镜头渲染广角镜头。到目前为止,我已经让着色器正确应用了失真,但我一直在按照我想要的方式控制曲面细分。现在我的曲面细分控制着色器只是将一个三角形分成了一定数量的小三角形,然后我在曲面细分评估着色器中应用了镜头失真。

我采用这种方法的问题是,当我在场景中有非常大的三角形时,它们往往需要更多的翘曲。这意味着他们需要进行更多镶嵌,以确保获得良好的效果。遗憾的是,我无法在“顶点着色器”或“曲面细分控制着色器”中计算三角形的大小(在屏幕空间中),但我需要在“细分控制”着色器中定义曲面细分量。

我的问题是,有没有办法在OpenGL的可编程管道中保存整个原语,计算一些关于它的指标,然后用这些信息来控制曲面细分?

为了清晰起见,这里有一些示例问题的图像......

Small Triangles Look Good

图1(上图):每个红色或绿色方块最初是2个三角形,这个例子看起来不错,因为三角形很小。

Big Triangles Look Bad

图2(上图):每个红色或绿色区域最初是2个三角形,这个例子看起来很糟糕,因为三角形很小。

Small Triangles Again

图3(上图):另一个小三角形但有更大,更大网格的例子。注意边缘上有多少东西弯曲。镶嵌水平为4时仍然看起来很好。

Really Big Triangles Bad

图4(上图):另一个包含大三角形的示例,仅显示中心4列,因为如果存在更多列,则图像难以理解。这表明非常大的三角形不能很好地镶嵌细分。如果我将曲面细分设置得非常高,那么这就好了。但是,我也在较小的三角形上进行了大量的曲面细分。

1 个答案:

答案 0 :(得分:1)

在曲面细分控制着色器(TCS)中,您可以读取访问输入补丁图元中的每个顶点。虽然这在纸上听起来不错,但如果你试图计算一个补丁的最大边长,那实际上意味着在每次TCS调用时迭代补丁中的每个顶点,这并不是特别有效。

相反,在对象空间中预先计算补丁的中心并确定紧密绑定补丁的球体的半径可能更实际。将此边界信息存储为每个顶点的额外vec4属性,如下所示打包。

用于计算NDC空间中补丁最长长度的TCS的伪代码

#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));

  // ...
}

如果您通过此TCS运行4 th 图表,则应该为max_dist提供非常接近 2.0 的值,这意味着您需要尽可能多的细分。同时,3 rd 图中球体周边的许多斑块将接近 0.0 ;他们不需要太多的细分。

这不能正确处理部分补丁在屏幕外的情况。您需要将NDC极值钳位到[ -1.0 1.0 ]以正确处理这些情况。看起来比实际上更麻烦。