Назад | Содержание | Вперед

Обратная трассировка лучей

Текстурные карты и свойства материалов. Работа с растровыми картами (BMP, PNG, JPEG)

Однако простых числовых данных часто бывает недостаточно. Для моделирования сложных объектов часто приходится использовать текстуры. Рассмотрим несколько типов текстур, реализованных в демонстрационной программе:

Рассмотрим форматы графических файлов.

BMP

Самый простой, несжатый формат. В файле последовательно сохранены заголовок файла (BITMAPFILEHEADER), заголовок содержащий информацию о растре (BITMAPINFOHEADER), если нужно палитра (определяется по содержимому BITMAPINFOHEADER), и, собственно, построчно цвета. Трудно сказать почему, но строки сохранены снизу вверх. Еще нужно не забывать, что длинна строки в байтах должна быть кратна четырем. Если пользоваться растрами GDI (как и сделано в демонстрационной программе), типа CreateDIBSection, то можно сразу загружать в память.

Загрузка и сохранение:

bool Bitmap::LoadFromBmp(wchar *fileName)
{
    HANDLE hfile = CreateFile(fileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); 
    if (hfile == INVALID_HANDLE_VALUE)
        return false;
    DWORD nBytesRead;

    BITMAPFILEHEADER bmf;
    ReadFile(hfile, &bmf, sizeof(bmf), &nBytesRead, NULL);
    if (nBytesRead != sizeof(bmf))
        return false;

    ReadFile(hfile, &bmih, sizeof(bmih), &nBytesRead, NULL);
    if (nBytesRead != sizeof(bmih))
        return false;

    if (bmf.bfType != 0x4D42 || bmih.biSize != sizeof(bmih) || 
        bmih.biBitCount != 32 && bmih.biBitCount != 24 || 
        bmih.biPlanes != 1 || bmih.biCompression != BI_RGB)
        throw L"Must be 32 bit bitmap";

    WORD bitCount = bmih.biBitCount;

    CreateBitmap(bmih.biWidth, bmih.biHeight);

    if (bitCount == 32)
    {
        ReadFile(hfile, pBitmap, cx*cy*4, &nBytesRead, NULL);
        if (nBytesRead != cx*cy*4)
            return false;
    }
    else if (bitCount == 24)
    {
        int nLine = cx*3;
        while (nLine%4 != 0) 
            nLine++;
        
        BYTE *data = new BYTE[nLine];
        for (int j = 0; j < cy; j++)
        {
            ReadFile(hfile, data, nLine, &nBytesRead, NULL);
            if (nBytesRead != nLine)
                return false;

            PBYTE dataLine = data;
            PBYTE bitmapLine = pBitmap + 4*cx*j;
            for (int i = 0; i < cx; i++)
            {
                *bitmapLine++ = *dataLine++;
                *bitmapLine++ = *dataLine++;
                *bitmapLine++ = *dataLine++;
                *bitmapLine++ = 0;
            }
        }
        delete data;
    }
    return true;
}

bool Bitmap::SaveToBmp(wchar *fileName)
{
    HANDLE hfile = CreateFile(fileName, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL); 
    if (hfile == INVALID_HANDLE_VALUE)
        return false;
    DWORD nBytesWritten;
    
    BITMAPFILEHEADER bmf;
    bmf.bfType = 0x4D42;
    bmf.bfSize = 0;
    bmf.bfReserved1 = 0;
    bmf.bfReserved2 = 0;
    bmf.bfOffBits = sizeof(BITMAPFILEHEADER);
    WriteFile(hfile, &bmf, sizeof(bmf), &nBytesWritten, NULL);

    WriteFile(hfile, &bmih, sizeof(bmih), &nBytesWritten, NULL);

    WriteFile(hfile, pBitmap, cx*cy*4, &nBytesWritten, NULL);

    CloseHandle(hfile);
    return true;
}

