14

我刚开始使用 OpenGL 曲面细分,遇到了一些麻烦。我正在细分由一个顶点形成的一系列补丁。这些顶点/补丁以类似网格的方式构造,以便稍后形成由 Perlin Noise 生成的地形。

我遇到的问题是,从第二个补丁开始,之后的每 5补丁,有时会有很多镶嵌(不是我配置的方式),但大多数时候它根本没有镶嵌。

像这样:

  

两个白色圆圈标记高度/过度镶嵌的补丁。还要注意未镶嵌补丁的模式。

奇怪的是,它可以在我的 Surface Pro 2(Intel HD4400 显卡)上运行,但在我的主台式电脑(AMD HD6950 显卡)上却存在错误。有没有可能是硬件坏了?

补丁是使用以下代码生成的:

    vec4* patches = new vec4[m_patchesWidth * m_patchesDepth];
    int c = 0;
    for (unsigned int z = 0; z < m_patchesDepth; ++z) {
        for (unsigned int x = 0; x < m_patchesWidth; ++x) {
            patches[c] = vec4(x * 1.5f, 0, z * 1.5f, 1.0f);
            c++;
        }
    }
    m_fxTerrain->Apply();
    glGenBuffers(1, &m_planePatches);
    glBindBuffer(GL_ARRAY_BUFFER, m_planePatches);
    glBufferData(GL_ARRAY_BUFFER, m_patchesWidth * m_patchesDepth * sizeof(vec4), patches, GL_STATIC_DRAW);
    GLuint loc = m_fxTerrain->GetAttrib("posIn");
    glEnableVertexAttribArray(loc);
    glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, sizeof(vec4), nullptr);
    delete(patches);

并绘制:

    glPatchParameteri(GL_PATCH_VERTICES, 1);
    glBindVertexArray(patches);

    glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
    glDrawArrays(GL_PATCHES, 0, nrOfPatches);

顶点着色器:

#version 430 core
in vec4 posIn;

out gl_PerVertex {
    vec4 gl_Position;
};

void main() {
    gl_Position = posIn;
}

控制着色器:

#version 430
#extension GL_ARB_tessellation_shader : enable
layout (vertices = 1) out;

uniform float OuterTessFactor;
uniform float InnerTessFactor;

out gl_PerVertex {
    vec4 gl_Position;
} gl_out[];

void main() {

    if (gl_InvocationID == 0) {
        gl_TessLevelOuter[0] = OuterTessFactor;
        gl_TessLevelOuter[1] = OuterTessFactor;
        gl_TessLevelOuter[2] = OuterTessFactor;
        gl_TessLevelOuter[3] = OuterTessFactor;

        gl_TessLevelInner[0] = InnerTessFactor;
        gl_TessLevelInner[1] = InnerTessFactor;
    }
    gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position;
}

评估着色器:

#version 430 
#extension GL_ARB_tessellation_shader : enable
layout (quads, equal_spacing, ccw) in;

uniform mat4 ProjView;
uniform sampler2D PerlinNoise;

out vec3 PosW;
out vec3 Normal;
out vec4 ColorFrag;
out gl_PerVertex {
    vec4 gl_Position;
};

void main() {
    vec4 pos = gl_in[0].gl_Position;
    pos.xz += gl_TessCoord.xy;
    pos.y = texture2D(PerlinNoise, pos.xz / vec2(8, 8)).x * 10.0f - 10.0f;
    Normal = vec3(0, 1, 0);
    gl_Position = ProjView * pos;
    PosW = pos.xyz;
    ColorFrag = vec4(pos.x / 64.0f, 0.0f, pos.z / 64.0f, 1.0f);
}

片段着色器:

#version 430 core
in vec3 PosW;
in vec3 Normal;
in vec4 ColorFrag;
in vec4 PosH;

out vec3 FragColor;
out vec3 FragNormal;

void main() {
    FragNormal = Normal;
    FragColor = ColorFrag.xyz;
}

我试图对不同的细分级别进行硬编码,但这没有帮助。我最近开始使用 OpenGL,所以如果我在做一些愚蠢的事情,请告诉我。

那么有没有人知道是什么导致了某些补丁的“闪烁”?

