I’m currently trying to create a ray tracer in C++ but I’m having difficulty writing the .bmp produced at the end. I’m determined to do it manually, so I can learn more about image files and writing them etc. But I’m having some difficulty. I’m fairly new to C++ but have been using Python for a while.
I’m almost there now, I just have one strange problem. Everything is correct up to mid way trough the important colours (which I set to 0), where all sorts of random characters spring up and this continues for the next few bytes, a couple of bytes in to the pixel data. Before and after that everything is fine, but I just can’t explain it. My current code is in the edit:
Here is the hex:

the problem area is highlighted
#include <iostream>
#include <fstream>
#include <math.h>
using namespace std;
//-------------------------Object list
const int renderSize[2] = {254,254};
float sphere1Pos[3] = {0.0,0.0,0.0}; //first sphere at origin to make calculations easier
float sphere1Radius = 10.0;
float sphere1Colour= (255.0,0.0,0.0);
float light1Pos = (0.0,20.0,0.0); //Above sphere
float light1Intensity = 0.5;
// ------------------------
float dot(float* a,float* b); //Calculates the dot product
struct pixel {
unsigned char R;
unsigned char G;
unsigned char B;
};
//bmp--------------
struct bitmapMagicNumber {
unsigned char magicNumber[2];
};
struct bitmapFileHeader {
unsigned char fileSize;
short reserved1;
long reserved2;
short offset;
};
struct bitmapInformationHeader {
short headerSize;
short padding;
short width;
short height;
short planes;
short bitDepth;
short compression;
short imageSize;
short xPixelsPerMetre;
short yPixelsPerMetre;
short colours;
short importantColours;
};
void setBitmapMagicNumber(bitmapMagicNumber& magicNum){
magicNum.magicNumber[0] = 0x42;
magicNum.magicNumber[1] = 0x4D;
};
void setBitmapFileHeader(bitmapFileHeader& fileHeader,bitmapInformationHeader& informationHeader,pixel pixelArray) {
fileHeader.fileSize = 54 + sizeof(pixelArray);
fileHeader.reserved1 = 0;
fileHeader.reserved2 = 0;
fileHeader.offset = 54;
};
void setBitmapInformationHeader(bitmapInformationHeader& informationHeader){
informationHeader.headerSize = 40;
informationHeader.padding=0;
informationHeader.width = renderSize[0];
informationHeader.height = renderSize[1];
informationHeader.planes = 1;
informationHeader.bitDepth = 24;
informationHeader.compression = 0;
informationHeader.imageSize = 0;
informationHeader.xPixelsPerMetre = 0;
informationHeader.yPixelsPerMetre = 0 ;
informationHeader.colours = 0;
informationHeader.importantColours = 0;
};
void writeBitmap(bitmapMagicNumber& magicNum, bitmapFileHeader& fileHeader,bitmapInformationHeader& informationHeader,pixel pixelArray){
ofstream out("test.bmp",ios::out|ios::binary);
//file header
out.write((char*) &magicNum,2);
out.write((char*) &fileHeader.fileSize,sizeof(fileHeader.fileSize));
if (sizeof(fileHeader.fileSize)<3){
out.write((char*) &informationHeader.padding,1);
}
out.write((char*) &informationHeader.padding,1);
out.write((char*) &fileHeader.reserved1,2);
out.write((char*) &fileHeader.reserved2,2);
out.write((char*) &fileHeader.offset,sizeof(fileHeader.offset));
out.write((char*) &informationHeader.padding,1);
out.write((char*) &informationHeader.padding,1);
//information header
out.write((char*) &informationHeader.headerSize,sizeof(informationHeader.headerSize));
out.write((char*) &informationHeader.padding,1);
out.write((char*) &informationHeader.padding,1);
out.write((char*) &informationHeader.width,sizeof(informationHeader.width));
if (sizeof(informationHeader.width)<4){
out.write((char*) &informationHeader.padding,1);
}
if (sizeof(informationHeader.width)<3){
out.write((char*) &informationHeader.padding,1);
}
if (sizeof(informationHeader.width)<2){
out.write((char*) &informationHeader.padding,1);
}
out.write((char*) &informationHeader.height,sizeof(informationHeader.height));
if (sizeof(informationHeader.height)<4){
out.write((char*) &informationHeader.padding,1);
}
if (sizeof(informationHeader.height)<3){
out.write((char*) &informationHeader.padding,1);
}
if (sizeof(informationHeader.height)<2){
out.write((char*) &informationHeader.padding,1);
}
out.write((char*) &informationHeader.planes,sizeof(informationHeader.planes));
out.write((char*) &informationHeader.bitDepth,sizeof(informationHeader.bitDepth));
out.write((char*) &informationHeader.compression,4);
out.write((char*) &informationHeader.imageSize,4);
out.write((char*) &informationHeader.xPixelsPerMetre,4);
out.write((char*) &informationHeader.yPixelsPerMetre,4);
out.write((char*) &informationHeader.colours,4);
out.write((char*) &informationHeader.importantColours,4);
//pixel data
for (int y=0; y < renderSize[1]; y++) {
for (int x=0; x< renderSize[0]; x++) {
out.write((char*) &pixelArray[x][y],sizeof(pixel));
}
}
out.close();
}
// end bmp-----------
int main() {
pixel pixelArray[renderSize[0]][renderSize[1]];
for (int y=0; y < renderSize[1]; y++) {
for (int x=0; x< renderSize[0]; x++) {
float rayPos[3] = {x,y, -1000.0};
float rayDir[3] = {0.0,0.0,-1.0};
bool intersect;
//for each object in scene, see if intersects. (for now there is only one object to make things easier)
//-------sphere ray intersection....
float distance[3];
distance[0]= rayPos[0]-sphere1Pos[0];
distance[1]= rayPos[1]-sphere1Pos[1];
distance[2]= rayPos[2]-sphere1Pos[2];
float a = dot(rayDir, rayDir);
float b = 2 * dot(rayDir, distance);
float c = dot(distance, distance) - (sphere1Radius * sphere1Radius);
float disc = b * b - 4 * a * c;
if (disc < 0)
intersect=false;
else
intersect=true;
//--------------------
if (intersect==true){
pixelArray[x][y].R = 0;
pixelArray[x][y].G = 0;
pixelArray[x][y].B = 0;
}
else {
pixelArray[x][y].R = 0;
pixelArray[x][y].G = 0;
pixelArray[x][y].B = 0;
}
// trace to lights (as long as another object is not in the way)
}
}
//write .bmp
bitmapMagicNumber magicNum;
bitmapFileHeader fileHeader;
bitmapInformationHeader informationHeader;
setBitmapMagicNumber(magicNum);
setBitmapFileHeader(fileHeader,informationHeader, pixelArray[renderSize[0]][renderSize[1]]);
setBitmapInformationHeader(informationHeader);
writeBitmap(magicNum,fileHeader,informationHeader, pixelArray[renderSize[0]][renderSize[1]]);
}
//calculate dot product
float dot(float* a,float* b)
{
float dp = 0.0;
for (int i=0;i<3;i++)
dp += a[i] * b[i];
return dp;
}
While it may not be your only issue, you almost certainly have data alignment issues.
In your
bitmapFileHeader, for example, assuminglonghas four-byte alignment andshorthas two-byte alignment, there will be two bytes of unnamed padding betweenmagicNumberandfileSize(there are similar issues in most of the other data structures).As a solution, you can represent the header and other structures as an array of
char(which has no padding) and copy the relevant data into the correct locations in the array.Your compiler might provide a way to “pack” the data structures so that they are unaligned, which can also solve your problem, but doing so is wholly unportable.