3

我花了一天时间开发一个 OpenGL 应用程序,该应用程序将镶嵌网格并应用镜头失真。目标是能够为各种不同的镜头渲染广角镜头。到目前为止,我已经让着色器正确地应用了失真,但我一直在以我想要的方式控制镶嵌时遇到问题。现在我的 Tessellation Control Shader 只是将一个三角形分解为一组较小的三角形,然后我在 Tessellation Evaluation Shader 中应用镜头失真。

我使用这种方法遇到的问题是,当我在场景中有非常大的三角形时,它们往往需要更多的变形。这意味着需要对它们进行更多细分,以确保获得好看的结果。不幸的是,我无法在顶点着色器或镶嵌控制着色器中计算三角形的大小(在屏幕空间中),但我需要在镶嵌控制着色器中定义镶嵌量。

那么我的问题是,有没有办法在 OpenGL 的可编程管道中获取整个基元,计算一些关于它的指标,然后使用这些信息来控制曲面细分?

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

小三角形看起来不错

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

大三角形看起来很糟糕

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

又是小三角形

图 3(上图):另一个带有小三角形但网格大得多的示例。注意边缘有多少东西弯曲。细分级别为 4 时看起来仍然不错。

真的大三角形不好

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

4

1 回答 1

1

在 Tessellation Control Shader (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图表,您应该得出一个max_dist非常接近2.0的值,这意味着您需要尽可能多的细分。同时,第三张图中球体外围的许多补丁将接近0.0;他们不需要太多的细分。

这不能正确处理部分补丁在屏幕外的情况。您需要将 NDC 极端值限制为 [ -1.0 , 1.0 ] 以正确处理这些情况。看起来麻烦多于它的价值。

于 2014-12-26T16:21:15.367 回答