<p>I have a rendering system that I use for drawing height maps. The system uses triangle strips of indexed verts in a vao. The system has worked fine on the 3 Nvidia cards I have tried it on across several versions of windows and OSX. I don't own any Systems with an ATI card but I sent the application to a colleague to look at for me, and on his machine no output is produced in the 3d viewport. He has a Radeon HD 7800 and is using windows 10 which is one of the operating systems I have tried it on with an Nvidia card. Running OpenGL extensions viewer indicates that his card supports a modern range of capabilities up to GL 4.5.</p>

<p>I attached an opengl debug callback. The shader program compiles fine and the vao is created without errors. There are no errors on draw either. The shaders use #version 330 core but I tried dropping down to #version 130 and simplifying the shaders to basically output the vertex position and a flat color, and it still works fine on nvidia but outputs nothing on ATI, so I doubt the shaders are the issue.</p>

<p>I suspect it has something to do with the way the vao is set up or possibly some feature that is missing in ATI drivers or hardware.</p>

<p>The following two blocks of code are in Kotlin using LWJGL3. The relevant GL calls are all the same as from Java though. They are a very tiny wrapper to the <code>C</code> calls.</p>

<p>The vao gets set up like this: </p>

<pre><code>vao = glGenVertexArrays()

if (vao &gt; 0) {

    val stride = floatsPerVertex * 4
    glBindVertexArray(vao)

    val vbo = glGenBuffers()
    val ibo = glGenBuffers()

    if (vbo &gt; 0 &amp;&amp; ibo &gt; 0) {
        glBindBuffer(GL_ARRAY_BUFFER, vbo)

        glBufferData(GL_ARRAY_BUFFER, heightMapVertexData, GL_STATIC_DRAW)


        if (positionAttribute.location &gt;= 0) {
            glEnableVertexAttribArray(positionAttribute.location)
            glVertexAttribPointer(positionAttribute.location, 2, GL_FLOAT, false, stride, 0)
        }

        if (uvAttribute.location &gt;= 0) {
            glEnableVertexAttribArray(uvAttribute.location)
            glVertexAttribPointer(uvAttribute.location, 2, GL_FLOAT, false, stride, 8)
        }

        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo)

        glBufferData(GL_ELEMENT_ARRAY_BUFFER, heightMapIndexData, GL_STATIC_DRAW)

    } else {
        throw RuntimeException("error setting up buffers")
    }

    glBindVertexArray(0)
} else {
    throw RuntimeException("error generating vao")
}
</code></pre>

<p>A draw call does this:</p>

<pre><code>glDisable(GL_BLEND)
glDisable(GL_CULL_FACE)
glEnable(GL_DEPTH_TEST)
glEnable(GL_SCISSOR_TEST)
glEnable(GL_MULTISAMPLE)
glClearColor(background.r, background.g, background.b, background.a)
glScissor(xPosition, flippedY, width, height)
glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT)
glViewport(xPosition, flippedY, width, height)
glUseProgram(heightMapProgram)
glUniformMatrix4fv(mvMatrixUniform.location, false, mvMatrix.get(0, floatBuffer))
glUniformMatrix3fv(nMatrixUniform.location, false, normalMatrix.get(0, floatBuffer))
glUniformMatrix4fv(mvpMatrixUniform.location, false, mvpMatrix.get(0, floatBuffer))
glUniform3f(lightDirectionUniform.location, lightDirection.x, lightDirection.y, lightDirection.z)
glUniform4f(color1Uniform.location, 0.157f, 0.165f, 0.424f, 1.0f)
glUniform4f(color2Uniform.location, 0.459f, 0.761f, 0.859f, 1.0f)
glUniform4f(color3Uniform.location, 0.353f, 0.706f, 0.275f, 1.0f)
glUniform4f(color4Uniform.location, 0.922f, 0.922f, 0.157f, 1.0f)
glUniform4f(color5Uniform.location, 0.835f, 0.176f, 0.165f, 1.0f)
glUniform4f(color6Uniform.location, 0.955f, 0.955f, 0.955f, 1.0f)
glUniform4f(ambientUniform.location, 0.1f, 0.1f, 0.1f, 1.0f)
glUniform4f(diffuseUniform.location, 0.6f, 0.6f, 0.6f, 1.0f)
glUniform4f(specularUniform.location, 0.85f, 0.85f, 0.85f, 1.0f)
glUniform1f(shininessUniform.location, 1.7f)
glUniform1f(heightScaleUniform.location, heightMapScaleFactor.value)
glUniform1f(uvScaleUniform.location, heightMap.width / textureResolution)
glUniform1i(heightMapTextureUniform.location, 0)
glActiveTexture(GL_TEXTURE0)
glBindTexture(GL_TEXTURE_2D, textureId)
glBindVertexArray(vao)
glDrawElements(GL_TRIANGLE_STRIP, indexCount, GL_UNSIGNED_INT, 0)
glBindVertexArray(0)
glDisable(GL_SCISSOR_TEST)
</code></pre>

