从零实现光线追踪器(0)- 将数据保存为图片
2018-10-22 本文已影响0人
不吃折耳根
前言
本人一直对图形学感兴趣。之前一直在scratchapixel.com学习图形学的知识。这个网站对相关的数学基础以及图形学入门概念讲解十分详细,推荐有一定英语能力的人阅读。但是读了那么多理论知识,总想着去自己动手实现一把。可是万事开头难啊,到底需要编写哪些类,设计哪些算法,让人头大。这时我去油管找到了一个2013年的教程使用C++实现了一个简易的光线追踪器,效果还挺好。在这里写下自己的学习过程供大家参考和交流。
这是该作者的教程地址共9节Raytracer from Scratch in C++,有兴趣的同学可以科学上网浏览一下。该作者的编码水平并不高,其中有很多可以优化的地方,但是他所展示的实现光线追踪器的一套流程还是十分值得学习。
下面我们正式开始。
1.保存bmp图片
第一步我们需要编写一个函数saveBmp()
将图像数据保存为bmp图片。此处也可以去查阅相关的博客来编写该函数。而一个简单的bmp的格式为bmpFileHeader
+bmpInfoHeader
+data
。创建一个头文件命名为func.h
,在其中进行如下编写。
首先我们定义字长。此处第一行#pragma pack(2)
是必不可少的。否则无法字节对齐,bmp格式相关参数会设置错误。
#pragma pack(2)
typedef unsigned char BYTE;
typedef unsigned short WORD;
typedef unsigned long DWORD;
typedef long LONG;
接下来我们定义一个bmpFileHeader
和bmpInfoHeader
结构体。
struct BmpFileHeader
{
WORD bfType;
DWORD bfSize;
WORD bfReserved1;
WORD bfReserved2;
DWORD bfOffBits;
};
struct BmpInfoHeader
{
DWORD biSize;
LONG biWidth;
LONG biHeight;
WORD biPlanes;
WORD biBitCount;
DWORD biCompression;
DWORD biSizeImage;
LONG biXPelsPerMeter;
LONG biYPelsPerMeter;
DWORD biClrUsed;
DWORD biClrImportant;
};
此处各项变量的意义可以去查阅讲解bitmap位图格式的相关博客
接下来我们定义一个结构体用于保存RGB数据
struct RGBType
{
unsigned char r;
unsigned char g;
unsigned char b;
void set(unsigned char r1, unsigned char g1, unsigned char b1)
{
r = r1;
g = g1;
b = b1;
}
};
这样我们就可以来编写saveBmp()
函数了。
void saveBmp(const char *fileName, int w, int h, RGBType *data)
{
int size = 4 * w * h;
//设置bmpfileheader的各种数值
BmpFileHeader fileHeader;
fileHeader.bfType = 0x4D42;//0x4d42即字母 'B''M'
fileHeader.bfReserved1 = 0;//保留位1
fileHeader.bfReserved2 = 0;//保留位2
fileHeader.bfSize = 54 + size;//文件总长度 文件头(54字节) + 图像数据(size)
fileHeader.bfOffBits = 54;//文件头长度
//设置infoheader的值
BmpInfoHeader infoHeader = {0};
infoHeader.biSize = 40;//infoheader的长度
infoHeader.biHeight = -h;//高度
infoHeader.biWidth = w;//宽度
infoHeader.biPlanes = 1;
infoHeader.biBitCount = 24;//颜色位数。一般为24
infoHeader.biSizeImage = 0;//默认为0
infoHeader.biCompression = 0;//压缩率 ,默认为0
//将传入的RGB数值写入文件中
FILE *output = fopen(fileName, "wb");
if (output)
{
fwrite(&fileHeader, 14, 1, output);
fwrite(&infoHeader, 40, 1, output);
for (int i = 0; i < w * h; ++i)
{
RGBType rgb = data[i];
unsigned char color[3] = {rgb.r, rgb.g, rgb.b};
fwrite(color, 3, 1, output);
}
fclose(output);
}
}
接下来我们进行简单的测试。
#include "func.h"
#include <iostream>
#include <cmath>
#include <fstream>
using namespace std;
int main(int argc, char *argv[])
{
cout << "rendering";
int width = 640;
int height = 480;
int n = width * height;
RGBType *pixels = new RGBType[n];
for (int x = 0; x < width; ++x)
{
for (int y = 0; y < height; ++y)
{
int i = y * width + x;
//设置一个矩形
if((x>100 && x<300)&&(y>200 && y<400)){
pixels[i].set(200,100,200);
}else{
pixels[i].set(255,255,255);
}
}
}
//将RGB数据保存到bmp图片中
saveBmp("pic.bmp",width,height,pixels);
}
编译运行一下。打开pic.bmp
如下。可以使用windows自带图片查看器或者windows上的画图
直接查看。
那么这就是第一步。已经初见成果(可以看到一个矩形了哈哈)。那么离后面的成功就不远了~~