在 OpenGL 中绘制单色二维数组
Posted
技术标签:
【中文标题】在 OpenGL 中绘制单色二维数组【英文标题】:Drawing a monochrome 2D array in OpenGL 【发布时间】:2020-02-07 15:02:53 【问题描述】:我需要将 OpenGL 用于非常特定的目的。我有一个大小为 [SIZE][SIZE] 的一维浮点数组(它总是正方形),它代表一个二维图像。绘图只是这里的额外内容,因为我一直使用第三个程序通过将数组输出到文本文件来进行绘图,但我想提供在程序本身中进行绘图的选项。
这个数组在一个循环中不断更新,因为它应该代表一个模拟字段的值,其细节是完全无关的,但重要的一点是它们每个的值将是一个浮点数-1 和 1。现在,我想将这个数组绘制为 2D 图像(实时),主循环的每 N 步。我尝试使用 X11 的像素绘图工具(我在 Linux 上这样做),并通过循环它并在 SIZE X SIZE 窗口上逐个像素地绘制数组,但这非常慢并且需要更多时间比模拟本身。我一直在研究 OpenGL,从我读过的内容来看,理想的解决方案是将我的数组重新解释为 2D 纹理,然后将其打印在四边形上。显然,要使用裸 OpenGL,我必须重新调整我的代码才能使用 OpenGL 绘图的主循环,这有点不切实际,所以如果在 GLFW 中也能做到这一点,我很满意。
要绘制的图像始终是正方形的,其方向完全无关紧要,无论是镜像绘制、倒置绘制、转置绘制等,都应该是完全各向同性的。
程序的主干遵循下一个方案
#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;
我尝试了一些代码 sn-ps 并修改了我发现的其他一些示例,但无法使其工作,要么他们必须调用 glLoop 来打破我自己的模拟循环,要么它只是在中心打印一个像素。
所以我的主要问题是如何从field
的第一个 SIZE X SIZE 组件中制作纹理,然后在 QUAD 上绘制它。
谢谢!
【问题讨论】:
您至少有三个子问题:1. 将模拟状态转换为 OpenGL 纹理,2. 设置矩阵堆栈以有效地查看使用该纹理绘制的四边形,3. 使模拟适应在 GLUT 的事件循环模型的范围内工作(或切换到不同的应用程序框架,如 GLFW);选择一个子问题并编辑问题以专注于此。 @genpfault 感谢您的评论,只是尝试这样做。由于在这个库中为四边形绘制纹理似乎是一件相当简单的事情,我很高兴有一种方法可以在我自己的循环中创建纹理。谢谢! @Spektre 感谢您的评论,我没有示例数据,但以float field[3][3] = a,b,c,d,e,f,g,h,j;
形式的内容为例,带有 a、b、c、d、e、f、g、h ,j 是介于 -1 和 1 之间的随机数。实际上它们更大,但这是我想要打印的那种数组
@Spektre 我可以轻松地将其转换为长度为 SIZE^2 的一维。大小在200到1000之间
【参考方案1】:
对于新手来说最简单的方法是使用没有着色器的旧 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>
,您可以将其移至着色器(非常简单的顶点和片段着色器),但我觉得您是新手,着色器也很简单很多东西让你开始。如果你真的想走那条路,请看:
它还涵盖了没有 GLUT 但在 Windows 上的 GL 初始化...
现在对上面的程序做一些注释:
我使用GL_LUMINANCE32F_ARB
纹理格式扩展
它的 32 位浮点纹理格式没有被限制,因此您的数据保持原样。它现在应该出现在所有 gfx 硬件上。我这样做是为了简化向着色器的过渡,后者可以直接在原始数据上操作...
size
在原始 GL 规范中,纹理大小应该是 2 的幂,所以 16,32,64,128,256,512,... .但是在 linux 和 MAC 上,GL 的实现可能会出现问题,所以如果某些东西不起作用,请尝试使用 2 大小的幂(以防万一)...
也不要太疯狂,因为 gfx 卡有限制,通常 2048 是低端东西的安全限制。如果你需要更多,那就做更多 QUADS/纹理的马赛克
GL_CLAMP_TO_EDGE
这也是扩展(现在是硬件原生),所以你的纹理坐标从 0
到 1
而不是从 0+pixel/2
到 1-pixel/2
...
但是,所有这些都不是 GL 1.0 的东西,因此您需要向您的应用程序添加扩展(如果 GLUT 或您使用的任何东西还没有)。所有这些都只是标记/常量,没有函数调用,所以万一编译器抱怨它应该足够了:
#include <gl\glext.h>
在包含gl.h
之后或直接添加定义:
#define GL_CLAMP_TO_EDGE 0x812F
#define GL_LUMINANCE32F_ARB 0x8818
顺便说一句。您的代码看起来不像 GLUT 应用程序(但我可能错了,因为我不使用它)例如:
simple GLUT app example您的标题建议 GLFW3 与 GLUT 完全不同(除非它源自 GLUT),所以也许您应该编辑标签和 OP 以匹配您真正拥有的/使用。
现在是着色器:
如果您在<-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 中绘制单色二维数组的主要内容,如果未能解决你的问题,请参考以下文章