在 OpenGL 中绘制单色二维数组
Drawing a monochrome 2D array in OpenGL
我需要将 OpenGL 用于非常特定的目的。我有一个一维浮点数数组,大小为 [SIZE][SIZE](它总是正方形),代表二维图像。绘图在这里只是额外的,因为我一直使用第三方程序通过将数组输出到文本文件来进行绘图,但我想提供在程序本身中进行绘图的选项。
这个数组在循环中不断更新,因为它应该表示模拟字段的值,其细节无关紧要,但重要的是它们中的每一个的值都将是一个在 -1 和 1 之间浮动。现在,我只想将这个数组绘制为二维图像(实时),主循环的每 N 步。我尝试使用 X11 的像素绘图工具(我在 Linux 上这样做),并通过循环遍历并在 SIZE X SIZE window 上逐个像素地绘制数组,但是这非常慢,并且比模拟本身花费的时间要多得多。我一直在研究 OpenGL,从我读到的内容来看,理想的解决方案是将我的数组重新解释为 2D 纹理,然后将其打印在四边形上。显然要使用裸 OpenGL,我将不得不重新调整我的代码以与 OpenGL 绘图的主循环一起工作,这有点不切实际,所以如果可以在 GLFW 中完成同样的操作,我很高兴。
要绘制的图像始终是方形的,它的方向完全无关紧要,无论是镜像绘制、倒置绘制、转置绘制等都没有关系,因为它应该是完全各向同性的。
程序的主要backbone遵循下一个方案
#include <iostream>
#include <GLFW/glfw3.h>
using namespace std;
int main(int argc, char** argv)
{
if (GFX) //GFX is a bool, only draw stuff if it's 1 (its value doesnt change)
{
//Initialize GLFW
}
float field[2*SIZE][SIZE] = {0}; //This is the array to print (only the first SIZE * SIZE components)
for (int i = 0; i < totalTime; i++)
{
for (int x=0; x < SIZE; x++)
{
for (int y=0; y < SIZE; y++)
{
//Each position of the array is then updates here
}
}
if (GFX)
{
//The drawing should be done here
}
}
return 0;
}
我已经尝试了一些代码片段并修改了我发现的一些其他示例,但无法使其正常工作,要么他们必须调用 glLoop 来破坏我自己的模拟循环,要么它只是在中心打印一个像素。
所以我的主要问题是如何用 field
的前 SIZE X SIZE 分量制作纹理,然后在 QUAD 上绘制它。
谢谢!
对于新手来说最简单的就是使用没有着色器的旧API。为了完成这项工作,您只需将数据编码为 <0.0,1.0>
范围内的浮点数的一维线性数组,这可以从 <-1,+1>
非常快地在 CPU 侧使用单个 for 循环完成,如下所示:
for (i=0;i<size*size;i++) data[i]=0.5*(data[i]+1.0);
我既不使用 GLUT 也不为你的平台编写代码,所以我坚持只渲染:
//---------------------------------------------------------------------------
const int size=512; // data resolution
const int size2=size*size;
float data[size2]; // your float size*size data
GLuint txrid=-1; // GL texture ID
//---------------------------------------------------------------------------
void init() // this must be called once (after GL is initialized)
{
int i;
// generate float data
Randomize();
for (i=0;i<size2;i++) data[i]=Random();
// create texture
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D,txrid);
glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S,GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T,GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,GL_NEAREST);
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE,GL_MODULATE);
glDisable(GL_TEXTURE_2D);
}
//---------------------------------------------------------------------------
void exit() // this must be called once (before GL is unitialized)
{
// release texture
glDeleteTextures(1,&txrid);
}
//---------------------------------------------------------------------------
void gl_draw()
{
glClear(GL_COLOR_BUFFER_BIT);
glDisable(GL_DEPTH_TEST);
glDisable(GL_CULL_FACE);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
// bind texture
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D,txrid);
// copy your actual data into it
glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE32F_ARB, size, size, 0, GL_LUMINANCE, GL_FLOAT, data);
// render single textured QUAD
glColor3f(1.0,1.0,1.0);
glBegin(GL_QUADS);
glTexCoord2f(0.0,0.0); glVertex2f(-1.0,-1.0);
glTexCoord2f(1.0,0.0); glVertex2f(+1.0,-1.0);
glTexCoord2f(1.0,1.0); glVertex2f(+1.0,+1.0);
glTexCoord2f(0.0,1.0); glVertex2f(-1.0,+1.0);
glEnd();
// unbind texture (so it does not mess with othre rendering)
glBindTexture(GL_TEXTURE_2D,0);
glDisable(GL_TEXTURE_2D);
glFlush();
SwapBuffers(hdc); // ignore this GLUT should make it on its own
}
//---------------------------------------------------------------------------
此处预览:
为了完成这项工作,您需要在 GLUT 创建 GL 上下文后在应用程序启动时调用 init()
,并在 GLUT 关闭 GL 上下文之前在应用程序结束时调用 exit()
。 gl_draw()
将呈现您的数据,因此必须在 GLUT 的绘图事件中调用它。
如果您不想在 CPU 侧将范围转换为 <0,1>
,您可以将其移动到着色器(非常简单的顶点和片段着色器),但我感觉你' re rookie 和 shaders 对你来说太过分了。如果你真的想走那条路,请看:
它还涵盖了没有 GLUT 但 Windows ...
的 GL 初始化
现在对上面的程序做一些说明:
我使用了GL_LUMINANCE32F_ARB
纹理格式扩展
它的 32 位浮点纹理格式未固定,因此您的数据保持原样。它应该出现在所有现在的 gfx HW 上。我这样做是为了简化向着色器的过渡,后者可以直接在原始数据上进行操作...
size
在原始 GL 规范中,纹理大小应该是 2 的幂,所以 16,32,64,128,256,512,... 如果不是,您需要使用矩形纹理扩展,但它在 gfx HW 中原生存在多年,所以不需要改变任何东西。但是在 linux 和 MAC 上,GL 实现可能会出现问题,所以如果某些东西不起作用,请尝试使用 2 大小的幂(以防万一)...
也不要对尺寸过于狂热,因为 gfx 卡有限制,通常 2048 是低端东西的安全限制。如果你需要更多,那就做更多的马赛克 QUADS/textures
GL_CLAMP_TO_EDGE
这也是扩展(现在是 HW 原生的)所以你的纹理坐标从 0
到 1
而不是从 0+pixel/2
到 1-pixel/2
...
然而,所有这些都不是 GL 1.0 的东西,因此您需要向您的应用程序添加扩展(如果 GLUT 或您使用的任何东西还没有)。所有这些都只是 tokens/constants 没有函数调用所以万一编译器抱怨它应该足够了:
#include <gl\glext.h>
包含 gl.h
后或直接添加定义:
#define GL_CLAMP_TO_EDGE 0x812F
#define GL_LUMINANCE32F_ARB 0x8818
顺便说一句。你的代码看起来不像 GLUT 应用程序(但我可能错了,因为我不使用它)例如看这个:
你的 header 建议 GLFW3 与 GLUT 完全不同(除非它源自 GLUT)所以也许你应该编辑标签和 OP 以匹配您的真实情况 have/use.
现在着色器:
如果您在 <-1,+1>
范围内生成数据:
for (i=0;i<size2;i++) data[i]=(2.0*Random())-1.0;
并使用这些着色器:
顶点:
// Vertex
#version 400 core
layout(location = 0) in vec2 pos; // position
layout(location = 8) in vec2 tex; // texture
out vec2 vpos;
out vec2 vtex;
void main()
{
vpos=pos;
vtex=tex;
gl_Position=vec4(pos,0.0,1.0);
}
片段:
// Fragment
#version 400 core
uniform sampler2D txr;
in vec2 vpos; // position
in vec2 vtex; // texture
out vec4 col;
void main()
{
vec4 c;
c=texture(txr,vtex);
c=(c+1.0)*0.5;
col=c;
}
那么结果是一样的(GPU端转换速度更快)。但是,您需要将 GL_QUADS
转换为 VAO/VBO(unless nVidia card is used 但即便如此,您肯定应该使用 VBO/VAO)。
我需要将 OpenGL 用于非常特定的目的。我有一个一维浮点数数组,大小为 [SIZE][SIZE](它总是正方形),代表二维图像。绘图在这里只是额外的,因为我一直使用第三方程序通过将数组输出到文本文件来进行绘图,但我想提供在程序本身中进行绘图的选项。
这个数组在循环中不断更新,因为它应该表示模拟字段的值,其细节无关紧要,但重要的是它们中的每一个的值都将是一个在 -1 和 1 之间浮动。现在,我只想将这个数组绘制为二维图像(实时),主循环的每 N 步。我尝试使用 X11 的像素绘图工具(我在 Linux 上这样做),并通过循环遍历并在 SIZE X SIZE window 上逐个像素地绘制数组,但是这非常慢,并且比模拟本身花费的时间要多得多。我一直在研究 OpenGL,从我读到的内容来看,理想的解决方案是将我的数组重新解释为 2D 纹理,然后将其打印在四边形上。显然要使用裸 OpenGL,我将不得不重新调整我的代码以与 OpenGL 绘图的主循环一起工作,这有点不切实际,所以如果可以在 GLFW 中完成同样的操作,我很高兴。
要绘制的图像始终是方形的,它的方向完全无关紧要,无论是镜像绘制、倒置绘制、转置绘制等都没有关系,因为它应该是完全各向同性的。
程序的主要backbone遵循下一个方案
#include <iostream>
#include <GLFW/glfw3.h>
using namespace std;
int main(int argc, char** argv)
{
if (GFX) //GFX is a bool, only draw stuff if it's 1 (its value doesnt change)
{
//Initialize GLFW
}
float field[2*SIZE][SIZE] = {0}; //This is the array to print (only the first SIZE * SIZE components)
for (int i = 0; i < totalTime; i++)
{
for (int x=0; x < SIZE; x++)
{
for (int y=0; y < SIZE; y++)
{
//Each position of the array is then updates here
}
}
if (GFX)
{
//The drawing should be done here
}
}
return 0;
}
我已经尝试了一些代码片段并修改了我发现的一些其他示例,但无法使其正常工作,要么他们必须调用 glLoop 来破坏我自己的模拟循环,要么它只是在中心打印一个像素。
所以我的主要问题是如何用 field
的前 SIZE X SIZE 分量制作纹理,然后在 QUAD 上绘制它。
谢谢!
对于新手来说最简单的就是使用没有着色器的旧API。为了完成这项工作,您只需将数据编码为 <0.0,1.0>
范围内的浮点数的一维线性数组,这可以从 <-1,+1>
非常快地在 CPU 侧使用单个 for 循环完成,如下所示:
for (i=0;i<size*size;i++) data[i]=0.5*(data[i]+1.0);
我既不使用 GLUT 也不为你的平台编写代码,所以我坚持只渲染:
//---------------------------------------------------------------------------
const int size=512; // data resolution
const int size2=size*size;
float data[size2]; // your float size*size data
GLuint txrid=-1; // GL texture ID
//---------------------------------------------------------------------------
void init() // this must be called once (after GL is initialized)
{
int i;
// generate float data
Randomize();
for (i=0;i<size2;i++) data[i]=Random();
// create texture
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D,txrid);
glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S,GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T,GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,GL_NEAREST);
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE,GL_MODULATE);
glDisable(GL_TEXTURE_2D);
}
//---------------------------------------------------------------------------
void exit() // this must be called once (before GL is unitialized)
{
// release texture
glDeleteTextures(1,&txrid);
}
//---------------------------------------------------------------------------
void gl_draw()
{
glClear(GL_COLOR_BUFFER_BIT);
glDisable(GL_DEPTH_TEST);
glDisable(GL_CULL_FACE);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
// bind texture
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D,txrid);
// copy your actual data into it
glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE32F_ARB, size, size, 0, GL_LUMINANCE, GL_FLOAT, data);
// render single textured QUAD
glColor3f(1.0,1.0,1.0);
glBegin(GL_QUADS);
glTexCoord2f(0.0,0.0); glVertex2f(-1.0,-1.0);
glTexCoord2f(1.0,0.0); glVertex2f(+1.0,-1.0);
glTexCoord2f(1.0,1.0); glVertex2f(+1.0,+1.0);
glTexCoord2f(0.0,1.0); glVertex2f(-1.0,+1.0);
glEnd();
// unbind texture (so it does not mess with othre rendering)
glBindTexture(GL_TEXTURE_2D,0);
glDisable(GL_TEXTURE_2D);
glFlush();
SwapBuffers(hdc); // ignore this GLUT should make it on its own
}
//---------------------------------------------------------------------------
此处预览:
为了完成这项工作,您需要在 GLUT 创建 GL 上下文后在应用程序启动时调用 init()
,并在 GLUT 关闭 GL 上下文之前在应用程序结束时调用 exit()
。 gl_draw()
将呈现您的数据,因此必须在 GLUT 的绘图事件中调用它。
如果您不想在 CPU 侧将范围转换为 <0,1>
,您可以将其移动到着色器(非常简单的顶点和片段着色器),但我感觉你' re rookie 和 shaders 对你来说太过分了。如果你真的想走那条路,请看:
它还涵盖了没有 GLUT 但 Windows ...
的 GL 初始化现在对上面的程序做一些说明:
我使用了
GL_LUMINANCE32F_ARB
纹理格式扩展它的 32 位浮点纹理格式未固定,因此您的数据保持原样。它应该出现在所有现在的 gfx HW 上。我这样做是为了简化向着色器的过渡,后者可以直接在原始数据上进行操作...
size
在原始 GL 规范中,纹理大小应该是 2 的幂,所以 16,32,64,128,256,512,... 如果不是,您需要使用矩形纹理扩展,但它在 gfx HW 中原生存在多年,所以不需要改变任何东西。但是在 linux 和 MAC 上,GL 实现可能会出现问题,所以如果某些东西不起作用,请尝试使用 2 大小的幂(以防万一)...
也不要对尺寸过于狂热,因为 gfx 卡有限制,通常 2048 是低端东西的安全限制。如果你需要更多,那就做更多的马赛克 QUADS/textures
GL_CLAMP_TO_EDGE
这也是扩展(现在是 HW 原生的)所以你的纹理坐标从
0
到1
而不是从0+pixel/2
到1-pixel/2
...
然而,所有这些都不是 GL 1.0 的东西,因此您需要向您的应用程序添加扩展(如果 GLUT 或您使用的任何东西还没有)。所有这些都只是 tokens/constants 没有函数调用所以万一编译器抱怨它应该足够了:
#include <gl\glext.h>
包含 gl.h
后或直接添加定义:
#define GL_CLAMP_TO_EDGE 0x812F
#define GL_LUMINANCE32F_ARB 0x8818
顺便说一句。你的代码看起来不像 GLUT 应用程序(但我可能错了,因为我不使用它)例如看这个:
你的 header 建议 GLFW3 与 GLUT 完全不同(除非它源自 GLUT)所以也许你应该编辑标签和 OP 以匹配您的真实情况 have/use.
现在着色器:
如果您在 <-1,+1>
范围内生成数据:
for (i=0;i<size2;i++) data[i]=(2.0*Random())-1.0;
并使用这些着色器:
顶点:
// Vertex
#version 400 core
layout(location = 0) in vec2 pos; // position
layout(location = 8) in vec2 tex; // texture
out vec2 vpos;
out vec2 vtex;
void main()
{
vpos=pos;
vtex=tex;
gl_Position=vec4(pos,0.0,1.0);
}
片段:
// Fragment
#version 400 core
uniform sampler2D txr;
in vec2 vpos; // position
in vec2 vtex; // texture
out vec4 col;
void main()
{
vec4 c;
c=texture(txr,vtex);
c=(c+1.0)*0.5;
col=c;
}
那么结果是一样的(GPU端转换速度更快)。但是,您需要将 GL_QUADS
转换为 VAO/VBO(unless nVidia card is used 但即便如此,您肯定应该使用 VBO/VAO)。