<p>Here are the shaders for what they're worth even though reducing these to #version 130 doesn't help and it doesn't work with or without the commented code on ATI. Nvidia runs all variations. The shaders themselves don't appear to have any impact on the issue.</p>

<p>vertex:</p>

<pre><code>#version 330

uniform mat4 modelViewProjectionMatrix;
uniform float heightScale;
uniform sampler2D heightMapTexture;

in vec2 position;
in vec2 uv;

out VertexData {
    vec2 position;
    vec2 uv;
} VertexOut;

void main () {
    VertexOut.uv = uv;
    VertexOut.position = position;
    float height = texture(heightMapTexture, uv).r * heightScale;
    gl_Position = modelViewProjectionMatrix * vec4(position, height, 1.0);
}
</code></pre>

<p>fragment:</p>

<pre><code>#version 330

uniform mat4 modelViewProjectionMatrix;
uniform mat4 modelViewMatrix;
uniform mat3 normalMatrix;
uniform vec3 lightDirection;
uniform vec4 color1;
uniform vec4 color2;
uniform vec4 color3;
uniform vec4 color4;
uniform vec4 color5;
uniform vec4 color6;
uniform vec4 ambientColor;
uniform vec4 diffuseColor;
uniform vec4 specularColor;
uniform float shininess;
uniform float heightScale;
uniform float uvScale;
uniform sampler2D heightMapTexture;


in VertexData {
    vec2 position;
    vec2 uv;
} VertexIn;

out vec4 colorOut;

//const ivec3 off = ivec3(-1, 0, 1);
//
//const float gamma = 2.2;

//vec3 toLinear(vec3 v) {
//  return pow(v, vec3(gamma));
//}
//
//vec4 toLinear(vec4 v) {
//  return vec4(toLinear(v.rgb), v.a);
//}
//
//vec3 toGamma(vec3 v) {
//  return pow(v, vec3(1.0 / gamma));
//}
//
//vec4 toGamma(vec4 v) {
//  return vec4(toGamma(v.rgb), v.a);
//}

