GLSL/OpenGL着色器曲面细分出现闪烁和失效的问题

14

我刚开始接触OpenGL镶嵌技术,并遇到一些问题。我正在对由一个顶点形成的一系列补丁进行细分。这些顶点/补丁以网格状的方式结构化,以后会形成由Perlin噪声生成的地形。

我遇到的问题是,从第二个补丁开始,每隔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)。


你如何设置细分级别? - Jaa-c
@Jaa-c 我这样设置它们: GLuint loc = m_fxTerrain->GetUniform("OuterTessFactor"); glUniform1f(loc, 8.0f); 但我也尝试在着色器中硬编码它们,但仍然存在同样的错误。 - toi
1个回答

11

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

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

提示:在地形上使用 drawelements,对于基于2D位移的网格有更好的性能表现。
在控制着色器中使用

layout (vertices = 4) out;

由于您的路径有4个控制点,所以顺序仍然很重要(逆时针/顺时针)。
个人而言,我不喜欢使用内置变量,因此对于顶点着色器,您可以将顶点数据发送到tesscontrol,像这样:

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

Tess 控制:

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
注意,输入和输出顶点数据都是数组。
这个tesselator很简单:
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,用于三角形。我喜欢四边形,因为它更容易索引,内存成本更低。在细分期间它也会被分成三角形。http://www.informit.com/articles/article.aspx?p=2120983 此外,还有另一种类型的隔离线细分。(查看链接,第二个链接非常好。)

总之,尝试使用四边形或三角形,并将控制顶点设置为4(或3)。我的(相当复杂的)地形着色器配备了视锥体剪裁、基于地形的geoclipmap的细分着色器剪裁,以及在顶点着色器中的顶点变形。也许这段代码的某些部分会很有用。http://speedy.sh/TAvPR/gshader.txt

一个大约每个三角形4像素的细分场景以75 FPS(带有fraps)运行,具有运行时法线计算、双三次平滑和其他功能。我使用的是AMD HD 5750。如果有更好的代码和预先烘焙的法线,它仍然可以更快:D。(最高可在无法计算法线时达到120个)

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

enter image description here 许多顶点。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接