3

只是一个关于 DirectX11 Tessellation 的问题。

在 Hull Shader 中,可以设置的最大细分因子为 64(不知道为什么)。现在虽然这对于小型飞机来说已经足够了,但对于大型飞机来说,这还不够,所以我想知道如何渲染大型飞机?

以下代码生成我的四边形,R半径在哪里:

vertices[0] = D3DXVECTOR3(-R, 0.0f,  R); //Top left
vertices[1] = D3DXVECTOR3( R, 0.0f,  R); //Top right
vertices[2] = D3DXVECTOR3( R, 0.0f, -R); //Bottom right
vertices[3] = D3DXVECTOR3(-R, 0.0f, -R); //Bottom left
vertices[4] = D3DXVECTOR3(-R, 0.0f,  R); //Top left
vertices[5] = D3DXVECTOR3( R, 0.0f, -R); //Bottom right

indices[0] = 0;
indices[1] = 1;
indices[2] = 2;
indices[3] = 3;
indices[4] = 4;
indices[5] = 5;

我确实修改了它,将它放在一个循环中以生成多个四边形,比如在 3x3 网格中。但是,当我将它传递给曲面细分着色器时,它对第一个四边形效果很好,但其他四边形却搞砸了。

vector<D3DXVECTOR3> verts;
vector<D3DXVECTOR2> tex;

float R = 1000;

for(int z = 0; z < 4; z++)
{
    for(int x = 0; x < 4; x++)
    {
        float xOffset = x * (2*R);
        float zOffset = z * (2*R);

        // Load the vertex array with data.
        verts.push_back( D3DXVECTOR3(-R+ xOffset, 0.0f,  R+ zOffset) ); //Top left
        verts.push_back( D3DXVECTOR3( R+ xOffset, 0.0f,  R+ zOffset) ); //Top right
        verts.push_back( D3DXVECTOR3( R+ xOffset, 0.0f, -R+ zOffset) ); //Bottom right
        verts.push_back( D3DXVECTOR3(-R+ xOffset, 0.0f, -R+ zOffset) ); //Bottom left
        verts.push_back( D3DXVECTOR3(-R+ xOffset, 0.0f,  R+ zOffset) ); //Top left
        verts.push_back( D3DXVECTOR3( R+ xOffset, 0.0f, -R+ zOffset) ); //Bottom right

        tex.push_back( D3DXVECTOR2(0.0f, 0.0f) );
        tex.push_back( D3DXVECTOR2(1.0f, 0.0f) );
        tex.push_back( D3DXVECTOR2(0.0f, 1.0f) );
        tex.push_back( D3DXVECTOR2(0.0f, 1.0f) );
        tex.push_back( D3DXVECTOR2(1.0f, 0.0f) );
        tex.push_back( D3DXVECTOR2(1.0f, 1.0f) );
    }
}

// Set the number of vertices in the vertex array.
m_vertexCount = verts.size();

// Set the number of indices in the index array.
m_indexCount = verts.size();

// Create the vertex array.
vertices = new VertexType[m_vertexCount];
if(!vertices)
{
    return false;
}

// Create the index array.
indices = new unsigned long[m_indexCount];
if(!indices)
{
    return false;
}

for(int i = 0; i < m_vertexCount; i++)
{
    vertices[i].position = verts[i];
    vertices[i].texture =  tex[i];
    indices[i] = i;
}

这是我渲染环形平面时的样子:

为什么在使用多个四边形时不能正确镶嵌?我是否应该通过在我的图形类中创建一个循环来解决这个问题,然后我会一遍又一遍地翻译和渲染同一个四边形以创建一个网格?

希望这是有道理的。

4

1 回答 1

16

您是否真的想要对整个平面进行曲面细分是非常值得怀疑的——您只是在浪费时间对整个事物进行曲面细分,而观众甚至无法分辨远处点的差异。考虑实现某种实时细节级别 (LOD)算法。LOD 的概念是为较近的对象提供更多细节,同时让较远的对象更简单以节省渲染时间。如果您没有“获得” LOD,这里有一张可以帮助您的图像:

说明 LOD 的图像

High tessellation <-------------------------------> Low tessellation

虽然这些特定模型是在 Maya 等建模软件中手动创建的,而不是在 GPU 上实时细分,但希望您仍然能理解。当相机离模型很远时,在右边渲染兔子,当它靠近时,渲染更详细的兔子,直到相机就在对象旁边,当你渲染它的全部美感时。

在我的时代,我发现了两种我认为优于其他所有实时 LOD 的方法。第一个是为水渲染而开发的,它的链接在这里- 我真的无法比他们更好地解释它。当我现在试图解释另一个问题时,惊讶地观看(或者如果我失败了就指指点点并大笑)!