void main() {
//    if (!gl_FrontFacing) {
//        colorOut = vec4(0.076f, 0.082f, 0.212f, 1.0f);
//        return;
//    }
    colorOut = color1;
//    vec2 size = vec2(uvScale * 2, 0.0);
//    float unscaledHeight = texture(heightMapTexture, VertexIn.uv).r;
//    float height = unscaledHeight * heightScale;
//    float westPixel = textureOffset(heightMapTexture, VertexIn.uv, off.xy).r * heightScale;
//    float eastPixel = textureOffset(heightMapTexture, VertexIn.uv, off.zy).r * heightScale;
//    float northPixel = textureOffset(heightMapTexture, VertexIn.uv, off.yx).r * heightScale;
//    float southPixel = textureOffset(heightMapTexture, VertexIn.uv, off.yz).r * heightScale;
//    float northWestPixel = textureOffset(heightMapTexture, VertexIn.uv, off.xx).r * heightScale;
//    float southEastPixel = textureOffset(heightMapTexture, VertexIn.uv, off.zz).r * heightScale;
//    float northEastPixel = textureOffset(heightMapTexture, VertexIn.uv, off.zx).r * heightScale;
//    float southWestPixel = textureOffset(heightMapTexture, VertexIn.uv, off.xz).r * heightScale;
//    vec3 va = normalize(vec3(size.xy, eastPixel - westPixel));
//    vec3 vb = normalize(vec3(size.yx, southPixel - northPixel));
//    vec3 vc = normalize(vec3(size.xx, southEastPixel - northWestPixel));
//    vec3 vd = normalize(vec3(size.x, -size.x, southWestPixel - northEastPixel));
//    vec3 crossNormal = cross(va, vb);
//    crossNormal = normalize(vec3(crossNormal.x, -crossNormal.y, crossNormal.z));
//    vec3 xNormal = cross(vd, vc);
//    xNormal = normalize(vec3(xNormal.y, -xNormal.x, xNormal.z));
//    vec3 modelNormal = normalize(xNormal + crossNormal);
//    vec3 normal = normalize(normalMatrix * modelNormal);
//    float diffuse = max(dot(normal, lightDirection), 0.0) + 0.06;
//    vec4 lowColor;
//    vec4 highColor;
//    float interpolation;
//    if (unscaledHeight &lt;= 0.05) {
//        lowColor = color1;
//        highColor = color1;
//        interpolation = 0.5;
//    } else if (unscaledHeight &lt;= 0.19) {
//        lowColor = color1;
//        highColor = color2;
//        interpolation = (unscaledHeight - 0.05) * 7.1426;
//    } else if (unscaledHeight &lt;= 0.24) {
//        lowColor = color2;
//        highColor = color2;
//        interpolation = 0.5;
//    } else if (unscaledHeight &lt;= 0.38) {
//        lowColor = color2;
//        highColor = color3;
//        interpolation = (unscaledHeight - 0.24) * 7.1426;
//    } else if (unscaledHeight &lt;= 0.43) {
//        lowColor = color3;
//        highColor = color3;
//        interpolation = 0.5;
//    } else if (unscaledHeight &lt;= 0.57) {
//        lowColor = color3;
//        highColor = color4;
//        interpolation = (unscaledHeight - 0.43) * 7.1426;
//    } else if (unscaledHeight &lt;= 0.62) {
//        lowColor = color4;
//        highColor = color4;
//        interpolation = 0.5;
//    } else if (unscaledHeight &lt;= 0.76) {
//        lowColor = color4;
//        highColor = color5;
//        interpolation = (unscaledHeight - 0.62) * 7.1426;
//    } else if (unscaledHeight &lt;= 0.81) {
//        lowColor = color5;
//        highColor = color5;
//        interpolation = 0.5;
//    } else if (unscaledHeight &lt;= 0.95) {
//        lowColor = color5;
//        highColor = color6;
//        interpolation = (unscaledHeight - 0.81) * 7.1426;
//    } else {
//        lowColor = color6;
//        highColor = color6;
//        interpolation = 0.5;
//    }
//    vec4 baseColor = mix(toLinear(lowColor), toLinear(highColor), interpolation);
//    vec4 lightColor = toLinear(vec4(1.0));
//    vec4 diffuseColor = vec4(baseColor.rgb * lightColor.rgb * diffuse, 1.0);
//    vec4 spec = vec4(0.0);
//    vec3 eye = -normalize(vec3(modelViewMatrix * vec4(VertexIn.position.xy, height, 1.0)));
//    float intensity = max(dot(normal, lightDirection), 0.0);
//    if (intensity &gt; 0.0) {
//        vec3 halfVector = normalize(lightDirection + eye);
//        spec = toLinear(specularColor) * pow(max(dot(halfVector, normal), 0.0), shininess);
//    }
//    colorOut = toGamma(diffuseColor * max(intensity * toLinear(diffuseColor) + spec, toLinear(ambientColor)));
}
</code></pre>

<p>The geometry itself is correctly formed in <code>heightMapTexture</code> to the best of my knowledge because I've inspected it piece by piece in a debugger and because the strips come out correctly on Nvidia cards. I am assuming there are no fundamental differences in how <code>GL_TRIANGLE_STRIP</code> works between Nvidia and ATI. If there is a difference then that's exactly the type of thing this question is trying to figure out. The description of triangle strips in the OpenGL spec doesn't seem to leave much room for differences here if any, so it seems likely that if the strips themselves are formed correctly according to the spec they should work everywhere or nowhere.</p>

<p>The geometry is a basic hexagonally arranged triangle grid with degenerates in between the strips. I'd like to figure out what features are causing this to output nothing except the <code>glClear()</code> on ATI cards so I can get people with different machines looking at the application.</p>

<p>Here is an example of what it outputs on all the Nvidia machines across various windows and OSX flavors, in case that helps</p>

<p><a href="https://i.sstatic.net/NTg6O.jpg" rel="nofollow noreferrer"><img src="https://i.sstatic.net/NTg6O.jpg" alt="sample output"></a></p>

<p>On ATI it displays the gray scissor clear rectangle that is drawn by the snippet:</p>

<pre><code>glEnable(GL_SCISSOR_TEST)
glClearColor(background.r, background.g, background.b, background.a)
glScissor(xPosition, flippedY, width, height)
glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT)
</code></pre>

<p>Nothing else besides the gray rectangle is displayed.</p>

<p>In summary, for someone who has something like this working on an ATI card, are these the OpenGL calls that you would use to get it drawing on an ATI card or is there some obvious feature I'm using that's not supported by ATI that's causing the whole thing to not draw. I'm looking for possible differences between how ATI and Nvidia would handle this geometry.</p>

