OpenGL:对象边缘的颜色值

时间:2016-09-02 06:54:31

标签: c++ opengl

我实现了一个应用程序,它只是从摄像机视图渲染多边形网格的深度。

在顶点着色器中,gl_Position是使用如下的简单代码设置的。

gl_Position =  MVP * vec4(vertexPosition_modelspace,1);

在片段着色器中,返回的颜色值定义如下。

color = vec3(1-gl_FragCoord.z,1-gl_FragCoord.z,1-gl_FragCoord.z);

渲染单帧后,像素值被读取为浮点并保存为* .pgm图像文件。

float* pgmBuffer = new float[width*height * 3];
glReadPixels((GLint)0, (GLint)0,
                (GLint)width, (GLint)height,
                GL_RGB, GL_FLOAT, pgmBuffer);
WritePGM(width, height, pgmBuffer, imgname);

其中WritePGM的实现方式如下。 target和source index equation用于将glReadPixel结果的转换缓冲区顺序转换为PGM文件格式。 像素值[0,1]也被转换为[0,255]。

void OpenGL_Render::WritePGM(int width, int height, float* buffer, char* outPGM)
{
    int* gbuffer = new int[width*height];
    unsigned char *cbuffer = new unsigned char[width*height*sizeof(unsigned char)];

    int line = 1;
    for (int i = 0; i < width; i++)
    {
        for (int j = 0; j < height; j++)
        {
            int target = i*width + j;
            int source = width*height * 3 - (((i+1)*width - (j+1)) * 3);

            cbuffer[target] = buffer[source]*255;
        }

    }

    ofstream fout(outPGM);
    fout << "P5\n" << width << " " << height << "\n255\n";
    fout.write((char *)cbuffer, width*height * sizeof(unsigned char));
    fout.close();
}

这是问题所在。当我打开结果PGM图像和观察到的像素值时,我发现在多边形网格的边缘,在邻居的最小值和最大值之间存在一些像素值,如下面的屏幕截图所示。在这里,我将边缘视为多边形网格的轮廓或轮廓。

Rendering result of a box

Pixel values observed at the edge

我的问题是,

  1. 我猜这些中间值是从GPU的插值器生成的。这样对吗?仅供参考,我没有使用任何其他片段着色器,例如anit-aliasing。

  2. 如果是,如何删除或禁用插值?

  3. 如果错了,那些中间值的原因是什么?

1 个答案:

答案 0 :(得分:0)

  

我猜那些中间值是从插值器生成的   GPU。是不是?

没有。图形管道中的插值器与您在边缘上看到的内容无关。让我解释一下通常使用的插值。您的顶点/ tess /几何着色器最终会生成顶点数据,即它们为每个顶点设置位置,法线和其他值。例如,每个三角形(每个顶点)仅调用一次顶点着色器3次。但片段着色器完全是另一回事。它被调用为基元的每个片段(为简单起见,您可以假设片段==像素)。然后,如果我想获得当前片段(三角形上的点)的三角形的法线向量,我应该使用哪一个?三角形可能有三个不同的法向量!答案是:他们都不是。当您在顶点(以及细分或几何)着色器中编写out变量时,请参阅片段着色器的相应in变量获取从当前基元的所有每顶点值插值的值。这样可以产生以下效果:

Phong shader with and without interpolation

注意右侧球体的边缘。它没有比左边多边形多边形,但它很光滑!这种效果是可能的,因为每个顶点中的法线(四个四边形相互接触的点)是球体的 true 法线。它实际上并不垂直于任何周围的四边形,但它实际上是顶点的半径矢量,因为它应该在数学上是正确的球体。但是在左边的图片上,插值被关闭,每个四边形每个四边形只有一个法线(以某种方式选择)被照亮,所以同一个四边形的所有像素都具有相同的法线,而在右边的图片中,每个像素都得到它自己的法线从四边形角插入的矢量。

将上述内容应用于屏幕截图,我们发现插值只会在多维数据集的上产生平滑渐变,但不会产生边缘。没有它,你会得到这样的东西:

A cube without vertex color interpolation

作为一个更原始的例子,想象一下你想绘制一条线(例如,使用GL_LINES),它可以平滑地将它的颜色从黑色变为白色。但是你只能设置线上两点的颜色值:因为它的开始和结束。因此,如果您没有插值,您将获得完全黑色或完全白线,具体取决于其他一些设置。例如,11像素长行将如下所示:

|0.0|0.0|0.0|0.0|0.0|0.0|0.0|0.0|0.0|0.0|0.0|

但启用插值后,像素颜色会平滑变化:

|0.0|0.1|0.2|0.3|0.4|0.5|0.6|0.7|0.8|0.9|1.0|

默认情况下启用顶点到片段着色器变量的插值,但您可以使用flat interpolation qualifier禁用它。

现在是第二个问题。

  

如果错了,那些中间值的原因是什么?

原因是您启用了抗锯齿功能。在不使用某些高级着色技术之前,您不需要任何额外的工作或特殊着色器来获得抗锯齿渲染。以下是启用抗锯齿的位置列表:

  • 您在代码中的某处调用了glEnable(GL_MULTISAMPLE);
  • 您的窗口处理库已经为您完成了。
  • 在视频驱动程序选项的某处强制使用抗锯齿功能。据我所知,Windows的nVidia驱动程序不久前有这样的功能。