- 参照 URL
- PFM Portable FloatMap Image Format
- Light Probe Image Gallery by Paul Debevec
- 下のようなLight Probe の画像のうち, PFM フォーマットの画像を読み込んで PPM フォーマットに変換する C のコード
- コードは未整理かつ かなり決めうちだけど, メモとして以下に貼り付け
// PFM を読み込む FILE* p_texture_file = nullptr; { const char* pfm_filename = "textures/memorial.pfm"; fopen_s( ( & p_texture_file ), pfm_filename, "rb" ); const bool is_success = ( p_texture_file != nullptr ); MY_ASSERT_MSG( is_success ); } f32* p_texture_buffer = nullptr; u32 texture_width = 0; u32 texture_height = 0; u32 total_texture_pixel_num = 0; u32 total_texture_f32_num = 0; u32 total_texture_byte_size = 0; { char type_str[ 128 ]; f32 byte_order; const u32 c_buffer_size = 256; char p_buffer[ c_buffer_size ]; u32 line_num = 0; // ヘッダの読み込み while ( ( fgets( p_buffer, c_buffer_size, p_texture_file ) != nullptr ) ) { if ( line_num < 3 ) { printf( "line %d: %s", line_num, p_buffer ); if ( line_num == 0 ) { sscanf_s( p_buffer, "%2c", ( &type_str ) ); type_str[ 2 ] = '\0'; } else if ( line_num == 1 ) { sscanf_s( p_buffer, "%d %d", ( &texture_width ), ( &texture_height ) ); total_texture_pixel_num = texture_width * texture_height; total_texture_f32_num = 3 * total_texture_pixel_num; total_texture_byte_size = sizeof( f32 ) * total_texture_f32_num; } else if ( line_num == 2 ) { sscanf_s( p_buffer, "%f", ( &byte_order ) ); break; } } line_num++; } if ( p_texture_buffer == nullptr ) { p_texture_buffer = new f32[ 3 * total_texture_pixel_num ](); MY_ASSERT( p_texture_buffer != nullptr ); } const size_t read_texture_byte_size = fread( p_texture_buffer, sizeof( f32 ), total_texture_f32_num, p_texture_file ); const bool is_success = ( read_texture_byte_size == total_texture_byte_size ); MY_ASSERT_MSG( ( ! is_success ), "read_texture_byte_size = %d, assumed_size = %d", read_texture_byte_size, total_texture_byte_size ); // Write image to PPM file. { FILE *f; fopen_s( ( &f ), "test_pfm.ppm", "w" ); fprintf( f, "P3\n%d %d\n%d\n", texture_width, texture_height, 255 ); for ( u32 y = 0; y < texture_height; ++y ) { for ( u32 x = 0; x < texture_width; ++x ) { // PFM : specified in left to right, bottom to top order // PPM : A raster of Height rows, in order from top to bottom. // PPM に書き出すために, Y 反転 const u32 read_y = ( texture_height - y - 1 ); const u32 pixel_index = read_y * texture_width + x; const f32 r = p_texture_buffer[ 3 * pixel_index ]; const f32 g = p_texture_buffer[ 3 * pixel_index + 1 ]; const f32 b = p_texture_buffer[ 3 * pixel_index + 2 ]; fprintf( f, "%d %d %d ", toInt( r ), toInt( g ), toInt( b ) ); } } } printf( "header = %s\n", type_str ); printf( "width = %d, height = %d\n", texture_width, texture_height ); printf( "byte_order = %f\n", byte_order ); printf( "total_texture_pixel_num = %d, total_texture_byte_size = %d\n", total_texture_pixel_num, total_texture_byte_size ); printf( "Total line_num = %d\n", line_num ); MY_ASSERT( p_texture_buffer != nullptr ); }
- 追記
- このままだと使いにくかったので, 以下は構造体にしたバージョン
- 下は使い方と構造体 PfmTexture
PfmTexture s_PfmTexture; // ライトプローブテクスチャの読み込み const char* pfm_filename = "textures/grace_probe.pfm"; s_PfmTexture.create( pfm_filename ); // PPM に変換 s_PfmTexture.writePPM( "test_output.ppm" ); // x = 32, y = 58 ピクセル目のカラーを取得 Vec rgb; s_PfmTexture.readPixel( ( & rgb ), x, y );
struct PfmTexture { PfmTexture() : mpTextureFilename( nullptr ), mpFile( nullptr ), mpTextureBuffer( nullptr ), mTextureWidth( 0 ), mTextureHeight( 0 ), mTotalTexturePixelNum( 0 ), mTotalTextureF32Num( 0 ), mTotalTextureByteSize( 0 ), mByteOrder( - 1.0f ) { } ~PfmTexture() { destroy(); } bool create( const char* filename ) { { MY_NULL_ASSERT( filename ); mpTextureFilename = filename; fopen_s( ( & mpFile ), mpTextureFilename, "rb" ); const bool is_success = ( mpFile != nullptr ); MY_ASSERT_MSG( is_success, "Failed to open %s.\n", mpTextureFilename ); } char type_str[ 128 ]; const u32 c_buffer_size = 256; char p_buffer[ c_buffer_size ]; u32 line_num = 0; // ヘッダの読み込み while ( ( fgets( p_buffer, c_buffer_size, mpFile ) != nullptr ) ) { if ( line_num < 3 ) { printf( "line %d: %s", line_num, p_buffer ); if ( line_num == 0 ) { sscanf_s( p_buffer, "%2c", ( &type_str ) ); type_str[ 2 ] = '\0'; } else if ( line_num == 1 ) { sscanf_s( p_buffer, "%d %d", ( & mTextureWidth ), ( & mTextureHeight ) ); mTotalTexturePixelNum = mTextureWidth * mTextureHeight; mTotalTextureF32Num = 3 * mTotalTexturePixelNum; mTotalTextureByteSize = sizeof( f32 ) * mTotalTextureF32Num; } else if ( line_num == 2 ) { sscanf_s( p_buffer, "%f", ( & mByteOrder ) ); break; } } line_num++; } if ( mpTextureBuffer == nullptr ) { mpTextureBuffer = new f32[ mTotalTextureF32Num ](); MY_ASSERT( mpTextureBuffer != nullptr ); } // ピクセルデータを一気に読み込む const size_t read_texture_byte_size = fread( mpTextureBuffer, sizeof( f32 ), mTotalTextureF32Num, mpFile ); const bool is_success = ( read_texture_byte_size == mTotalTextureByteSize ); MY_ASSERT_MSG( ( ! is_success ), "read_texture_byte_size = %d, assumed_size = %d", read_texture_byte_size, mTotalTextureByteSize ); return true; } void destroy() { if ( mpFile != nullptr ) { fclose( mpFile ); mpFile = nullptr; } if ( mpTextureBuffer != nullptr ) { delete[] mpTextureBuffer; mpTextureBuffer = nullptr; } } void readPixel( Vec* p_rgb, const u32 x, const u32 y, const bool enable_y_flip = false ) { MY_NULL_ASSERT( p_rgb ); MY_NULL_ASSERT( mpFile ); MY_NULL_ASSERT( mpTextureBuffer ); MY_ASSERT( x < mTextureWidth ); MY_ASSERT( y < mTextureHeight ); const u32 read_y = ( enable_y_flip ? ( mTextureHeight - y - 1 ) : y ); const u32 pixel_index = read_y * mTextureWidth + x; const u32 array_index = 3 * pixel_index; ( *p_rgb ).x = mpTextureBuffer[ array_index ]; ( *p_rgb ).y = mpTextureBuffer[ array_index + 1 ]; ( *p_rgb ).z = mpTextureBuffer[ array_index + 2 ]; } void writePPM( const char* ppm_filename ) { FILE* f; fopen_s( ( & f ), ppm_filename, "w" ); fprintf( f, "P3\n%d %d\n%d\n", mTextureWidth, mTextureHeight, 255 ); for ( u32 y = 0; y < mTextureHeight; ++y ) { for ( u32 x = 0; x < mTextureWidth; ++x ) { // PFM : specified in left to right, bottom to top order // PPM : A raster of Height rows, in order from top to bottom. // PPM に書き出すために, Y 反転 Vec rgb; readPixel( ( & rgb ), x, y, true ); fprintf( f, "%d %d %d ", toInt( rgb.x ), toInt( rgb.y ), toInt( rgb.z ) ); } } fclose( f ); } const char* mpTextureFilename; FILE* mpFile; f32* mpTextureBuffer; u32 mTextureWidth; u32 mTextureHeight; u32 mTotalTexturePixelNum; u32 mTotalTextureF32Num; u32 mTotalTextureByteSize; f32 mByteOrder; };