За дополнительной информацией обращайтесь к MSDN (http://msdn.microsoft.com/library/).

JPEG

Один из самых распространенных форматов сжатия с потерей данных. Позволяет получать впечатляющего сжатия (в 10 – 20 раз), без видимого ухудшения качества картинки. Часто используется для сохранения текстур в библиотеках материалов. Из недостатков можно отметить появление специфических артефактов при сильных степенях сжатия (появление блоков 8 на 8, ореол вокруг резких переходов).

Загрузка и сохранение:

bool Bitmap::LoadFromJpeg(wchar *fileName)
{
    FILE *infile;
    infile = _wfopen(fileName, L"rb");
    if (!infile)
        return false;

    jpeg_decompress_struct cinfo;
    jpeg_error_mgr jerr;
    cinfo.err = jpeg_std_error(&jerr);
    jpeg_create_decompress(&cinfo);
    jpeg_stdio_src(&cinfo, infile);
    jpeg_read_header(&cinfo, TRUE);
    jpeg_start_decompress(&cinfo);

    CreateBitmap(cinfo.image_width, cinfo.image_height);

    byte *data = new byte[cx*3];
    while (cinfo.output_scanline < cinfo.image_height)
    {
        byte *bitmapLine = GetScanLine(cinfo.output_scanline);
        jpeg_read_scanlines(&cinfo, (JSAMPARRAY)&data, 1);
        if (cinfo.num_components == 3)
            CopyRGBtoBGRA(bitmapLine, data, cx);
        else
            CopyGrayscaletoBGRA(bitmapLine, data, cx);
    }
    delete data;

    jpeg_finish_decompress(&cinfo);
    jpeg_destroy_decompress(&cinfo);
    fclose(infile);
    return true;
}

bool Bitmap::SaveToJpeg(wchar *fileName)
{
    FILE *outfile;
    outfile = _wfopen(fileName, L"wb");
    if (!outfile)
        return false;

    jpeg_compress_struct cinfo;
    jpeg_error_mgr jerr;
    cinfo.err = jpeg_std_error(&jerr);
    jpeg_create_compress(&cinfo);
    jpeg_stdio_dest(&cinfo, outfile);

    cinfo.image_width = cx;
    cinfo.image_height = cy;
    cinfo.input_components = 3;
    cinfo.in_color_space = JCS_RGB;

    jpeg_set_defaults(&cinfo);
    jpeg_set_quality(&cinfo, iDefJpegQuality, TRUE);
    jpeg_start_compress(&cinfo, TRUE);

    byte *data = new byte[cx*3];
    while (cinfo.next_scanline < cinfo.image_height)
    {
        byte *bitmapLine = GetScanLine(cinfo.next_scanline);
        CopyBGRAtoRGB(data, bitmapLine, cx);
        jpeg_write_scanlines(&cinfo, (JSAMPARRAY)&data, 1);
    }
    delete data;

    jpeg_finish_compress(&cinfo);
    jpeg_destroy_compress(&cinfo);
    fclose(outfile);
    return true;
}

Основной параметр, влияющий на степень сжатия, устанавливается функцией jpeg_set_quality. В программе это параметр iDefJpegQuality. Может меняться от 1 (наивысшее сжатие) до 100 (наилучшее качество).

За дополнительной информацией обращайтесь к файлу libjpeg.doc в составе библиотеки (http://jpeg.org/).

PNG

Формат растра со сжатием без потерь. Внутри используется библиотека zlib. Формат аналогичен gif, но позволяет использовать не только палитровые изображения , но и полноцветные изображения (вплоть до 24 разрядов с альфа каналом). В добавок, не требует лицензионных отчислений за использование.

Загрузка и сохранение:

void png_user_error_fn(png_structp png_ptr, png_const_charp error_msg)
{
    String error = (char*)error_msg;
    throw error.GetString();
}

void png_user_warning_fn(png_structp png_ptr, png_const_charp warning_msg)
{
    String warning = (char*)warning_msg;
    MessageBox(NULL, L"PNG Warning!", warning.GetString(), MB_ICONWARNING);
}

#define PNG_BYTES_TO_CHECK 4
bool Bitmap::LoadFromPng(wchar *fileName)
{
    FILE *file = _wfopen(fileName, L"rb");
    if (!file)
        return false;

    png_structp png_ptr;

    char header[PNG_BYTES_TO_CHECK];
    fread(header, 1, PNG_BYTES_TO_CHECK, file);
    if (png_sig_cmp((png_bytep)header, (png_size_t)0, PNG_BYTES_TO_CHECK))
        return false;

    png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, 
        &png_user_error_fn, &png_user_warning_fn);
    png_infop info_ptr = png_create_info_struct(png_ptr);
    png_infop end_info_ptr = png_create_info_struct(png_ptr);
    png_init_io(png_ptr, file);
    png_set_sig_bytes(png_ptr, PNG_BYTES_TO_CHECK);
    
    // low-level read
    png_read_info(png_ptr, info_ptr);

    png_uint_32 width = png_get_image_width(png_ptr, info_ptr);
    png_uint_32 height = png_get_image_height(png_ptr, info_ptr);
    int bit_depth = png_get_bit_depth(png_ptr, info_ptr);
    int color_type = png_get_color_type(png_ptr, info_ptr);
    int interlace_type = png_get_interlace_type(png_ptr, info_ptr);
    if (bit_depth != 8 || interlace_type != PNG_INTERLACE_NONE || color_type != PNG_COLOR_TYPE_RGB)
        throw L"Error open PNG: Supported only none interlaced 8 bit RGB and RGBA images";

    CreateBitmap(width, height);

    png_byte *row = new png_byte[width*4];
    png_read_update_info(png_ptr, info_ptr);
    for (int j = 0; j < cy; j++)
    {
        png_read_row(png_ptr, row, NULL);
        CopyRGBtoBGRA(GetScanLine(j), row, width);
    }
    png_read_end(png_ptr, info_ptr);
    delete row;

    png_destroy_read_struct(&png_ptr, &info_ptr, png_infopp_NULL);
    fclose(file);
    return true;
}

bool Bitmap::SaveToPng(wchar *fileName)
{
    FILE *file = _wfopen(fileName, L"wb");
    if (!file)
        return false;

    png_structp png_ptr;
    png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, 
        &png_user_error_fn, &png_user_warning_fn);
    png_infop info_ptr = png_create_info_struct(png_ptr);

    png_init_io(png_ptr, file);
    
    //png_set_compression_level(png_ptr, Z_BEST_COMPRESSION);
    //png_set_compression_level(png_ptr, Z_BEST_SPEED);
    //png_set_compression_mem_level(png_ptr, 8);
    //png_set_compression_strategy(png_ptr, Z_DEFAULT_STRATEGY);
    //png_set_compression_window_bits(png_ptr, 15);
    //png_set_compression_method(png_ptr, 8);
    //png_set_compression_buffer_size(png_ptr, 8192);

    png_set_IHDR(png_ptr, info_ptr, cx, cy, 8, PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE,
        PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);

    // low-level write

    png_write_info(png_ptr, info_ptr);
    png_byte *row = new png_byte[cx*4];
    for (int j = 0; j < cy; j++)
    {
        CopyBGRAtoRGB(row, GetScanLine(j), cx);
        png_write_row(png_ptr, row);
    }
    delete row;
    png_write_end(png_ptr, info_ptr);

    png_destroy_write_struct(&png_ptr, &info_ptr);
    fclose(file);
    return true;
}

Закомментированы дополнительные параметры сжатия zlib, не существенно влияющие на результат (степень сжатия, объем доступной памяти и т.д.).

За дополнительной информацией обращайтесь к libpng.txt в составе библиотеке (http://libpng.org/).

Замечу, что для успешной компиляции на платформе Windows, нужно убрать некоторые файлы из библиотек. Чтобы избавит вас от чтения внушительных размеров мануалов, в исходниках оставлены только необходимые файлы (*.c и *.cpp), которые нужно скомпилировать и передать линковщику. Файлы из библиотеки jpeg переименованы из *.c в *.cpp, чтобы не использовать директиву компилятора extern "C" {…}.

Назад | Содержание | Вперед


©Павел Коколемин

Рейтинг@Mail.ru