<p>P.S. I added this note to answer some of the comments that suggest I should go back to a single triangle and debug from scratch on my own. The reason I'm asking this question is that I don't currently have access to ATI hardware. I'm in South America until May and hardware availability is basically nil where I am. I have several workstations that I brought with me for work, but they all have Nvidia hardware. I have to do a full build and then ship the product to someone in a different time zone each time I attempt to correct the issue. I need to be more purposeful in my fixes than going back to drawing a triangle and working up from scratch. I would love to debug it myself on the affected hardware step by step. I just can't right now. I need to get the code closer to a likely solution before sending another build, so I'm looking for some educated guesses from people with direct experience with this type of geometry on ATI hardware. I would greatly appreciate more understanding than "That's what debug is for. Go back to a triangle.", because those comments really don't help me or anyone else reading this later. This problem seems like something that someone must have encountered before because it seems like a pretty standard way to draw a heightmap. I think that this question will help anyone else who has this problem in the future. I've lost several days to this, and there is no published answer to anything similar that I could find. If this remains unanswered until May, when I return home, I will write the answer myself and remove this note, but I hope it doesn't come to that.</p>

CC BY-SA 3.0
2016-12-31T17:41:26.053Z
Have you seen that drawing works if you take out the degenerate triangles? If not, then what makes you think that degenerate triangles have *anything* to do with your problem? "*The geometry itself is correctly formed because it works on Nvidia cards.*" No, that's not a reasonable conclusion to come to from that evidence. "Works on NVIDIA" only means "works on NVIDIA". There's plenty of stuff NVIDIA's GL implementations do that are outside the specification.
Nicol Bolas
CC BY-SA 3.0
2016-12-31T17:42:33.573Z
"*On ATI it displays the gray scissor clear box but that's it.*" What "gray scissor box" are you referring to?
Nicol Bolas
CC BY-SA 3.0
2016-12-31T17:44:28.373Z
The only way to resolve problems like this is to boil them down to the simplest elements: draw one triangle. Get that working. Then keep adding elements until you find the one that causes things to disappear. That's your problem.
Nicol Bolas
CC BY-SA 3.0
2016-12-31T17:52:30.003Z
I do not suspect that the degenerates have anything to do with the issue. I don't think I said that I did. I was simply describing the format of the geometry that is being sent that is not displaying correctly.By saying that the geometry itself is correctly formed what I meant is that the indices create triangle strips with the correct vertex indices specified in the correct order which produce the correct number of triangles with the correct alternating winding order. I was simply referring to the actual vertex data in `heightMapVertexData`. It is what i expect it to be.
Adam Brown
CC BY-SA 3.0
2016-12-31T17:55:49.543Z
"*I do not suspect that the degenerates have anything to do with the issue. I don't think I said that I did.*" But that is what you ask in the title of the question: "Are ATI cards lacking some feature that would allow them to draw triangle strips connected by degenerates with a single draw call using a vao?" So either that's what you're asking about or the title is misleading.
Nicol Bolas
CC BY-SA 3.0
2016-12-31T17:58:22.927Z
I am very familiar with debugging things from the ground up by adding one element at a time. That's how I got the system working on my Nvidia cards in the first place. That and reading the OpenGL spec.

Unfortunately I have no access to ATI hardware where I am right now so that's not an option for debugging this issue for another few months when I can get back home where I have ATI hardware.

I was hoping there was some simple well known reason ATI cards don't work with what I am doing that I just missed. That way I could get more eyes on my application sooner.
Adam Brown
CC BY-SA 3.0
2016-12-31T18:01:26.533Z
I have changed the title to be less specific about the geometry being drawn.
Adam Brown
CC BY-SA 3.0
2016-12-31T18:03:17.857Z
The gray scissor box I'm referring to is the one drawn by:
Adam Brown
CC BY-SA 3.0
2016-12-31T18:03:40.903Z
glEnable(GL_SCISSOR_TEST)
glEnable(GL_MULTISAMPLE)
glClearColor(background.r, background.g, background.b, background.a)
glScissor(xPosition, flippedY, width, height)
glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT)
Adam Brown
CC BY-SA 3.0
2016-12-31T21:37:34.640Z
Ok. I have added a better description of the gray scissor box to the question as you recommended.
Adam Brown
CC BY-SA 3.0
2016-12-31T22:48:39.403Z
Yes the ATIs (and Nvidias) optimize out attributes if they aren't used in the body of the shader. `if (positionAttribute.location >= 0) {` guards against enabling specific attributes if they are optimized out by the shader compiler. This had no impact on the output on ATI cards but it did fix an 0x501 error from the Nvidia cards when trying to enable an inactive attribute. The Nvidias still rendered fine regardless of the `0x501 GL_INVALID_VALUE` error however.
Adam Brown
CC BY-SA 3.0
2016-12-31T22:54:37.843Z
In the case of the shaders in this question both vertex attributes shown here `positionAttribute` and `uvAttribute` are non-negative. The code is shared in other locations and the guard against the negative attributes is for those other cases. The 0x501 I mentioned was from those other cases as well.
Adam Brown
CC BY-SA 3.0
2017-01-01T12:35:51.620Z
It's Kotlin. I didn't think the language was too important in the post in the is case since the GL calls are all the same. I'll update the question to mention that. `or` in this way is bitwise in kotlin. `||` is logical or in Kotlin.
Adam Brown
<opengl>
Is there a difference in how ATI cards draw triangle strips vs Nvidia cards that would cause ATI cards to output nothing?
Adam Brown
# Is there a difference in how ATI cards draw triangle strips vs Nvidia cards that would cause ATI cards to output nothing?

