在OpenGL位图呈现方法? [英] Bitmap rendering methodology in OpenGL?

查看:178
本文介绍了在OpenGL位图呈现方法?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我一直对位的位图分析器最近在纯C只是为了了解更简单的图​​像格式的低水平运作。迄今为止,在使用位图文件维基百科的文章,我已经能够(我在想什么至少)正确分析信息 - 至少,大部分

问题是,我不太知道该怎么从那里做的事:因为我一直在用3.1环境中工作,我有机会获得更多的现代化功能,这是很好的,虽然我还是输了。我有一个GLFW窗口设置和到目前为止还没有真正呈现的任何东西,因为我刚才一直注重解析/低级别的细节。

因为我想真的很难避免看实际code的例子,这会是很酷的,如果有人可以给我什么背后涉及的渲染位图过程中,只使用OpenGL的解释/ GLFW和ISO C标准库。

虽然我有一个适当的情侣着色器,我能够加载它们没有任何问题,我在想,我需要做的是渲染[无形]四是顺应尺寸(宽,高)图像本身的,然后所述像素数据传递到OpenGL的。然而,主要的问题是着色器的设置是这样的:

顶点着色器

 的#Version 150
#extension GL_ARB_separate_shader_objects:启用在VEC2位置布局(位置= 0);
在VEC2 UV_In布局(位置= 1);出VEC2紫外线;无效的主要()
{
    GL_POSITION = vec4(位置,0.0,1.0F);
    UV = UV_In;
}

片段着色器

 的#Version 150
#extension GL_ARB_separate_shader_objects:启用在VEC2 UV;出VEC3输出;统一sampler2D TheSampler;无效的主要()
{
    输出=的Texture2D(TheSampler,UV).rgb;
}

和我不知道如何获取实际的UV坐标的着色器需要。我想我需要生成顶点,将它们存储在一个数组,并调用的东西沿着 glVertexAttribPointer的线(...)的UV坐标,但我不知道我应该从图像用什么数据来获得这个,我,甚至是否拥有它已经在函数中解析。我会想象它会涉及爬行使用循环内/外(外再presenting的X,内部的Y)的行/列的时尚形象。不过,我觉得这个有些困惑,我不知​​道这是我需要的。

无论哪种方式,如何做到这一点的任何建议将不胜AP preciated。


实际code解析图像( HEADER_LENGTH = 54个字节):

  GLuint Image_LoadBmp(为const char * FNAME,image_bmp_t *数据)
{
    uint8_t有头[HEADER_LENGTH]    FILE * F = FOPEN(FNAME,RB);    如果(!F)
    {
        的printf(错误:文件\\%s \\中无法打开[可能]由于不正确的路径:/ \\ n,FNAME);        返回0; //返回false
    }    数据 - >文件名=的strdup(FNAME); // TODO:写的strdup一个包装它的退出回​​报NULL方案    常量为size_t num_bytes_read = FREAD((void *的)头,的sizeof(uint8_t有),HEADER_LENGTH,F);    如果(num_bytes_read!= HEADER_LENGTH)
    {
        的printf(错误:文件\\%s \\的可能不是因为被打开头大小是_SIZE_T_SPECIFIER字节;\\
                这是一个无效的格式\\ n,FNAME,num_bytes_read);        返回0;
    }    如果(标题[0]!= *(uint8_t有*)的B||标题[1]!= *(uint8_t有*)的M)
    {
        的printf(错误:文件\\%s \\中没有一个有效的签名\\ n,FNAME);        返回0;
    }    DATA-> IMAGE_SIZE = *(uint32_t的*)及(报头[0x22]);
    数据 - > header_size =(uint32_t的)(标题[0x0E的]);
    数据 - >宽度=(uint32_t的)(标题[0×12]);
    数据 - >身高=(uint32_t的)(标题[0x16]);
    数据 - > pixel_data_pos =(uint32_t的)(标题[0x0A的]);
    数据 - > COM pression_method =(uint8_t有)(标题[0X1E]);
    数据 - > BPP =(uint8_t有)(标题[为0x1C]);    // TODO(也许):增加对其他COM pression方法的支持    如果(数据并行>!COM pression_method = CM_RGB)
    {
        看跌期权(错误:文件\\%s \\中没有此解析器支持COM pression方法; \\ N\\
              \\ t目前,支持COM pression方法是:\\ n\\
              \\ t - BI_RGB \\ n \\ n
             );        返回0;
    }    返回1;
}

和根据从当前图像采集的图像信息我调试输出如下所示:

 信息为资产/精灵/中堂/ nave0001.bmp{
     大小= 3612头的大小= 40
     宽度= 27高度= 43
     像素阵列地址= 54的COM pression方法= 0
     每像素= 24位
 }


解决方案