更新:我有一个朋友运行该项目,他得到了相同的闪烁镶嵌图案,但除了过度镶嵌外,根本没有绘制失败的补丁。他的显卡和我一样(AMD HD6950)。

4

1 回答 1

11

您应该使用三角形/四边形细分,其中每个面片有 3 或 4 个顶点。如我所见,您使用四边形(我也使用它们)。在这种情况下,您可以这样设置:

glPatchParameteri(GL_PATCH_VERTICES,4);
glBindVertexArray(VertexArray);

(提示:对地形使用绘图元素,基于 2D 位移的网格的性能要好得多。)
在控制着色器中,使用

layout (vertices = 4) out;

因为你的补丁有 4 个控制点。顺序仍然很重要(CCW/CW)。
我个人不喜欢使用内置变量,因此对于顶点着色器,您可以像这样将顶点数据发送到 tesscontrol:

layout (location = 0) out vec3 outPos;
....
outPos.xz = grid.xy;
outPos.y = noise(outPos.xz);

苔丝控制:

layout (location = 0) in vec3 inPos[]; //outPos (location = 0) from vertex shader
//'collects' the 4 control points to an array in the order they're sended
layout (location = 0) out vec3 outPos[];  //send the c.points to the ev. shader
...
gl_TessLevelOuter[0] = outt[0];
gl_TessLevelOuter[1] = outt[1];
gl_TessLevelOuter[2] = outt[2];
gl_TessLevelOuter[3] = outt[3];

gl_TessLevelInner[0] = inn[0];
gl_TessLevelInner[1] = inn[1];

outPos[ID] = inPos[ID];//gl_invocationID = ID

请注意,输入和输出顶点数据都是一个数组。

tessev 很简单:

layout (location = 0) in vec3 inPos[]; //the 4 control points
layout (location = 0) out vec3 outPos; //this is no longer array, next is the fragment shader
...
//edit: do not forgot to add the next line
layout (quads) in;

vec3 interpolate3D(vec3 v0, vec3 v1, vec3 v2, vec3 v3) //linear interpolation for x,y,z coords on the quad
{
    return mix(mix(v0,v1,gl_TessCoord.x),mix(v3,v2,gl_TessCoord.x),gl_TessCoord.y);
};
...main{...
outPos = interpolate3D(inPos[0],inPos[1],inPos[2],inPos[3]); //the four control points of the quad. Every other point is linearly interpolated between them according to the TessCoord.
gl_Position = mvp * vec4(outPos,1.0f);

四域的良好表示:http: //ogldev.atspace.co.uk/www/tutorial30/tutorial30.html

我认为问题出在您的单顶点补丁上。我无法想象如何将一个顶点路径划分为三角形,我不知道它在另一个硬件上是如何工作的。镶嵌是为了将图元划分为其他简单的图元,在 OGL 的情况下划分为三角形,因为它可以由 GPU 轻松处理(3 个点始终位于一个平面上)。因此,对于三角形,补丁顶点的最小数量应该是 3。我喜欢四边形,因为它更容易索引,而且内存成本更低。在镶嵌过程中它也会被分成三角形。http://www.informit.com/articles/article.aspx?p=2120983 另外,还有另一种类型,等值线镶嵌。(查看链接,第二个很好。)

总而言之,尝试使用四边形或三角形,并将控制顶点设置为 4(或 3)。我的(相当复杂的)地形着色器在这里带有平截头体剔除,镶嵌着色器剔除基于 geoclipmap 的地形。此外,在没有镶嵌的情况下,它适用于顶点着色器中的顶点变形。也许这段代码的某些部分会有用。http://speedy.sh/TAvPR/gshader.txt

具有大约 4 像素/三角形的镶嵌的场景以 75 FPS(带片段)运行,具有运行时法线计算和双三次平滑等。我正在使用 AMD HD 5750。使用更好的代码和预烘焙的法线,它仍然可以更快:D。(以最大 120 w/o 正常计算运行。)

哦,如果您在着色器中置换顶点,您只能发送 x 和 z 坐标。它也会更快。

在此处输入图像描述 很多顶点。

于 2014-02-10T14:47:35.907 回答