使用 glVertex2i() 绘制图形显示使用 glScaled() 缩放时的人工制品
Plotting graphs with glVertex2i() shows artefacts when scaled with glScaled()
我正在使用 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 中解决这个问题,或者我是否必须通过某种插值来增加数据点的数量,然后在不缩放的情况下进行绘图?
感谢您提供的任何帮助。
没有数据丢失。只是,您正在以不适合数据的方式使用绘图原语。 GL_LINE_STRIP 绘制一条连续的长线,在您传入时连接点,并在您设置的颜色之间进行插值。
基本上您要做的是,从最后一个蓝色容器到下一个红色容器高度画一条线 blue-to-red,然后从红色容器到绿色容器,再从那里到蓝色容器。然后你跳到下一个红色垃圾桶等等。所以本质上你是在用红色和蓝色之间的连接线绘制小 "spikes"。当然,如果要填充的宽度像素多于垃圾箱,就会有间隙。
我建议你拿一张(绘图)纸,自己动手执行绘图步骤,以了解这种结果是如何发生的。
实话实说:无论如何,这不是绘制直方图的最有效方法。一种更好的方法是将直方图数据加载到 1D 纹理中,绘制一个大四边形(或者更好的视口填充三角形,使用剪刀测试将视口剪切为矩形)并为每个片段(大致是一个像素,一些额外的东西)在片段着色器中使用X坐标从纹理中查找bin并从纹理中减去Y坐标并将结果传递给step
或smoothstep
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.
}
结果看起来像这样
如果将渲染分成 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()
我正在使用 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 中解决这个问题,或者我是否必须通过某种插值来增加数据点的数量,然后在不缩放的情况下进行绘图?
感谢您提供的任何帮助。
没有数据丢失。只是,您正在以不适合数据的方式使用绘图原语。 GL_LINE_STRIP 绘制一条连续的长线,在您传入时连接点,并在您设置的颜色之间进行插值。
基本上您要做的是,从最后一个蓝色容器到下一个红色容器高度画一条线 blue-to-red,然后从红色容器到绿色容器,再从那里到蓝色容器。然后你跳到下一个红色垃圾桶等等。所以本质上你是在用红色和蓝色之间的连接线绘制小 "spikes"。当然,如果要填充的宽度像素多于垃圾箱,就会有间隙。
我建议你拿一张(绘图)纸,自己动手执行绘图步骤,以了解这种结果是如何发生的。
实话实说:无论如何,这不是绘制直方图的最有效方法。一种更好的方法是将直方图数据加载到 1D 纹理中,绘制一个大四边形(或者更好的视口填充三角形,使用剪刀测试将视口剪切为矩形)并为每个片段(大致是一个像素,一些额外的东西)在片段着色器中使用X坐标从纹理中查找bin并从纹理中减去Y坐标并将结果传递给step
或smoothstep
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.
}
结果看起来像这样
如果将渲染分成 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()