(Asked by: username_0 on 2016-12-31)

I have a rendering system that I use for drawing height maps. The system uses triangle strips of indexed verts in a vao. The system has worked fine on the 3 Nvidia cards I have tried it on across several versions of windows and OSX. I don't own any Systems with an ATI card but I sent the application to a colleague to look at for me, and on his machine no output is produced in the 3d viewport. He has a Radeon HD 7800 and is using windows 10 which is one of the operating systems I have tried it on with an Nvidia card. Running OpenGL extensions viewer indicates that his card supports a modern range of capabilities up to GL 4.5.

I attached an opengl debug callback. The shader program compiles fine and the vao is created without errors. There are no errors on draw either. The shaders use #version 330 core but I tried dropping down to #version 130 and simplifying the shaders to basically output the vertex position and a flat color, and it still works fine on nvidia but outputs nothing on ATI, so I doubt the shaders are the issue.

I suspect it has something to do with the way the vao is set up or possibly some feature that is missing in ATI drivers or hardware.

The following two blocks of code are in Kotlin using LWJGL3. The relevant GL calls are all the same as from Java though. They are a very tiny wrapper to the `C` calls.

The vao gets set up like this:

```
vao = glGenVertexArrays()

if (vao > 0) {

    val stride = floatsPerVertex * 4
    glBindVertexArray(vao)

    val vbo = glGenBuffers()
    val ibo = glGenBuffers()

    if (vbo > 0 && ibo > 0) {
        glBindBuffer(GL_ARRAY_BUFFER, vbo)

        glBufferData(GL_ARRAY_BUFFER, heightMapVertexData, GL_STATIC_DRAW)

        if (positionAttribute.location >= 0) {
            glEnableVertexAttribArray(positionAttribute.location)
            glVertexAttribPointer(positionAttribute.location, 2, GL_FLOAT, false, stride, 0)
        }

        if (uvAttribute.location >= 0) {
            glEnableVertexAttribArray(uvAttribute.location)
            glVertexAttribPointer(uvAttribute.location, 2, GL_FLOAT, false, stride, 8)
        }

        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo)

        glBufferData(GL_ELEMENT_ARRAY_BUFFER, heightMapIndexData, GL_STATIC_DRAW)

    } else {
        throw RuntimeException("error setting up buffers")
    }

    glBindVertexArray(0)
} else {
    throw RuntimeException("error generating vao")
}

```

A draw call does this:

```
glDisable(GL_BLEND)
glDisable(GL_CULL_FACE)
glEnable(GL_DEPTH_TEST)
glEnable(GL_SCISSOR_TEST)
glEnable(GL_MULTISAMPLE)
glClearColor(background.r, background.g, background.b, background.a)
glScissor(xPosition, flippedY, width, height)
glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT)
glViewport(xPosition, flippedY, width, height)
glUseProgram(heightMapProgram)
glUniformMatrix4fv(mvMatrixUniform.location, false, mvMatrix.get(0, floatBuffer))
glUniformMatrix3fv(nMatrixUniform.location, false, normalMatrix.get(0, floatBuffer))
glUniformMatrix4fv(mvpMatrixUniform.location, false, mvpMatrix.get(0, floatBuffer))
glUniform3f(lightDirectionUniform.location, lightDirection.x, lightDirection.y, lightDirection.z)
glUniform4f(color1Uniform.location, 0.157f, 0.165f, 0.424f, 1.0f)
glUniform4f(color2Uniform.location, 0.459f, 0.761f, 0.859f, 1.0f)
glUniform4f(color3Uniform.location, 0.353f, 0.706f, 0.275f, 1.0f)
glUniform4f(color4Uniform.location, 0.922f, 0.922f, 0.157f, 1.0f)
glUniform4f(color5Uniform.location, 0.835f, 0.176f, 0.165f, 1.0f)
glUniform4f(color6Uniform.location, 0.955f, 0.955f, 0.955f, 1.0f)
glUniform4f(ambientUniform.location, 0.1f, 0.1f, 0.1f, 1.0f)
glUniform4f(diffuseUniform.location, 0.6f, 0.6f, 0.6f, 1.0f)
glUniform4f(specularUniform.location, 0.85f, 0.85f, 0.85f, 1.0f)
glUniform1f(shininessUniform.location, 1.7f)
glUniform1f(heightScaleUniform.location, heightMapScaleFactor.value)
glUniform1f(uvScaleUniform.location, heightMap.width / textureResolution)
glUniform1i(heightMapTextureUniform.location, 0)
glActiveTexture(GL_TEXTURE0)
glBindTexture(GL_TEXTURE_2D, textureId)
glBindVertexArray(vao)
glDrawElements(GL_TRIANGLE_STRIP, indexCount, GL_UNSIGNED_INT, 0)
glBindVertexArray(0)
glDisable(GL_SCISSOR_TEST)

```

