Мерцание и разрушение шейдеров GLSL/OpenGL

Я только начал с тесселяции OpenGL и столкнулся с проблемой. Я - тесселлирующая серия патчей, образованных по одной вершине. Эти вершины/патчи структурированы в виде сетки, чтобы позднее сформировать ландшафт, создаваемый шумом Перлина.

Проблема, с которой я столкнулся, заключается в том, что начиная со второго патча и после каждого 5 th патча после этого иногда бывает много тесселяции (не так, как я настроил), но большую часть времени он вообще не тесселируется.

Так же:

  39M6uA8.png

Два белых круга отмечают высоко/более тесселированные патчи. Также обратите внимание на шаблон несвязанных патчей.

Странно, что он работает на моей 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);

Vertex Shader:

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

Ответы

Ответ 1

Вы должны использовать треугольник/квадрат тесселяции, в котором каждый патч имеет 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

Обратите внимание, что как внутренние, так и внешние данные вершин являются массивом.

Тессев прост:

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, так как это может быть легко обработано графическим процессором (3 точки всегда лежат в плоскости). Итак, минимальное количество вершин патчей должно быть 3, для треугольника. Мне нравятся квадроциклы, потому что он проще индексировать, а стоимость памяти меньше. Он также будет разделен на треугольники во время тесселяции. http://www.informit.com/articles/article.aspx?p=2120983 Кроме того, существует другой тип - тесселяция изоляции. (проверьте ссылки, второй очень хорош.)

В общем, попробуйте с помощью квадов или треугольников и установите контрольные вершины на 4 (или 3). Мой (довольно сложный) рельефный шейдер здесь, с усеченным отрезком, тесселяционным шейдером, отбирающим для местности, основанной на геоклипаме. Кроме того, без тесселяции он работает с вершинным морфом в вершинном шейдере. Возможно, какая-то часть этого кода будет полезна. http://speedy.sh/TAvPR/gshader.txt

Сцена с тесселяцией около 4 пикселов/треугольников работает при 75 FPS (с фреймами) с обычным вычислением времени выполнения и бикубическим сглаживанием и другими вещами. Я использую AMD HD 5750. Он все еще может быть намного быстрее с лучшим кодом и предварительно испеченными нормалями: D. (работает при макс. 120 w/o нормальном расчете).

О, и вы можете отправлять координаты x и z только в том случае, если вы смещаете вершину в шейдере. Это также будет быстрее.

enter image description here Множество вершин.