首先我要说:你在读头的做法几乎是完美的。唯一的缺点:您code不处理字节序和你截断你的头的字段(它会破坏在任何尺寸大于255的图像

下面是一个修复

 数据 - > IMAGE_SIZE =(uint32_t的)标题[0x22] | (uint32_t的)标题[0x23]<< 8 | (uint32_t的)标题[0X24]<< 16 | (uint32_t的)标题[0x25]<< 24;

和所有其他领域超过8位大相同的模式。每个头字段演员阵容时,只需要prevent截断。它转换为目标变量的类型。也不用担心性能,现代编译器把它变成非常有效的code。

到目前为止,你的函数仍然缺乏读取图像数据。我只是假定数据将在现场数据 - 方式>像素稍后

您在图像读过之后,你可以将它传递给OpenGL的。 OpenGL的管理其图像所谓的纹理对象。通常的节:


  1. 与glGenTextures创建一个纹理对象名称

  2. 与glBindTexture绑定纹理对象

  3. 与glPixelStorei组像素传送参数上的所有GL_UNPACK_ ...参数

  4. 上传与glTexImage2D纹理
    5。


    • 纹理映射之交


    • 生成贴图。


这正好如下:

  GLuint texName;
glGenTexture(1,&安培; texName);
glBindTexture(GL_TEXTURE_2D,texName);glPixelStorei(GL_UNPACK_SWAP_BYTES,GL_FALSE);
glPixelStorei(GL_UNPACK_LSB_FIRST,GL_FALSE);
glPixelStorei(GL_UNPACK_ROW_LENGTH,0); //也可以设置为图像大小,但是这仅用于
glPixelStorei(GL_UNPACK_IMAGE_HEIGHT,0); //如果想仅加载图像的一个子集
glPixelStorei(GL_UNPACK_SKIP_PIXELS,0);
glPixelStorei(GL_UNPACK_SKIP_ROWS,0);
glPixelStorei(GL_UNPACK_SKIP_IMAGES,0);
glPixelStorei(GL_UNPACK_SKIP_ALIGNMENT,4); //一个人的pretty重要。对于真彩色的DIB走线4GLenum internalformat;
开关(数据并行> BPP){
案例24:
    internalformat = GL_RGB;打破;案例32:
    internalformat = GL_RGBA;打破;
}glTexImage2D(GL_TEXTURE_2D,0,internalformat,
             数据 - >宽线,数据>的高度,0
             GL_BRGA,GL_UNSIGNED_INT_8_8_8_8线,数据>像素);glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);

该GL_UNSIGNED_INT_8_8_8_8类型需要解释。你看,治疗的DIB一个32位无符号整数ALS复合色结构。其实在Windows中,你可以找到一个颜色类型,这是一个typedef-ED整数。这就是包含在的DIB的。通过使用BGRA格式与4×8组成整型我们做的OpenGL的正是格式解压像素。

I've been working on a bit of a bitmap parser lately in pure C just to understand the low level workings of simpler image formats. So far, using Wikipedia's article on bitmap files, I've been able to (what I think at least) parse the information correctly - at least, most of it.

The problem is that I'm not quite sure what to do from there: since I've been working with a 3.1 context, I have access to many more modernized features which is nice, albeit I'm still lost. I have a window setup with GLFW and so far haven't really rendered anything because I've just been focusing on the parsing/low level details.

Since I'm trying to really hard to avoid looking at actual code examples, it'd be cool if someone could explain to me what the process involved behind rendering a bitmap is, using just OpenGL/GLFW and the ISO C Standard Library.

While I have a couple shaders in place, and I'm able to load them without any issues, I'm thinking that what I need to do is render an [invisible] quad which conforms to the dimensions (width, height) of the image itself, and then pass the pixel data to OpenGL. The main problem however is the shaders are setup like this:

Vertex Shader

#version 150
#extension GL_ARB_separate_shader_objects : enable

layout(location = 0) in vec2 Position;
layout(location = 1) in vec2 UV_In;

out vec2 UV;

void main()
{
    gl_Position = vec4( Position, 0.0f, 1.0f );
    UV = UV_In;
}

Fragment Shader

#version 150
#extension GL_ARB_separate_shader_objects : enable

in vec2 UV;

out vec3 Output;

uniform sampler2D TheSampler;

void main()
{
    Output = texture2D( TheSampler, UV ).rgb;
}

And I'm not sure how to obtain the actual UV coordinates the shader requires. I'm thinking I'll need to generate the vertices, store them in an array, and call something along the lines of glVertexAttribPointer(...) for the UV coordinates, but I'm not sure what data I should use from the image to obtain this, or even whether or not I have it already parsed within the function. I would imagine it would involve crawling the image using an inner/outer for loop (the outer representing the x, the inner the y) in a row/column fashion. Still, I feel somewhat confused about this and I'm not sure if this is what I need.

Either way, any advice on how to do this would be greatly appreciated.


The actual code to parse the image ( HEADER_LENGTH = 54 bytes ):