Here are the shaders for what they're worth even though reducing these to #version 130 doesn't help and it doesn't work with or without the commented code on ATI. Nvidia runs all variations. The shaders themselves don't appear to have any impact on the issue.

vertex:

```
#version 330

uniform mat4 modelViewProjectionMatrix;
uniform float heightScale;
uniform sampler2D heightMapTexture;

in vec2 position;
in vec2 uv;

out VertexData {
    vec2 position;
    vec2 uv;
} VertexOut;

void main () {
    VertexOut.uv = uv;
    VertexOut.position = position;
    float height = texture(heightMapTexture, uv).r * heightScale;
    gl_Position = modelViewProjectionMatrix * vec4(position, height, 1.0);
}

```

fragment:

```
#version 330

uniform mat4 modelViewProjectionMatrix;
uniform mat4 modelViewMatrix;
uniform mat3 normalMatrix;
uniform vec3 lightDirection;
uniform vec4 color1;
uniform vec4 color2;
uniform vec4 color3;
uniform vec4 color4;
uniform vec4 color5;
uniform vec4 color6;
uniform vec4 ambientColor;
uniform vec4 diffuseColor;
uniform vec4 specularColor;
uniform float shininess;
uniform float heightScale;
uniform float uvScale;
uniform sampler2D heightMapTexture;

in VertexData {
    vec2 position;
    vec2 uv;
} VertexIn;

out vec4 colorOut;

//const ivec3 off = ivec3(-1, 0, 1);
//
//const float gamma = 2.2;

//vec3 toLinear(vec3 v) {
//  return pow(v, vec3(gamma));
//}
//
//vec4 toLinear(vec4 v) {
//  return vec4(toLinear(v.rgb), v.a);
//}
//
//vec3 toGamma(vec3 v) {
//  return pow(v, vec3(1.0 / gamma));
//}
//
//vec4 toGamma(vec4 v) {
//  return vec4(toGamma(v.rgb), v.a);
//}

void main() {
//    if (!gl_FrontFacing) {
//        colorOut = vec4(0.076f, 0.082f, 0.212f, 1.0f);
//        return;
//    }
    colorOut = color1;
//    vec2 size = vec2(uvScale * 2, 0.0);
//    float unscaledHeight = texture(heightMapTexture, VertexIn.uv).r;
//    float height = unscaledHeight * heightScale;
//    float westPixel = textureOffset(heightMapTexture, VertexIn.uv, off.xy).r * heightScale;
//    float eastPixel = textureOffset(heightMapTexture, VertexIn.uv, off.zy).r * heightScale;
//    float northPixel = textureOffset(heightMapTexture, VertexIn.uv, off.yx).r * heightScale;
//    float southPixel = textureOffset(heightMapTexture, VertexIn.uv, off.yz).r * heightScale;
//    float northWestPixel = textureOffset(heightMapTexture, VertexIn.uv, off.xx).r * heightScale;
//    float southEastPixel = textureOffset(heightMapTexture, VertexIn.uv, off.zz).r * heightScale;
//    float northEastPixel = textureOffset(heightMapTexture, VertexIn.uv, off.zx).r * heightScale;
//    float southWestPixel = textureOffset(heightMapTexture, VertexIn.uv, off.xz).r * heightScale;
//    vec3 va = normalize(vec3(size.xy, eastPixel - westPixel));
//    vec3 vb = normalize(vec3(size.yx, southPixel - northPixel));
//    vec3 vc = normalize(vec3(size.xx, southEastPixel - northWestPixel));
//    vec3 vd = normalize(vec3(size.x, -size.x, southWestPixel - northEastPixel));
//    vec3 crossNormal = cross(va, vb);
//    crossNormal = normalize(vec3(crossNormal.x, -crossNormal.y, crossNormal.z));
//    vec3 xNormal = cross(vd, vc);
//    xNormal = normalize(vec3(xNormal.y, -xNormal.x, xNormal.z));
//    vec3 modelNormal = normalize(xNormal + crossNormal);
//    vec3 normal = normalize(normalMatrix * modelNormal);
//    float diffuse = max(dot(normal, lightDirection), 0.0) + 0.06;
//    vec4 lowColor;
//    vec4 highColor;
//    float interpolation;
//    if (unscaledHeight <= 0.05) {
//        lowColor = color1;
//        highColor = color1;
//        interpolation = 0.5;
//    } else if (unscaledHeight <= 0.19) {
//        lowColor = color1;
//        highColor = color2;
//        interpolation = (unscaledHeight - 0.05) * 7.1426;
//    } else if (unscaledHeight <= 0.24) {
//        lowColor = color2;
//        highColor = color2;
//        interpolation = 0.5;
//    } else if (unscaledHeight <= 0.38) {
//        lowColor = color2;
//        highColor = color3;
//        interpolation = (unscaledHeight - 0.24) * 7.1426;
//    } else if (unscaledHeight <= 0.43) {
//        lowColor = color3;
//        highColor = color3;
//        interpolation = 0.5;
//    } else if (unscaledHeight <= 0.57) {
//        lowColor = color3;
//        highColor = color4;
//        interpolation = (unscaledHeight - 0.43) * 7.1426;
//    } else if (unscaledHeight <= 0.62) {
//        lowColor = color4;
//        highColor = color4;
//        interpolation = 0.5;
//    } else if (unscaledHeight <= 0.76) {
//        lowColor = color4;
//        highColor = color5;
//        interpolation = (unscaledHeight - 0.62) * 7.1426;
//    } else if (unscaledHeight <= 0.81) {
//        lowColor = color5;
//        highColor = color5;
//        interpolation = 0.5;
//    } else if (unscaledHeight <= 0.95) {
//        lowColor = color5;
//        highColor = color6;
//        interpolation = (unscaledHeight - 0.81) * 7.1426;
//    } else {
//        lowColor = color6;
//        highColor = color6;
//        interpolation = 0.5;
//    }
//    vec4 baseColor = mix(toLinear(lowColor), toLinear(highColor), interpolation);
//    vec4 lightColor = toLinear(vec4(1.0));
//    vec4 diffuseColor = vec4(baseColor.rgb * lightColor.rgb * diffuse, 1.0);
//    vec4 spec = vec4(0.0);
//    vec3 eye = -normalize(vec3(modelViewMatrix * vec4(VertexIn.position.xy, height, 1.0)));
//    float intensity = max(dot(normal, lightDirection), 0.0);
//    if (intensity > 0.0) {
//        vec3 halfVector = normalize(lightDirection + eye);
//        spec = toLinear(specularColor) * pow(max(dot(halfVector, normal), 0.0), shininess);
//    }
//    colorOut = toGamma(diffuseColor * max(intensity * toLinear(diffuseColor) + spec, toLinear(ambientColor)));
}

```

