使用网格数据和一维纹理图在 opengl 中创建等高线图

Posted

技术标签:

【中文标题】使用网格数据和一维纹理图在 opengl 中创建等高线图【英文标题】:Create contour plot in opengl using grid data and a 1D texture map 【发布时间】:2012-01-11 18:50:09 【问题描述】:

我在规则间隔的网格上有一组 X、Y、Z 值,我需要使用 C++ 创建一个填充颜色的等高线图。我已经在谷歌上搜索了好几天,共识似乎是使用openGL中的一维纹理贴图可以实现这一点。但是,我还没有找到一个如何实际执行此操作的示例,而且仅阅读 openGL 文档就没有任何进展。我的困惑归结为一个核心问题:

我的数据不包含每个像素的 X、Y 值 - 它是一个规则间隔的网格,在 X 和 Y 轴上每 4 个单位包含一个数据,具有一个正整数 Z 值。

例如:(0, 0, 1), (4, 0, 1), (8, 0, 2), (0, 4, 2), (0, 8, 4), (4, 4 , 3) 等。

由于轮廓将基于 Z 值并且数据点之间存在间隙,因此应用 1D 纹理如何实现此数据的轮廓(即应用 1D 纹理如何在网格点之间进行插值?)强>

我找到的最接近的例子是在茶壶示例中的红皮书 (http://fly.cc.fer.hr/~unreal/theredbook/chapter09.html) 的在线版本中,但是我假设茶壶模型的每个像素都有数据,因此不需要在数据点之间进行插值。

如果有人能阐明我的问题,或者更好地指出一个以这种方式使用 1D 纹理贴图的具体示例,我将永远感激不尽,因为我在这个项目上花了 2 天时间,几乎没有什么可展示的它。

编辑:

以下代码是我正在使用的代码,虽然它确实在正确的位置显示了点,但没有发生插值或轮廓 - 这些点只是显示为点。

//Create a 1D image - for this example it's just a red line
int stripeImageWidth = 32;
GLubyte   stripeImage[3*stripeImageWidth];
for (int j = 0; j < stripeImageWidth; j++) 
    stripeImage[3*j] = j < 2 ? 0 : 255;
    stripeImage[3*j+1] = 255;
    stripeImage[3*j+2] = 255;

glDisable(GL_TEXTURE_2D);
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
glTexImage1D(GL_TEXTURE_1D, 0, 3, stripeImageWidth, 0, GL_RGB, GL_UNSIGNED_BYTE, stripeImage);
glTexParameterf(GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, GL_CLAMP);
glTexParameterf(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL);

glTexGeni( GL_S, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR );
float s[4] =  0,1,0,0 ;
glTexGenfv( GL_S, GL_OBJECT_PLANE, s );
glEnable( GL_TEXTURE_GEN_S );
glEnable( GL_TEXTURE_1D );
glBegin(GL_POINTS);

//_coords contains X,Y,Z data - Z is the value that I'm trying to contour
for (int x = 0; x < _coords.size(); ++x)

    glTexCoord1f(static_cast<ValueCoord*>(_coords[x])->GetValue());
    glVertex3f(_coords[x]->GetX(), _coords[x]->GetY(), zIndex);

glEnd();

【问题讨论】:

【参考方案1】:

这个想法是使用 Z 坐标作为 S 坐标进入纹理。纹理坐标上的线性插值然后创建轮廓。请注意,通过使用着色器,您可以将 XY->Z 数据放入 2D 纹理中,并使用着色器在 1D 纹理的色带中对 2D 采样器的值进行间接处理。

更新:代码示例

首先我们需要稍微改变一下使用纹理的方式。

为此准备纹理:

//Create a 1D image - for this example it's just a red line
int stripeImageWidth = 32;
GLubyte   stripeImage[3*stripeImageWidth];
for (int j = 0; j < stripeImageWidth; j++) 
    stripeImage[3*j] = j*255/32; // use a gradient instead of a line
    stripeImage[3*j+1] = 255;
    stripeImage[3*j+2] = 255;


GLuint texID;
glGenTextures(1, &texID);
glBindTexture(GL_TEXTURE_1D, texID);
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
glTexImage1D(GL_TEXTURE_1D, 0, 3, stripeImageWidth, 0, GL_RGB, GL_UNSIGNED_BYTE, stripeImage);

// We want the texture to wrap, so that values outside the range [0, 1] 
// are mapped into a gradient sawtooth
glTexParameterf(GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameterf(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameterf(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL);

还有 this 绑定它以供使用。

// The texture coordinate comes from the data, it it not
// generated from the vertex position!!!
glDisable( GL_TEXTURE_GEN_S ); 
glDisable(GL_TEXTURE_2D);
glEnable( GL_TEXTURE_1D );
glBindTexture(GL_TEXTURE_1D, texID);

现在你的概念问题:你不能直接从 XYZ 数据制作等高线图。 XYZ 只是稀疏的采样点。您需要填补空白,例如首先将其放入 2D 直方图中。为此创建一个网格,每个方向都有一定数量的 bin,初始化为所有 NaN(伪代码)

float hist2D[bins_x][bins_y] = NaN, NaN, ...

然后对于每个 XYZ,如果不是 NaN,则将 Z 值添加到网格的 bin 中,否则将 NaN 替换为 Z 值。然后在直方图上使用拉普拉斯滤波器来平滑仍然包含 NaN 的 bin。最后,您可以使用

将网格渲染为等高线图
glBegin(GL_QUADS);
for(int y=0; y<grid_height; y+=2) for(int x=0; x<grid_width; x+=2) 
    glTexCoord1f(hist2D[x  ][y  ]]); glVertex2i(x  ,y);
    glTexCoord1f(hist2D[x+1][y  ]]); glVertex2i(x+1,y);
    glTexCoord1f(hist2D[x+1][y+1]]); glVertex2i(x+1,y+1);
    glTexCoord1f(hist2D[x  ][y+1]]); glVertex2i(x  ,y+1);

glEnd();

或者您可以将网格上传为 2D 纹理并使用片段着色器间接进入色带。

另一种填补稀疏 XYZ 数据空白的方法是找到 XY 集的 2D Voronoi 图并使用它来创建采样几何。顶点的 Z 值将是导致 Voronoi 单元相交的 XYZ 的距离加权平均值。

【讨论】:

精彩的详细答案!谢谢

以上是关于使用网格数据和一维纹理图在 opengl 中创建等高线图的主要内容,如果未能解决你的问题,请参考以下文章

OpenGL-载入纹理

用opengl实现网格[重复]

如何在 OpenGL 中将多个纹理分配到单个网格中?

在 Qt 中创建原始 GL 上下文?

通过opengl中的纹理传递数组数据

如何在 1D 缓冲区中生成 2D 纹理并将其加载到 OpenGL 中?