Что не так с моим вручную созданным файлом .tga?

Я пытаюсь создать базовый трассировщик лучей на C и решил использовать файл .tga в качестве выходного файла рендеринга. Но по какой-то причине созданный файл отображается сплошным белым цветом в каждом средстве просмотра файлов tga, которое я могу найти. Начало шестнадцатеричного дампа для файла, который я записываю в здесь, и кажется совершенно очевидным, что большие участки изображение должно быть черным.

Цикл записи в файл:

void raycast(uint16_t width, uint16_t height, FILE* file, Sphere* sphere) {
    Vec3* camera = makeVec3(0, 0, 0);
    Vec3* light = makeVec3(1, -1, -1);
    int count = 0;
    double zMax = 1 / cos(toRads(vFOV) / 2.0);
    double yMax = 1 / cos(toRads(hFOV) / 2.0);
    for (uint16_t i = 0; i < height; i++) {
        for (uint16_t j = 0; j < width; j++) {
            int16_t zOffset = (height / 2 - i);
            double zprop = zOffset / (height / 2.0);
            double zCoord = zMax * zprop;
            int16_t yOffset = (width / 2 - j);
            double yprop = yOffset / (width / 2.0);
            double yCoord = yMax * yprop;
            Vec3* coord = makeVec3(1, yCoord, zCoord);
            normalize(coord);
            Ray* ray = makeRay(camera, coord);
            Vec3* intersect = sphereIntersect(sphere, ray);
            if (intersect == NULL) {
                fputc(255, file);
                fputc(255, file);
                fputc(255, file);
            } else {
                printf("disp black\n");
                fputc(0x00, file);
                fputc(0x00, file);
                fputc(0x00, file);
                count++;
            }
        }
    }
    printf("%d\n", count);
}

и я вижу, что disp black выводится на консоль, а count увеличивается до более чем 300 000, так что явно нет ничего плохого в записи нулей в файл. Я изо всех сил старался следовать формату заголовка, который нашел здесь, есть ли проблема с что?

Я также заметил, что изображение представляет собой сплошной цвет всего, что я поместил в блок if, как будто оператор if всегда берется.

РЕДАКТИРОВАТЬ: код, который записывает заголовок

static void writeHeader(FILE* file) {
    static uint16_t width = 800;
    static uint16_t height = 600;
    // 800x600 image, 24 bits per pixel
    unsigned char header[18] = {
        0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x18, 0x00, 0x00, 0x00, 0x00,
        (unsigned char)(width & 0x00FF), (unsigned char)((width & 0xFF00) / 256),
        (unsigned char)(height & 0x00FF), (unsigned char)((height & 0xFF00) / 256),
        0x08, 0x00
    };
    fwrite(header, sizeof(char), 18, file);
}

person Calvin Godfrey    schedule 07.05.2020    source источник
comment
Проблема скорее всего в шапке, из-за чего вы видите не то, что ожидаете. Если заголовок неправильный, данные будут неправильно интерпретированы. Выложите код для написания шапки.   -  person gavinb    schedule 08.05.2020
comment
@gavinb обновлено! Это в основном жестко закодированный массив, который записывается.   -  person Calvin Godfrey    schedule 08.05.2020


Ответы (1)


Заголовок неправильный. Код, который вы разместили, записывает данные RGB, но сопоставляя шестнадцатеричный дамп со спецификацией на странице, на которую вы ссылаетесь, мы видим:

Identification Field | 00 = No ID field
Colour Map Type      | 01 = CMap Included
Image Type Code      | 01 = Type 1: Colour Mapped
Colour Map Spec      | 00 00 00 01 18 ...
Image Spec
X Origin             | 00 00 = 0
Y Origin             | 00 00 = 0
Width                | 20 03 = 800px
Height               | 58 02 = 600px
Bits/Pixel           | 08    = 8bpp
Descriptor           | 00...

ваш заголовок говорит, что файл имеет цветовую карту. Поскольку заголовок не соответствует данным, он неправильно интерпретируется и не отображает то, что вы ожидаете увидеть.

Вы почти наверняка хотите, чтобы тип 2 «Unmapped RGB» соответствовал данным, которые вы записываете, поэтому 2-й байт должен быть 0x00 (вы не предоставляете цветовую карту), а 3-й байт должен быть 0x02 (указывая RGB). Затем спецификацию карты цветов следует игнорировать, чтобы ее можно было заполнить нулями. Размер и т. д. выглядит нормально.

Я не рекомендую просто писать фиксированный заголовок в виде массива байтов, подобных этому. Он подвержен ошибкам и негибок. Очень легко определить struct, который определяет заголовок, а затем вы получите преимущество помеченных полей, правильных типов и символических констант, если вы определите enum для таких вещей, как код типа изображения и так далее.

Связанная статья уже предоставляет это, вам просто нужно убедиться, что компилятор не дополняет ее с помощью прагмы, чтобы убедиться, что макет точно такой, как описано:

#pragma pack(push, 1)
typedef struct {
   char  idlength;
   char  colourmaptype;
   char  datatypecode;
   short int colourmaporigin;
   short int colourmaplength;
   char  colourmapdepth;
   short int x_origin;
   short int y_origin;
   short width;
   short height;
   char  bitsperpixel;
   char  imagedescriptor;
} HEADER;
#pragma pack(pop)
person gavinb    schedule 07.05.2020
comment
Спасибо за помощь! Я не полностью понял все поля в заголовке и хотел, чтобы что-то работало, прежде чем создавать структуру. Я внес ваши изменения, и когда я открываю его в tgaviewer в Windows, он больше не сплошной белый. Но когда я пытаюсь открыть его с помощью eog в Linux, он говорит, что тип изображения TGA не поддерживается. Есть идеи по этому поводу? - person Calvin Godfrey; 08.05.2020
comment
Итак, когда вы просматриваете изображение с помощью tgaviewer, оно выглядит так, как вы ожидаете? Что касается просмотра в журнале, зависит от фактических байтов заголовка. Может быть что угодно. - person gavinb; 08.05.2020