The geometry itself is correctly formed in `heightMapTexture` to the best of my knowledge because I've inspected it piece by piece in a debugger and because the strips come out correctly on Nvidia cards. I am assuming there are no fundamental differences in how `GL_TRIANGLE_STRIP` works between Nvidia and ATI. If there is a difference then that's exactly the type of thing this question is trying to figure out. The description of triangle strips in the OpenGL spec doesn't seem to leave much room for differences here if any, so it seems likely that if the strips themselves are formed correctly according to the spec they should work everywhere or nowhere.

The geometry is a basic hexagonally arranged triangle grid with degenerates in between the strips. I'd like to figure out what features are causing this to output nothing except the `glClear()` on ATI cards so I can get people with different machines looking at the application.

Here is an example of what it outputs on all the Nvidia machines across various windows and OSX flavors, in case that helps

[![sample output](https://i.sstatic.net/NTg6O.jpg)](https://i.sstatic.net/NTg6O.jpg)

On ATI it displays the gray scissor clear rectangle that is drawn by the snippet:

```
glEnable(GL_SCISSOR_TEST)
glClearColor(background.r, background.g, background.b, background.a)
glScissor(xPosition, flippedY, width, height)
glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT)

```

Nothing else besides the gray rectangle is displayed.

In summary, for someone who has something like this working on an ATI card, are these the OpenGL calls that you would use to get it drawing on an ATI card or is there some obvious feature I'm using that's not supported by ATI that's causing the whole thing to not draw. I'm looking for possible differences between how ATI and Nvidia would handle this geometry.

P.S. I added this note to answer some of the comments that suggest I should go back to a single triangle and debug from scratch on my own. The reason I'm asking this question is that I don't currently have access to ATI hardware. I'm in South America until May and hardware availability is basically nil where I am. I have several workstations that I brought with me for work, but they all have Nvidia hardware. I have to do a full build and then ship the product to someone in a different time zone each time I attempt to correct the issue. I need to be more purposeful in my fixes than going back to drawing a triangle and working up from scratch. I would love to debug it myself on the affected hardware step by step. I just can't right now. I need to get the code closer to a likely solution before sending another build, so I'm looking for some educated guesses from people with direct experience with this type of geometry on ATI hardware. I would greatly appreciate more understanding than "That's what debug is for. Go back to a triangle.", because those comments really don't help me or anyone else reading this later. This problem seems like something that someone must have encountered before because it seems like a pretty standard way to draw a heightmap. I think that this question will help anyone else who has this problem in the future. I've lost several days to this, and there is no published answer to anything similar that I could find. If this remains unanswered until May, when I return home, I will write the answer myself and remove this note, but I hope it doesn't come to that.

**Tags:** opengl

> **Comments:**
> **username_1**: Have you seen that drawing works if you take out the degenerate triangles? If not, then what makes you think that degenerate triangles have *anything* to do with your problem? "*The geometry itself is correctly formed because it works on Nvidia cards.*" No, that's not a reasonable conclusion to come to from that evidence. "Works on NVIDIA" only means "works on NVIDIA". There's plenty of stuff NVIDIA's GL implementations do that are outside the specification.  
> **username_1**: "*On ATI it displays the gray scissor clear box but that's it.*" What "gray scissor box" are you referring to?  
> **username_1**: The only way to resolve problems like this is to boil them down to the simplest elements: draw one triangle. Get that working. Then keep adding elements until you find the one that causes things to disappear. That's your problem.  
> **username_0**: I do not suspect that the degenerates have anything to do with the issue. I don't think I said that I did. I was simply describing the format of the geometry that is being sent that is not displaying correctly.By saying that the geometry itself is correctly formed what I meant is that the indices create triangle strips with the correct vertex indices specified in the correct order which produce the correct number of triangles with the correct alternating winding order. I was simply referring to the actual vertex data in `heightMapVertexData`. It is what i expect it to be.  
> **username_1**: "*I do not suspect that the degenerates have anything to do with the issue. I don't think I said that I did.*" But that is what you ask in the title of the question: "Are ATI cards lacking some feature that would allow them to draw triangle strips connected by degenerates with a single draw call using a vao?" So either that's what you're asking about or the title is misleading.  
> **username_0**: I am very familiar with debugging things from the ground up by adding one element at a time. That's how I got the system working on my Nvidia cards in the first place. That and reading the OpenGL spec.  Unfortunately I have no access to ATI hardware where I am right now so that's not an option for debugging this issue for another few months when I can get back home where I have ATI hardware.  I was hoping there was some simple well known reason ATI cards don't work with what I am doing that I just missed. That way I could get more eyes on my application sooner.  
> **username_0**: I have changed the title to be less specific about the geometry being drawn.  
> **username_0**: The gray scissor box I'm referring to is the one drawn by:  
> **username_0**: glEnable(GL_SCISSOR_TEST) glEnable(GL_MULTISAMPLE) glClearColor(background.r, background.g, background.b, background.a) glScissor(xPosition, flippedY, width, height) glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT)  
> **username_0**: Ok. I have added a better description of the gray scissor box to the question as you recommended.  
> **username_0**: Yes the ATIs (and Nvidias) optimize out attributes if they aren't used in the body of the shader. `if (positionAttribute.location >= 0) {` guards against enabling specific attributes if they are optimized out by the shader compiler. This had no impact on the output on ATI cards but it did fix an 0x501 error from the Nvidia cards when trying to enable an inactive attribute. The Nvidias still rendered fine regardless of the `0x501 GL_INVALID_VALUE` error however.  
> **username_0**: In the case of the shaders in this question both vertex attributes shown here `positionAttribute` and `uvAttribute` are non-negative. The code is shared in other locations and the guard against the negative attributes is for those other cases. The 0x501 I mentioned was from those other cases as well.  
> **username_0**: It's Kotlin. I didn't think the language was too important in the post in the is case since the GL calls are all the same. I'll update the question to mention that. `or` in this way is bitwise in kotlin. `||` is logical or in Kotlin.  

---

## 0 answers

