【问题标题】:Best way to render a bitmap/bitarray to 2d plane (with OpenGL)将位图/位数组渲染到 2d 平面的最佳方法(使用 OpenGL)
【发布时间】:2011-03-05 20:13:22
【问题描述】:

好的,这就是我所拥有的。我有一个 1d 位图(或位数组、位集、位串,但我现在称它为位图)包含来自康威生命生成游戏的活状态或死状态。 (x, y) 处的单元格由y * map_width + x 处的位表示。

现在我的生活游戏“引擎”开始工作了,如果我现在可以渲染一些图形内容就好了。我认为 OpenGL 将是一个不错的选择,但我不知道我应该如何开始以及是否有任何特定的函数或着色器(我对着色器一无所知)可以有效地将位图渲染到具有黑色和白色像素的 2d 平面。

如果你现在认为“不,你这个白痴 opengl 不好用......”,尽管说出来,我愿意改变。

编辑

我忘了说我使用了一个紧凑的位数组,每个字节存储 8 位,并使用掩码来检索这些字节。这是我手工制作的图书馆东西:

#include <stdint.h> // uint32_t
#include <stdlib.h> // malloc()
#include <string.h> // memset()
#include <limits.h> // CHAR_BIT

typedef uint32_t word_t;
enum {
    WORD_SIZE = sizeof(word_t), // size of one word in bytes
    BITS_PER_WORD = sizeof(word_t) * CHAR_BIT, // size of one word in bits
    MAX_WORD_VALUE = UINT32_MAX // max value of one word
};

typedef struct {
    word_t *words;
    int nwords;
    int nbytes;
} bitmap_t;

inline int WORD_OFFSET(int b) { return b / BITS_PER_WORD; }
inline int BIT_OFFSET(int b) { return b % BITS_PER_WORD; }

inline void setbit(bitmap_t bitmap, int n) { bitmap.words[WORD_OFFSET(n)] |= (1 << BIT_OFFSET(n)); }
inline void flipbit(bitmap_t bitmap, int n) { bitmap.words[WORD_OFFSET(n)] ^= (1 << BIT_OFFSET(n)); }
inline void clearbit(bitmap_t bitmap, int n) { bitmap.words[WORD_OFFSET(n)] &= ~(1 << BIT_OFFSET(n)); }
inline int getbit(bitmap_t bitmap, int n) { return (bitmap.words[WORD_OFFSET(n)] & (1 << BIT_OFFSET(n))) != 0; }

inline void clearall(bitmap_t bitmap) {
    int i;
    for (i = bitmap.nwords - 1; i >= 0; i--) {
        bitmap.words[i] = 0;
    }
}

inline void setall(bitmap_t bitmap) {
    int i;
    for (i = bitmap.nwords - 1; i >= 0; i--) {
        bitmap.words[i] = MAX_WORD_VALUE;
    }
}

bitmap_t bitmap_create(int nbits) {
    bitmap_t bitmap;
    bitmap.nwords = nbits / BITS_PER_WORD + 1;
    bitmap.nbytes = bitmap.nwords * WORD_SIZE;
    bitmap.words = malloc(bitmap.nbytes);

    if (bitmap.words == NULL) { // could not allocate memory
        printf("ERROR: Could not allocate (enough) memory.");
        exit(1);
    }

    clearall(bitmap);
    return bitmap;
}

void bitmap_free(bitmap_t bitmap) {
    free(bitmap.words);
}

