我一直在研究一个位图加载器,主要目标是除了正确解析数据并在OpenGL中渲染它之外什么都不做。我正处于需要在x / y(即逐个像素)的基础上绘制像素的点(至少,就我的渲染而言,这是我认为我需要做的事情)。我已经绑定了纹理对象并调用了glTexImage2D(...)
。
目前,我遇到的问题是逐像素算法。
据我了解,位图(也称为DIB)文件将颜色数据存储在所谓的像素数组中。每行像素由x
个字节组成,每个像素保持一个字节计数可被4(每像素32位),3(每像素24位),2(每像素16位)或1(每像素8位)。
我认为需要循环像素,同时计算像素阵列中的右偏移,相对于像素x / y坐标。这是真的吗?如果没有,我该怎么办?我真的有点困惑,不管是否做了this question I asked sometime ago中的指示,这种做法是正确的。
我认为以像素为单位进行处理是正确的方法,主要是因为
渲染一个带有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 );
}
答案 0 :(得分: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 );
}
答案 1 :(得分: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
的简单循环。