【问题标题】:Plotting graphs with glVertex2i() shows artefacts when scaled with glScaled()使用 glVertex2i() 绘制图形会在使用 glScaled() 缩放时显示伪影
【发布时间】:2019-05-16 16:41:37
【问题描述】:

我正在使用 OpenGL 绘制 RGB 图像直方图。由于这是一个 8 位图像直方图,我的数据集包含从 0 到 255 的数据点。

如果我在不使用 glScaled() 的情况下绘制直方图,那么图形会按预期绘制,但当然不会填充分配的区域(其宽度是可变的,高度是常数)。但是,当我使用 glScaled() 时,图表会显示奇怪的伪影。

请查看以下图片以查看问题示例:

上图显示了使用 256 个数据点绘制的直方图,未使用 glScaled() 进行缩放。

上面的两张图片显示了使用 256 个数据点绘制的直方图,并使用 glScaled() 进行了缩放。奇怪的人工制品很明显(缺少数据?)。请注意,由于光照水平的变化,第三个直方图的形状略有不同。

这是我的 OpenGL 初始化代码的相关部分:

glViewport(0, 0, width, height);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();

glOrtho(0.0f, width, height, 0.0f, 0.0f, 1.0f);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();

// If this line is removed then the graph plots correctly
// m_scale_factor = width / 256
glScaled(m_scale_factor, 1.0, 1.0);

glClear(GL_COLOR_BUFFER_BIT);

这是我的情节代码的相关部分:

glEnable(GL_BLEND);
glBlendFunc(GL_ONE, GL_ONE);

glBegin(GL_LINE_STRIP);
for (int n = 0; n < m_histogram_X; n++)
{
    glColor4ub(255, 0, 0, 255);
    glVertex2i(n, m_Hist_Channel_R[n]);
    glVertex2i(n, GRAPH_HEIGHT);

    glColor4ub(0, 255, 0, 255);
    glVertex2i(n, m_Hist_Channel_G[n]);
    glVertex2i(n, GRAPH_HEIGHT);

    glColor4ub(0, 0, 255, 255);
    glVertex2i(n, m_Hist_Channel_B[n]);
    glVertex2i(n, GRAPH_HEIGHT);
}
glEnd()

...

在这个阶段我觉得我必须声明我是OpenGL的新手,所以我可能误解了很多OpenGL的东西......

我的问题是:是否可以在 OpenGL 中解决这个问题,或者我是否必须通过某种插值来增加数据点的数量,然后在不缩放的情况下进行绘图?

感谢您提供的任何帮助。

【问题讨论】:

  • 如果您是 OpenGL 新手,我建议您学习现代 OpenGL。您正在学习的是 10 年以上已弃用的 OpenGL。见learnopengl.com

标签: c++ opengl opengl-compat


【解决方案1】:

如果将渲染拆分为 3 个通道,您应该能够使用三角形条而不是线条(应该可以填补空白)。

glBegin(GL_TRIANGLE_STRIP);
glColor4ub(255, 0, 0, 255);
for (int n = 0; n < m_histogram_X; n++)
{
    glVertex2i(n, m_Hist_Channel_R[n]);
    glVertex2i(n, GRAPH_HEIGHT);
}
glEnd()

glBegin(GL_TRIANGLE_STRIP);
glColor4ub(0, 255, 0, 255);
for (int n = 0; n < m_histogram_X; n++)
{
    glVertex2i(n, m_Hist_Channel_G[n]);
    glVertex2i(n, GRAPH_HEIGHT);
}
glEnd()

glBegin(GL_TRIANGLE_STRIP);
glColor4ub(0, 0, 255, 255);
for (int n = 0; n < m_histogram_X; n++)
{
    glVertex2i(n, m_Hist_Channel_B[n]);
    glVertex2i(n, GRAPH_HEIGHT);
}
glEnd()

【讨论】:

  • 解决了 8 位图像直方图和 16 位图像直方图的问题。谢谢。