GLuint Image_LoadBmp( const char* fname, image_bmp_t* data )
{   
    uint8_t  header[ HEADER_LENGTH ];

    FILE* f = fopen( fname, "rb" );

    if ( !f )
    {
        printf( "ERROR: file \"%s\" could not be opened [likely] due to incorrect path. :/ \n", fname );

        return 0; // return false
    }

    data->filename = strdup( fname ); // TODO: write a wrapper for strdup which exits program on NULL returns

    const size_t num_bytes_read = fread( ( void* )header, sizeof( uint8_t ), HEADER_LENGTH, f );

    if ( num_bytes_read != HEADER_LENGTH )
    {
        printf( "ERROR: file \"%s\" could not be opened due to header size being " _SIZE_T_SPECIFIER " bytes; "\
                "this is an invalid format. \n", fname, num_bytes_read );

        return 0;
    }

    if ( header[ 0 ] != *( uint8_t* )"B" || header[ 1 ] != *( uint8_t* )"M" )
    {
        printf( "ERROR: file \"%s\" does NOT have a valid signature \n", fname );

        return 0;
    }

    data->image_size         = *( uint32_t* )&( header[ 0x22 ] );
    data->header_size        = ( uint32_t )( header[ 0x0E ] );
    data->width              = ( uint32_t )( header[ 0x12 ] );
    data->height             = ( uint32_t )( header[ 0x16 ] );
    data->pixel_data_pos     = ( uint32_t )( header[ 0x0A ] );
    data->compression_method = ( uint8_t )( header[ 0x1E ] );
    data->bpp                = ( uint8_t )( header[ 0x1C ] );

    // TODO (maybe): add support for other compression methods

    if ( data->compression_method != CM_RGB )
    {
        puts( "ERROR: file \"%s\" does NOT have a supported compression method for this parser; \n" \
              "\t Currently, the compression methods supported are: \n" \
              "\t - BI_RGB \n\n"
             );

        return 0;
    }



    return 1;
}

And my debug output according to the image information gathered from the current image looks as follows:

Info for "assets/sprites/nave/nave0001.bmp" {  
     Size        = 3612      Header Size = 40  
     Width       = 27      Height      = 43  
     Pixel Array Address = 54      Compression Method  = 0  
     Bits Per Pixel      = 24
 }

解决方案

First let me say: Your approach on reading the header is almost perfect. Only drawback: Your code doesn't deal with Endianess and you're truncating your header's fields (it will break for images larger than 255 in any dimension.

Here's a fix

data->image_size = (uint32_t)header[0x22] | (uint32_t)header[0x23] << 8 | (uint32_t)header[0x24] << 16 | (uint32_t)header[0x25] << 24;

And the same pattern for all other fields larger than 8 bit. The cast for each header field is neccessary to prevent truncation. Cast it to the destination variable type. Also don't worry about performance, modern compilers turn this into very efficient code.

So far your function still lacks reading the image data. I'll just assume the data will be in a field data->pixels later on.

After you've read in your image you can pass it to OpenGL. OpenGL manages its images in so called "Texture Objects". The usual stanza is:

  1. Create a texture object name with glGenTextures
  2. Bind the texture object with glBindTexture
  3. Set pixel transfer parameters with glPixelStorei on all of the GL_UNPACK_… parameters
  4. Upload the texture with glTexImage2D 5.

    • Turn of mipmapping

    or

    • Generate Mipmaps.

This goes as follows

GLuint texName;
glGenTexture(1, &texName);
glBindTexture(GL_TEXTURE_2D, texName);

glPixelStorei(GL_UNPACK_SWAP_BYTES, GL_FALSE);
glPixelStorei(GL_UNPACK_LSB_FIRST, GL_FALSE);
glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);   // could also be set to image size, but this is used only
glPixelStorei(GL_UNPACK_IMAGE_HEIGHT, 0); // if one wants to load only a subset of the image
glPixelStorei(GL_UNPACK_SKIP_PIXELS, 0);
glPixelStorei(GL_UNPACK_SKIP_ROWS, 0);
glPixelStorei(GL_UNPACK_SKIP_IMAGES, 0);
glPixelStorei(GL_UNPACK_SKIP_ALIGNMENT, 4); // that one's pretty important. For TrueColor DIBs the alignment is 4

GLenum internalformat;
switch(data->bpp) {
case 24:
    internalformat = GL_RGB; break;

case 32:
    internalformat = GL_RGBA; break;
}

glTexImage2D(GL_TEXTURE_2D, 0, internalformat,
             data->width, data->height, 0
             GL_BRGA, GL_UNSIGNED_INT_8_8_8_8, data->pixels);

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);

The GL_UNSIGNED_INT_8_8_8_8 type needs explanation. You see, DIBs treat a 32 bit unsigned integer als a compound color structure. In fact in Windows you can find a color type, which is a typedef-ed integer. And that's what's contained in DIBs. By using BGRA format with the 4×8 component integer type we make OpenGL unpack a pixel in exactly that format.

这篇关于在OpenGL位图呈现方法?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