1

我一直在研究位图加载器,主要目标只是正确解析数据并在 OpenGL 中渲染。我现在需要在 x/y(即逐个像素)的基础上绘制像素(至少,就渲染而言,我认为这是我需要做的)。我已经绑定了纹理对象并调用了glTexImage2D(...).

目前,我遇到的问题是逐像素算法。

据我了解,位图(又名 DIB)文件将颜色数据存储在所谓的像素数组中。每行像素由字节数组成x,每个像素的字节数可被 4(每像素 32 位)、3(每像素 24 位)、2(每像素 16 位)或 1(每像素 8 位)整除像素)。

我认为需要遍历像素,同时计算像素数组中的正确偏移量,该偏移量相对于其像素 x/y 坐标。这是真的吗?如果没有,我该怎么办?老实说,我有点困惑,尽管我在前一段时间问过的这个问题中做了针对我的事情,但这种方法是否正确。

我认为逐个像素地处理它是正确的方法,主要是因为渲染一个四边形glVertex*glTexCoord*只产生一个灰色的矩形(当时我认为 OpenGL 会自己处理这个问题,因此为什么要尝试首先)。

我还应该注意,虽然我的问题显示的是 OpenGL 3.1 着色器,但我转移到了 SDL 1.2,因此我可以暂时使用即时模式,直到我实现了正确的算法,然后切换回现代 GL。


我正在解析的测试图像:

位图图像

它的数据输出(由于长度很长而被粘贴): http: //pastebin.com/6RVhAVRq

和代码

void R_RenderTexture_PixByPix( texture_bmp_t* const data, const vec3 center )
{


    glBindTexture( GL_TEXTURE_2D, data->texbuf_id );

    glBegin( GL_POINTS );
    {
        const unsigned width  = data->img_data->width + ( unsigned int ) center[ VEC_X ];
        const unsigned height = data->img_data->height + ( unsigned int ) center[ VEC_Y ];

        const unsigned bytecount    = GetByteCount( data->img_data->bpp );

        const unsigned char* pixels = data->img_data->pixels;

        unsigned color_offset = 0;
        unsigned x_pixel;

        for ( x_pixel = center[ VEC_X ]; x_pixel < width; ++x_pixel )
        {
            unsigned y_pixel;

            for ( y_pixel = center[ VEC_Y ]; y_pixel < height; ++y_pixel )
            {

            }

            const bool do_color_update = true; //<--- replace true with a condition which checks to see if the color needs to be updated.

            if ( do_color_update )
            {
                glColor3fv( pixels + color_offset );
            }

            color_offset += bytecount;
        }
    }
    glEnd();

    glBindTexture( GL_TEXTURE_2D, 0 );
}
4

2 回答 2

2

您在代码中完全错过了 OpenGL 纹理的要点。纹理为您保存图像,光栅器为您对像素数据进行所有迭代。无需自己编写缓慢的像素推动器循环。

正如您的代码现在所代表的那样,纹理完全是伪造的并且什么都不做。你可以完全省略对 glBindTexture 的调用,它仍然可以工作——或者不工作,因为你实际上并没有绘制任何东西,你只是设置了 glColor 状态。要绘制一些东西,你必须调用 glVertex。

那么为什么不利用现代 GPU 的像素推动性能并实际使用纹理呢?这个怎么样:

void R_RenderTexture_PixByPix( texture_bmp_t* const data, const vec3 center )
{
    if( 0 == data->texbuf_id ) {
        glGenTextures(1, &(data->texbuf_id));
        glBindTexture( GL_TEXTURE_2D, data->texbuf_id );

        glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
        // there are a few more, but the defaults are ok
        // if you didn't change them no need for further unpack settings

        GLenum internal_format;
        GLenum format;
        GLenum type;
        switch(data->img_data->bpp) {
        case 8:
            // this could be a palette or grayscale
            internal_format = GL_LUMINANCE8;
            format = GL_LUMINANCE;
            type = GL_UNSIGNED_BYTE;
            break;

        case 15:
            internal_format = GL_RGB5;
            format = GL_BGR; // BMP files have BGR pixel order
            type = GL_UNSIGNED_SHORT_1_5_5_5;
            break;

        case 16:
            internal_format = GL_RGB8;
            format = GL_BGR; // BMP files have BGR pixel order
            type = GL_UNSIGNED_SHORT_5_6_5;
            break;

        case 24:
            internal_format = GL_RGB8;
            format = GL_BGR; // BMP files have BGR pixel order
            type = GL_UNSIGNED_BYTE;
            break;

        case 32:
            internal_format = GL_RGB8;
            format = GL_BGR; // BMP files have BGR pixel order
            type = GL_UNSIGNED_INT_8_8_8_8;
            break;

        }

        glTexImage2D( GL_TEXTURE_2D, 0, internal_format,
                      data->img_data->width, data->img_data->height, 0,
                      format, type, data->img_data->pixels );
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
    } else {
        glBindTexture( GL_TEXTURE_2D, data->texbuf_id );
    }


    static GLfloat verts[] = {
            0, 0, 
            1, 0,
            1, 1,
            0, 1
    };    
    // the following is to address texture image pixel centers
    // tex coordinates 0 and 1 are not on pixel centers!
    float const s0 = 1. / (2.*tex_width);
    float const s1 = ( 2.*(tex_width-1) + 1.) / (2.*tex_width);
    float const t0 = 1. / (2.*tex_height);
    float const t1 = ( 2.*(tex_height-1) + 1.) / (2.*tex_height);
    GLfloat texcoords[] = {
            s0, t0,
            s1, t0,
            s1, t1,
            s0, t1
    };

    glEnableClientState(GL_VERTEX_ARRAY);
    glEnableClientState(GL_TEXTURE_COORD_ARRAY);

    glEnable(GL_TEXTURE_2D);

    glVertexPointer(2, GL_FLOAT, 0, verts);
    glTexCoordPointer(2, GL_FLOAT, 0, texcoords);

    glColor4f(1., 1., 1., 1.);
    glDrawArrays(GL_QUADS, 0, 4);

    glDisableClientState(GL_VERTEX_ARRAY);
    glDisableClientState(GL_TEXTURE_COORD_ARRAY);

    glBindTexture( GL_TEXTURE_2D, 0 );
}
于 2013-01-15T11:27:26.923 回答
1

你的直觉基本上是正确的。像素存储为字节数组,但字节被排列成连续的组,每组代表一个像素。要解决单个像素,您需要进行如下计算:

unsigned char* image_data = start_of_pixel_data;
unsigned char* pixel_addr = image_data + bytes_per_pixel * (y * width_in_pixels + x);

请注意以像素为单位的宽度,因为有时在行的末尾会有填充以使以字节为单位的总行宽达到 4/8/16/32/64/等的倍数。我建议首先以十六进制查看位图的实际字节,以了解发生了什么。这是一个很好的学习练习,会让您对自己的像素行走代码充满信心,这正是您想要的。您可能可以使用调试器来执行此操作,或者使用printf图像字节编写一个简单的循环。

于 2013-01-15T06:38:25.517 回答