【解决方案2】:

没有数据丢失。只是,您正在以不适合数据的方式使用绘图原语。 GL_LINE_STRIP 绘制一条长长的连续线,在您传入点时将它们连接起来,并在您设置的颜色之间进行插值。

基本上你要做的是,从最后一个蓝色箱到下一个红色箱高度画一条蓝到红的线,然后从红色箱到绿色箱,然后从那里到蓝色箱。然后你跳到下一个红色垃圾箱,依此类推。所以基本上你正在用红色和蓝色之间的连接线绘制小的“尖峰”。当然,如果要填充的像素宽度多于 bin 的数量,则会出现间隙。

我建议你拿一张(绘图)纸,自己动手执行绘图步骤,以了解这个结果是如何发生的。

说实话:无论如何,这并不是绘制直方图的最有效方法。更好的方法是将直方图数据加载到一维纹理中,绘制一个大四边形(或者更好的是一个视口填充三角形,使用剪刀测试将视口切割成一个矩形)和每个片段(大约是一个像素,用一些额外的东西)在片段着色器中使用X坐标从纹理中查找bin并从纹理中减去Y坐标并将结果传递给stepsmoothstep GLSL函数确定像素的颜色。对于新手来说可能听起来很奇怪,但是绘制一个三角形并在片段着色器中完成其余的工作 更有效,因为它提交了一大片多边形。它还提供了更好的质量!

更新——示例着色器

一个实际的应用程序可以用这样的着色器来实现(Shadertoy 语义):

void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
    // Normalized pixel coordinates (from 0 to 1)
    vec2 uv = fragCoord/iResolution.xy;

    // read the histogram data from a sampler
    // on shadertoy there are no 1D textures, so we use a
    // single row of a 2D texture instead, and sweep up/down
    // over time to get a "dynamic" histogram.
    vec3 h = texture(iChannel0, vec2(uv.x, sin(0.01*iTime))).rgb;

    // discard fragments which are "outside" the histogram
    // also use this value later for the alpha channel
    float a = smoothstep( 0.000, 0.001, length(h));
    if( 0. == a ){ discard; }

    // Color the fragment. The smoothstep gives some antialiasing.
    // For perfect pixel coverage based antialiasing we'd have to
    // determine the slope of the histogram, to construct a tangent
    // and determine the distance to it, i.e. create a
    // Signed Distance Field
    fragColor = vec4(
        smoothstep(-0.001, 0.001, h.r - uv.y),
        smoothstep(-0.001, 0.001, h.g - uv.y),
        smoothstep(-0.001, 0.001, h.b - uv.y),
        a );

    // Instead of using the smoothstep and/or the SDF using a
    // multisampled buffer and performing a simple `y >= h` test
    // would yield probably a nicer result.
}

结果如下所示

【讨论】:

  • 简单地分别绘制三种颜色,以避免相邻顶点之间的颜色变化,将大大有助于解决问题。
  • @datenwolf 感谢您的有用评论。正如我所说,我对 OpenGL 很陌生,你的建议听起来对我的新手 GL 状态相对复杂。我需要花更多的时间来学习 OpenGL,不幸的是,时间才是最重要的。
  • @Ben Voigt 我尝试了你的建议,它确实让事情变得更好,但是,唉,仍然不完美。我认为,至少就目前而言,我将插入额外的数据点并在没有 glScaled() 的情况下进行绘图。一旦我有时间,我可以了解更多关于 OpenGL 的知识并实现类似于 datenwolf 描述的解决方案
  • @Amanda:我已经用一些着色器代码更新了我的答案。您仍然需要一些 OpenGL 主机代码来将着色器和直方图加载到纹理中。但这应该会给你这个想法。
  • @datenwolf 非常感谢,非常感谢。我会尝试你的想法。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-04-29
  • 1970-01-01
  • 2011-11-08
相关资源
最近更新 更多