我在 openGL 中使用 VBO 和 IBO 成功生成了高度图。该数据的全貌如下图所示。我真正想要展示的只是非黑色的数据。当颜色为黑色时,我将 z 值设为零。
这是我生成索引的代码
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));
}
}
}
}
我的着色器代码
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";
不幸的是,在我使用我的投影矩阵(正交)裁剪 z 值之后。我仍然在边缘看到一些伪像,如下图所示。
这是我的正交投影矩阵
m_projection.ortho( minX, maxX, minY, maxY, minZ, maxZ);
看起来 openGL 会自动重新创建一些三角形,因此它会在边缘(紫线)周围产生伪影。
是否有任何方法可以使这种边缘伪影消失,或者我需要重新定义索引?
预先感谢您的帮助。
最佳答案
这些三角形从何而来?
很抱歉让您失望了,但这就是裁剪的工作原理。每个图元(三角形或线)的裁剪过程不会以二进制答案结束(即抛出或保留整个图元)。相反,图元被替换为一个或多个图元,这些图元代表剪裁体积内的原始图元的一部分。所以如果你有一个三角形 ABC:
A---------B
\ |
\ |
\ |
\ |
C
A、B、C 的高度分别为 10、-1、-1,然后将其剪裁到高度 = 0,然后 OpenGL 将其替换为这个较小的三角形 Abc:
A---b B
\ |
c
C
A、b、c的高度分别为10、0、0。
您可以把它想象成通过提高裁剪平面高度来提高假想的水位,OpenGL 会正确地重建与该水位对应的海岸线。
那些小三角形是这种期望行为的结果。
参见 Vertex Post-Processing在 wiki 中,或 OpenGL 规范中的 Primitive Clipping 章节。
建议的解决方案
如果您想丢弃在高度 <= 0 的顶点相交的整个边缘,那么您可以在 VBO 中生成一个跳过这些顶点的几何体,或者如果您不能这样做,因为您的高度图是动态的,然后使用几何着色器仅传递那些完全处于高度 > 0 的图元。
如果您的硬件不支持几何着色器(并且高度图是动态的),那么您唯一的选择就是减少伪像。一种选择是使用 GL_LINE_STRIP
而不是 glPolygonMode
绘制线条(就像您现在正在做的那样)。它将避免在边缘生成那些长线,但仍会看到那些在顶点处相遇的短剪裁线段。
原始重启索引
这个和神器无关,不过以后说不定能派上用场。您当前的索引生成代码似乎以来回方式遍历网格。我不知道你为什么这样做,但请记住,你可以按顺序生成所有内容并使用 GL_PRIMITIVE_RESTART
结束一个 GL_TRIANGLE_STRIP
(或 GL_LINE_STRIP
) 并从同一VBO 中的不同顶点 开始一个新顶点。
关于c++ - 使用正射投影裁剪后的边缘伪影,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40122596/