1

我正在尝试用 C 制作一个基本的光线追踪器,并决定使用一个.tga文件作为输出渲染文件。但无论出于何种原因,创建的文件在我能找到的每个 tga 文件查看器中都显示为纯白色。我写入的文件的 hexdump 的开头是 here,很明显图像的大片区域应该是黑色的。

写入文件的循环是:

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

1 回答 1

3

The header is wrong. The code you posted is writing RGB data, but matching the hex dump with the spec in the page you linked, we see:

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...

your header says the file is colour-mapped. Since the header doesn't match the data, it is misinterpreted and not displaying what you expect to see.

You almost certainly want a Type 2 'Unmapped RGB' to match the data you are writing, so the 2nd byte should be 0x00 (you don't supply a colour map) and the 3rd byte should be 0x02 (indicating RGB). The colour map spec should then be ignored, so that can be zero-filled. The size etc looks fine.

I don't recommend simply writing a fixed header as an array of bytes like this. It is error prone and inflexible. It is very easy to define a struct which defines the header, and then you get the advantage of labelled fields, proper types, and symbolic constants if you define an enum for things like the image type code, and so on.

The linked article already provides this, you just need to make sure it is not padded by the compiler by means of a pragma to ensure the layout is exactly as described:

#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)
于 2020-05-07T23:39:32.933 回答