zufryy zufryy - 1 month ago 9
C++ Question

Edge artifacts after clipping using ortho projection

I success to generate the height map using VBO and IBO in openGL. The full picture of this data is shown in the figure below. What I want really show is only the data with non-black color. I put zero in z-value when the color is black.

Here is my code to generated the indexes

void triangle::getIndices(QVector<double> minmaxX_, QVector<double> minmaxY_)
{
indices.clear();
int numY = round((minmaxY_[1] - minmaxY_[0]) / minmaxY_[2]) + 1;
int numX = round((minmaxX_[1] - minmaxX_[0]) / minmaxX_[2]) + 1;
for (int row=0; row<numY-1; row++) {
if ((row&1) == 0 ) { // even rows
for ( int col=0; col<numX; col++ ) {
indices.append(col + row*numX);
indices.append(col + (row + 1)*numX);
}
}
else {
for ( int col=numX-1; col>=0; col-- ) {
indices.append(col + (row*numX));
indices.append(col + ((row + 1)*numX));
}
}
}
}


My Shader Code

const char* const frgshdr_source =
"uniform const float colorSize;\n"
"uniform vec3 colorTable[512];"
"varying float height;\n"
"uniform float heightSize;\n"
"uniform float heightMin;\n"
"void main() {\n"
" //vec3 colorTable2[int(colorSize)+1] = colorTable;\n"
" float h = (height - heightMin)/heightSize;\n"
" if (h < 0.0f || h > 1.0f) {\n"
" gl_FragColor = vec4(0.0f, 0.0f, 0.0f, 0.0f);}\n"
" else {"
" int i = 0;\n"
" while (h >= float(i + 1)/colorSize) {\n"
" i += 1;}\n"
" vec3 base = mix(colorTable[i], colorTable[i+1], colorSize*(h - (float(i)/colorSize)));\n"
" gl_FragColor = vec4(base, 1.0f);}\n"
"}\n";


Full Data

Unfortunately after I clip the z-value using my projection matrix (orthographic). I still get some artifacts on the edge like in the picture below.

here is my orthographic projection matrix

m_projection.ortho( minX, maxX, minY, maxY, minZ, maxZ);


After Clipping

Look's like openGL recreate some triangles automatically so it makes artifacs around the edge (purple line).

zoom
enter image description here

Is there any method to make this edge artifacts disappear or I need to define my indexes again ?

Thank you in advance for your help.

Answer

Where those triangles come from?

Sorry to disappoint you, but this is how clipping is supposed to work. The clipping process of each primitive (triangle or line) does not end in a binary answer (i.e. throwing or retaining the entire primitive). Instead the primitive is replaced with one or more primitives which represent the part of the original primitive within the clipping volume. So if you have a triangle ABC:

A---------B
  \       |
    \     |
      \   |
        \ |
          C

With heights 10, -1, -1 of A, B, C respectively, and you clip it against height = 0, then OpenGL replaces it with this smaller triangle Abc:

A---b     B
  \ |
    c


          C

With heights 10, 0, 0 of A, b, c respectively.

You can think of it as if by raising the clipping plane height you raise an imaginary water-level, and OpenGL correctly reconstructs the shore-line corresponding to that water-level.

raising water

Those small triangles are the result of this desired behavior.

See Vertex Post-Processing in the wiki, or the Primitive Clipping chapter in the OpenGL spec.

Proposed solution(s)

If you want to throw away entire edges that intersect at a vertex of height <= 0, then you can either generate a geometry in the VBO that skips those vertices, or if you cannot do that because your height-map is dynamic, then use a geometry shader to pass-through only those primitives which are entirely at height > 0.

If your hardware does not support geometry shaders (and heightmap is dynamic), then your only choice is to reduce the artifact. One option is to draw the lines with GL_LINE_STRIP instead of glPolygonMode (as it appears you're doing now). It will avoid generating those long lines at the edges, but those short clipped segments meeting at the vertices will still be seen.

Primitive restart index

This is unrelated to the artifact, but it may come in handy in the future. Your current index generating code seems to traverse the grid in a back-and-forth fashion. I don't know why you do this, but keep in mind that you can generate everything in sequence and use GL_PRIMITIVE_RESTART to end one GL_TRIANGLE_STRIP (or GL_LINE_STRIP) and start a new one from a different vertex within the same VBO.