【问题讨论】:

    标签: c opengl conways-game-of-life


    【解决方案1】:

    这是我的 OGL Game of Life 实现的代码。

    这会上传纹理(每次要更新数据时都这样做):

    glTexImage2D( GL_TEXTURE_2D, 0, 1, game->width, game->height, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, game->culture[game->phase] );
    

    game-&gt;culture[phase]char* 类型的数据数组,大小为 width * height(在写入和读取的两个交替数组之间进行相位切换)。

    因为使用了GL_LUMINANCE,所以颜色只会是黑色和白色。

    另外,你需要用这个设置矩形(每一帧,但我猜你已经知道了)

    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    
    
        glBegin(GL_QUADS);                                  // Draw A Quad
            glVertex3f(-1.0f, 1.0f, 0.0f);                  // Top Left
            glTexCoord2i( 1, 0 );
            glVertex3f( 1.0f, 1.0f, 0.0f);                  // Top Right
            glTexCoord2i( 1, 1 );
            glVertex3f( 1.0f,-1.0f, 0.0f);                  // Bottom Right
            glTexCoord2i( 0, 1 );
            glVertex3f(-1.0f,-1.0f, 0.0f);                  // Bottom Left
            glTexCoord2i( 0, 0 );
        glEnd();
    

    当然,您可以使用缓冲区并将“模型”保留在 GPU 内存中,但仅使用一个 quad 就没有必要。

    【讨论】:

    • 由于我在一个字节中存储 8 位,是否需要在将其提供给 OpenGL 之前将其转换为每字节一位,或者它是否具有理解掩码存储的模式?
    • @nightcracker 看到我在制作 GoL 时问的这个问题 stackoverflow.com/questions/327642/…
    • 你的问题几乎是我的问题:) 谢谢。虽然我有点担心,因为这会使内存使用量增加 8 倍。感谢上帝,我将我的应用程序模块化,所以我可以轻松更改系统。
    • @nightcracker 是的,使用所有 8 位的想法在制作 Game of Life 时非常明显。但是您必须知道,打包位会使您在每个单元写入或读取时多使用两次操作,这比 RAM 重要得多。此外,您将比 RAM 限制更早地达到 OGL 纹理大小限制。无论如何,数组方法效率极低(您花费大部分时间迭代死单元格或重复模式),如果您希望它真正有效,请转到 hashlife
    【解决方案2】:

    首先考虑在 GPU 上通过 2 个 OpenGL 纹理之间的乒乓来进行模拟。如果不是一些复杂的优化 - Conway's Life 对于 GPU 来说是一项非常简单的任务。这将需要 2 个帧缓冲区对象和对着色器的一些了解。

    Edit-1:示例片段着色器(大脑编译)

    #version 130
    uniform sampler2D input;
    out float life;
    void main() {
        ivec2 tc = ivec2(gl_FragCoord);
        float orig = texelFetch(input,tc,0);
        float sum = orig+
            texelFetchOffset(input,tc,0,ivec2(-1,0))+
            texelFetchOffset(input,tc,0,ivec2(+1,0))+
            texelFetchOffset(input,tc,0,ivec2(0,-1))+
            texelFetchOffset(input,tc,0,ivec2(0,+1))+
            texelFetchOffset(input,tc,0,ivec2(-1,-1))+
            texelFetchOffset(input,tc,0,ivec2(-1,+1))+
            texelFetchOffset(input,tc,0,ivec2(+1,-1))+
            texelFetchOffset(input,tc,0,ivec2(+1,+1));
        if(sum<1.9f)
            life = 0.0f;
        else if(sum<2.1f)
            life = orig;
        else life = 1.0f;
    }
    

    顶点着色器是一个简单的传递:

    #version 130
    in vec2 vertex;
    void main() {
       gl_Position = vec4(vertex,0.0,1.0);
    }
    

    【讨论】:

    • 再次阅读我的问题:“(我对着色器一无所知)”。虽然我很想学习:)
    • 有什么建议吗? (虽然我认为从着色器开始会错位。我可以获得一个基本代码来工作(所以我可以编译:D),但我什至无法让指挥官的 sn-p 工作)。
    【解决方案3】:

    如果您坚持使用 OpenGL,最简单的方法是将您的位图作为纹理上传,然后渲染与该纹理映射的四边形。上传位看起来像这样:

    glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, width, height, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, data);
    

    假设每个单元格都是一个字节,0 代表黑色,0xFF 代表白色。请注意,在某些 OpenGL 版本中,widthheight 必须是 2 的幂。

    【讨论】:

      【解决方案4】:

      旧版本的 OpenGL 提供了直接绘制位图而不需要中间纹理的功能:glBitmap 与其他绘制图像的方法相比,glBitmap 相当慢,但由于人们很少使用它,所以这还不错.

      http://www.opengl.org/sdk/docs/man/xhtml/glBitmap.xml

      使用glRasterPosglWindowPos 放置位图。

      http://www.opengl.org/sdk/docs/man/xhtml/glRasterPos.xml http://www.opengl.org/sdk/docs/man/xhtml/glWindowPos.xml

      位图有一个小陷阱:如果使用glRasterPosglWindowPos 设置的光栅位置在视口之外,则不会绘制位图的任何部分,即使它到达视口;请参阅glBitmap 的参考页面了解解决方法。

      【讨论】:

        猜你喜欢
        • 2012-12-20
        • 1970-01-01
        • 1970-01-01
        • 2013-02-01
        • 1970-01-01
        • 1970-01-01
        • 2017-10-03
        • 1970-01-01
        • 2011-12-08
        相关资源
        最近更新 更多