在 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。为了完成这项工作,您只需将数据编码为范围为&lt;0.0,1.0&gt; 的浮点数的一维线性数组,这可以从&lt;-1,+1&gt; 在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 端将范围转换为 &lt;0,1&gt;,您可以将其移至着色器(非常简单的顶点和片段着色器),但我觉得您是新手,着色器也很简单很多东西让你开始。如果你真的想走那条路,请看:

complete GL+GLSL+VAO/VBO C++ example

它还涵盖了没有 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

    这也是扩展(现在是硬件原生),所以你的纹理坐标从 01 而不是从 0+pixel/21-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

您的标题建议 GLFW3GLUT 完全不同(除非它源自 GLUT),所以也许您应该编辑标签和 OP 以匹配您真正拥有的/使用。

现在是着色器:

如果您在&lt;-1,+1&gt; 范围内生成数据:

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 中绘制单色二维数组的主要内容,如果未能解决你的问题,请参考以下文章

OpenGL - 将像素绘制到屏幕上?

OpenGL:绘制二维图像的最快方法

用C++在Win32中用LoadImage()绘制HBITMAP的二维数组

无法绘制二维数组,pygame

绘制具有单独分量数组的二维向量

使用带有 Swing 的鼠标绘制(单色)数组的最简单方法是啥?