Reputation: 2369
I'm trying to make a basic ray tracer in C, and decided to use a .tga
file as the output render file. But for whatever reason, the file that gets created shows as being solid white in every tga file viewer I can find. The start of the hexdump for the file I write to is here, and it seems pretty clear that large swaths of the image should be black.
The loop that writes to the file is:
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);
}
and I'm seeing disp black
get printed to the console, and count
gets incremented to over 300,000, so there's clearly nothing wrong with writing the zeros to the file. I tried my best to follow the header format that I found here, is there a problem with that?
I also noticed that the image is a solid color of whatever I put in the if
block, as though if
statement is always taken.
EDIT: Code that writes the header
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);
}
Upvotes: 1
Views: 682
Reputation: 20018
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)
Upvotes: 3