2

私は最近、より単純な画像形式の低レベルの動作を理解するために、純粋なCでビットビットパーサーのビットに取り組んでいます。これまでのところ、ビットマップファイルに関するウィキペディアの記事を使用して、私は(少なくとも私が思うに)情報を正しく解析することができました-少なくとも、そのほとんど。

問題は、そこから何をすべきかよくわからないことです。3.1コンテキストで作業しているので、まだ迷っていますが、より多くの最新機能にアクセスできます。私はGLFWを使用してウィンドウを設定していますが、解析/低レベルの詳細に焦点を合わせているため、これまでのところ実際には何もレンダリングされていません。

私は実際のコード例を見ないように一生懸命努力しているので、OpenGL /GLFWとISOC標準ライブラリだけを使用して、ビットマップのレンダリングの背後にあるプロセスを誰かが説明してくれると便利です。

いくつかのシェーダーを配置していて、問題なくロードできますが、必要なのは、の寸法(幅、高さ)に一致する[非表示]クワッドをレンダリングすることだと思います。画像自体を作成し、ピクセルデータをOpenGLに渡します。ただし、主な問題は、シェーダーが次のように設定されていることです。

バーテックスシェーダー

#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;
}

フラグメントシェーダー

#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;
}

また、シェーダーに必要な実際のUV座標を取得する方法がわかりません。頂点を生成し、それらを配列に格納し、UV座標の線に沿って何かを呼び出す必要があると思いますが、glVertexAttribPointer(...)これを取得するために画像からどのデータを使用する必要があるかわかりません。関数内ですでに解析されているかどうかに関係なく。行/列の方法で内側/外側のforループ(外側はxを表し、内側はyを表す)を使用して画像をクロールする必要があると思います。それでも、私はこれについていくらか混乱していると感じており、これが私が必要としているものであるかどうかはわかりません。

いずれにせよ、これを行う方法についてのアドバイスをいただければ幸いです。


画像を解析するための実際のコード(HEADER_LENGTH= 54バイト):

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;
}

また、現在の画像から収集された画像情報によるデバッグ出力は次のようになります。

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
 }
4

2 に答える 2

2

最初に言わせてください: ヘッダーを読む上でのあなたのアプローチはほぼ完璧です。唯一の欠点: コードは Endianess を処理せず、ヘッダーのフィールドを切り捨てています (任意の次元で 255 を超える画像では壊れます。

ここに修正があります

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

また、8 ビットより大きい他のすべてのフィールドについても同じパターンです。切り捨てを防ぐために、各ヘッダー フィールドのキャストが必要です。宛先変数型にキャストします。また、パフォーマンスについて心配する必要はありません。最新のコンパイラはこれを非常に効率的なコードに変換します。

これまでのところ、関数はまだ画像データを読み取っていません。後でデータがフィールドにあると仮定しますdata->pixels

画像を読み込んだ後、それを OpenGL に渡すことができます。OpenGL は、そのイメージをいわゆる「テクスチャ オブジェクト」で管理します。通常のスタンザは次のとおりです。

  1. glGenTextures でテクスチャ オブジェクト名を作成する
  2. テクスチャ オブジェクトを glBindTexture でバインドする
  3. すべての GL_UNPACK_… パラメータで glPixelStorei を使用してピクセル転送パラメータを設定します
  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);   // 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);

GL_UNSIGNED_INT_8_8_8_8 型には説明が必要です。ご覧のとおり、DIB は 32 ビットの符号なし整数を複合色構造として扱います。実際、Windows では、typedef された整数である色の種類を見つけることができます。それが DIB に含まれるものです。4×8 コンポーネント整数型の BGRA フォーマットを使用することで、OpenGL はピクセルをまさにそのフォーマットでアンパックします。

于 2013-01-06T23:50:10.647 に答える
1

ここにはたくさんの質問があります。これを複数の質問に分割する必要があるかもしれませんが、概要は次のとおりです。

実際のピクセル データをシェーダーに渡す必要はありません。必要なのは、ピクセル データを使用して事前に GL でテクスチャ オブジェクトを作成し、シェーダーでそのテクスチャを参照することです。描画する必要がある実際のジオメトリは、(お察しのとおり) 4 つの角と対応するテクスチャ座標 (この場合、角の各軸の 0.0 と 1.0 だけです) を持つ 1 つのクワッドです。

シェーダーの魔法は、フラグメント シェーダーが出力内のすべてのピクセルに対して実行されることです。ユーザーは、GL がシェーダーに渡すさまざまなテクスチャ座標でテクスチャをサンプリングするだけです。

(GL を初めて使用する場合は、BMP データをテクスチャに取り込む前に、単純なクワッドを最初に固定色で描画してみてください。)

于 2013-01-06T23:32:27.080 に答える