GPU 地形细分和曲面细分

  • 资料来源: Wolfgang Engel的《 GPU Pro 4》一书中的第一个条目
  • 如果您稍微眯眼,您发布的图像可能类似于严重倾斜的地形。由于这是为地形开发的,因此这可能比为水渲染设计的其他方法更容易实现。

任何消息灵通的 GPU 程序员都知道,CPU 速度快,GPU 速度快。复杂图形的主要瓶颈是 CPU 和 GPU 之间的数据传输。它需要永远

这种专门为地形渲染开发的方法是一种几乎完全基于 GPU 的算法,旨在创建具有基于距离的 LOD 的细分网格。虽然性能在很大程度上取决于安装的硬件支持它的能力,但这种方法针对速度进行了高度优化。

几乎,该方法重复细分一个直到它达到所需的 LOD - 由程序员传递的一些参数表示。此外,在算法的每次迭代中也会进行剔除,避免了很多不必要的处理或对视锥体之外的区域进行细分。

由于此方法使用的所有数据都在 GPU 上保留和优化,因此 CPU 大部分可用于执行其他任务。此外,我们还利用平滑的 LOD 过渡方案来展示细分算法的实际应用。最终输出(这张带有部分叠加线框的图像)非常酷:

最终输出

现在,我不会撒谎。该算法难以下咽。所以,我会尽我所能为你拆掉它。这是行话:

Viewable region由变量 R 表示的可视区域被定义为轴对齐(通常朝上)的四边形,表示要细分和/或渲染的区域。该区域由中心位置和宽度定义,沿每个轴在正方向和负方向上延伸(见下图)。中心位置用 RC 表示。应用的偏移由 Rλ 表示。该区域的边界点由 P1、P2、P3 和 P4 表示。像这样描绘一个可见区域跨度:

可视区域跨度

Viewable region span一个可视区域跨度,用 θ 表示,可以通过以下函数量化,给定点 P 和应用的偏移量 λ:

θ(P, λ) = |P ScreenR - P ScreenL |

P ScreenL = (P VProjLxy )/P ProjLw

P ScreenR = (P ProjRxy )/P ProjRw

P ProjL = P WL × matProjection

P ProjR = P WR × matProjection

P WL = (P Wx - λ, P Wy )

P WR = (P Wx + λ, P Wy )

P W = P × matWorldView

可视区域跨度是其可视区域在屏幕上的宽度。

Maximum viewable region span由θ max表示的最大可视区域跨度是最大允许可视区域跨度。该值由用户设定,是LOD算法中的主要因素之一,也是细分算法中的关键部分。在 LOD 算法中,如果任何给定可视区域的可视区域跨度大于 θ max,则将其细分为 4 个新的可视区域。

Relative quadrant code此代码标识分割可视区域相对于其父可视区域的相对位置。此代码在细分算法中计算,并由 LOD 过渡算法使用。通常编码为 2 位掩码,此代码成为可视区域定义的一部分。


该算法具有三个主要步骤。

概述

第一步(准备):“初始化”+“中间区域缓冲区”

  • 只需设置;我不会用尽我的双手来解释它。

Step 2 (The Subdivision Algorithm):循环,包括“Dispose Region”和“Final Region Buffer”

第 3 步(LOD 算法):“最终区域缓冲区”+“LOD”+“输出”


第 2 阶段 - 细分算法

对于循环的每次迭代,将可视区域的输入流馈送到渲染管道的几何着色器阶段。第一次迭代的输入流是在阶段 1 中创建的初始输入流。所有后续迭代都使用前一次迭代的中间输出流作为输入。

几何着色器中使用了两个输出流。中间输出流用于保存用于进一步处理的可视区域。最终输出流用于保存在下一次​​迭代中不需要进一步处理的可视区域,并将作为算法第 3 阶段的一部分进行渲染。在几何着色器中,处理每个可视区域并执行以下三个操作之一:

  1. 如果确定可视区域不与视锥相交,则将其丢弃。剔除的可视区域不会添加到中间或最终输出流中。在确定可视区域是否与视锥相交时,请确保考虑算法第 3 阶段中的位移。

  2. 如果可视区域跨度θ,写为θ(RC,Rλ),大于最大可视区域跨度θmax,则可视区域R被分成四个象限(R1,R2,R3,R4)。每个象限本身都成为可视区域,然后被添加到中间输出流中,以便在循环的下一次迭代中重新处理。它们的相对象限代码(每个象限唯一的)也被添加到输出流中,以识别分割的可视区域与其父可视区域的相对位置。此代码稍后在渲染每个可视区域时由 LOD 过渡算法使用。要拆分可视区域 R,请创建四个新的可视区域 (R1,R2,R3,R4),它们各自的中心位置 (R1 C ,R2 C ,R3 C ,R4C ) 和应用的偏移量 (R1 λ ,R2 λ ,R3 λ ,R4 λ ) 定义如下:

R1 C = (R Cx - .5 × R λ , R Cy + .5 × R λ )

R2 C = (R Cx + .5 × R λ/ , R Cy + .5 × R λ )

R3 C = (R Cx + .5 × R λ , R Cy - .5 × R λ )

R4 C = (R Cx - .5 × R λ , R Cy - .5 × R λ )

R1 λ = .5 × R λ

R2 λ = .5 × R λ

R3 λ = .5 × R λ

R4 λ = .5 × R λ

  1. 如果可视区域跨度θ(R C ,R λ )小于或等于最大可视区域跨度θmax,则可视区域R被添加到可视区域的最终输出流中。

另请注意,在循环的最后一次迭代中,所有可见区域都必须添加到阶段 2 的输出流中。


第 3 阶段 - LOD 过渡算法

在算法的第 3 阶段,呈现可见区域流。阶段 3 的需要:具有 R λ的应用偏移的给定可视区域 R可能与具有 R λ大小的一半或两倍的应用偏移的另一个可视区域相邻。如果不在两个不同大小的可视区域之间执行平滑过渡,渲染时就会出现可见的不连续性或其他视觉异常。

为了避免我们的细分中出现裂缝,每个可视区域通过将可视区域分成四个四边形来渲染,分别用 Q1、Q2、Q3 和 Q4 表示。每个四边形的边界点,由静态边界点 (P 1 , P 2 , P 3 , P 4 ) 和变形边界点 (P L , P T , P R , P B , P C ) 的集合组成,定义如下:

边界点

总的来说,这些不重叠的四边形将覆盖与创建它们的可视区域相同的表面积。计算每个四边形的边界点以与相邻可视区域四边形的边界点对齐。变形四边形边界点 (P L , P T , P R , P B , P C ) 计算如下:

变形边界点

给定在点 P 处的可视区域跨度 θ 和应用的偏移量 λ,写为 θ(P, λ),使用以下公式计算变形因子 - 写为 T(P, λ):

变形因子

我们计算具有中心位置 R C和应用偏移 R λ的可视区域 R 的每个一般变形因子(T L、 T T、 T R、 T B、 T C ) :

一般变形因素


问题是,这些通用变形因子仅在相邻可视区域具有与 R λ相同的应用偏移时才起作用。见下图:计算一般变形因子时使用的各种位置的图表。

在计算变形因子时,我们需要处理两种特殊的边界情况。当一个可视区域与具有更大或更小的应用偏移 λ 的另一可视区域相邻时,就会出现这些特殊情况。案例定义如下:

特殊边界情况

特殊边界案例 1

应用了偏移R λ的可视区域R 与应用偏移0.5R λ的较小可视区域相邻。我们将有条件地设置以下变形因子:

特殊边界案例 1

上面的一组条件测试了相邻可视区域被分割成更小的可视区域的情况。因此,我们需要将受影响的变形因子锁定为 1。这是为了确保所有重叠的四边形顶点与较小的相邻可视区域的顶点完全匹配。下图提供了计算边界案例 1 的变形因子时使用的各种位置的图表。

边界案例 1 的位置

边界案例 2

应用偏移R λ的可视区域与应用偏移2R λ的较大可视区域相邻。为了能够测试这种情况,我们需要确保我们有一些关于我们正在渲染的当前可视区域的额外信息。在我们算法的第 2 阶段,我们需要存储每个可视区域的相关象限代码。这现在用于正确计算较大的相邻可视区域的中心位置。这也允许我们计算与较大的相邻可视区域相邻的可视区域的边缘。我们将根据可视区域的相对象限代码有条件地设置以下变形因子:

基于相关象限码的变形因子 基于相关象限码的变形因子

上面的一组条件测试了相邻的可视区域没有被分割成与当前可视区域相同的大小,而是应用了 2R λ的偏移量的情况。因此,我们需要将受影响的变形因子锁定为 0,以确保所有重叠的四边形顶点与较大的相邻可视区域的顶点完全匹配。下图提供了基于可视区域的相对象限代码计算边界案例 2 的变形因子时每个可视区域使用的各种位置的图表:

边界案例 2 相关象限代码


耶!你做到了最后!就在我开始享受用头撞桌子的时候!等等,你问什么?代码示例?好吧,当然! 给你- 包括 VC++2008 和 VC++2010 版本。享受!

于 2014-01-05T00:20:21.797 回答