基本光线追踪软件的设计与实现
这篇文章做的是计算机图形学中传统的光线追踪渲染,用的递归,最大反射次数为3,phong材料。

(上图 1024* 1024) 运行测速 = 0.09帧/秒
流程:
1.建立场景
2.相机发出射线
3.追踪该射线
4.根据光照模型和光照方向渲染
代码中的特点:
1.找到的这份代码,用的是结构体struct来定义的用于计算的数据,搜索了下C++里面的struct和class基本差别不大,最本质的一个是访问控制,struct默认访问控制是private,其实也就是说,在这份代码里面把每一个struct改为class也是同样可以运行的。
2.在这份代码里面大量的使用了inline内联函数,内联函数用法类似于#define ,省去了函数在寄存器中的反复调用,这样效率会更高的。
核心的结构体:
Vector , Color ,IGeometry , IMaterial , IntersectResult , PerspectiveCamera , Ray
同样有保存出渲染结果为找的类。
整个流程:
通过给一个test函数传值,test名称,函数的名字,运行次数这三个信息,如:test("rayTraceRecursive", rayTraceRecursive, 50);
test函数框架,先定义一个pixels32的图片变量,设置好大小, 在一个for循环里面,循环传入的运行次数这么多次,开始计时。
这个时候传入的函数名字,函数名字是const T_proc类型变量(T_proc的定义 typedef void(*T_proc)(const TPixels32Ref& dst);),用来直接作为函数的名字的变量,以运行相应的函数。
当执行for次数完成的时候,运行次数/经过的时间,得到渲染的fps值。
最后以 TFileOutputStream 获取图片输出的数据变量,TBmpFile::save的方式保存出照片。
画板测试部分:
void canvasTest(const TPixels32Ref& ctx) {
if (ctx.getIsEmpty())
return;
long w = ctx.width;
long h = ctx.height;
ctx.fillColor(Color32(0, 0, 0, 0));
Color32* pixels = ctx.pdata;
for (long y = 0; y < h; ++y){
for (long x = 0; x < w; ++x){
pixels[x].r = (UInt8)(x * 255 / w);
pixels[x].g = (UInt8)(y * 255 / h);
pixels[x].b = 0;
pixels[x].a = 255;
}
(UInt8*&)pixels += ctx.byte_width;
}
}
渲染深度测试部分:
void renderDepth(const TPixels32Ref& ctx) {
if (ctx.getIsEmpty())
return;
Union scene;
scene.push(new Sphere(Vector3(0, 10, -10), 10));
scene.push(new Plane(Vector3(0, 1, 0), 0));
PerspectiveCamera camera(Vector3(0, 10, 10), Vector3(0, 0, -1), Vector3(0, 1, 0), 90);
long maxDepth = 20;
long w = ctx.width;
long h = ctx.height;
ctx.fillColor(Color32(0, 0, 0, 0));
Color32* pixels = ctx.pdata;
scene.initialize();
camera.initialize();
float dx = 1.0f / w;
float dy = 1.0f / h;
float dD = 255.0f / maxDepth;
for (long y = 0; y < h; ++y){
float sy = 1 - dy*y;
for (long x = 0; x < w; ++x){
float sx = dx*x;
Ray3 ray(camera.generateRay(sx, sy));
IntersectResult result = scene.intersect(ray);
if (result.geometry) {
UInt8 depth = (UInt8)(255 - std::min(result.distance*dD, 255.0f));
pixels[x].r = depth;
pixels[x].g = depth;
pixels[x].b = depth;
pixels[x].a = 255;
}
}
(UInt8*&)pixels += ctx.byte_width;
}
}
渲染法线测试部分:
void renderNormal(const TPixels32Ref& ctx) {
if (ctx.getIsEmpty())
return;
Sphere scene(Vector3(0, 10, -10), 10);
PerspectiveCamera camera(Vector3(0, 10, 10), Vector3(0, 0, -1), Vector3(0, 1, 0), 90);
long maxDepth = 20;
long w = ctx.width;
long h = ctx.height;
ctx.fillColor(Color32(0, 0, 0, 0));
Color32* pixels = ctx.pdata;
scene.initialize();
camera.initialize();
float dx = 1.0f / w;
float dy = 1.0f / h;
float dD = 255.0f / maxDepth;
for (long y = 0; y < h; ++y){
float sy = 1 - dy*y;
for (long x = 0; x < w; ++x){
float sx = dx*x;
Ray3 ray(camera.generateRay(sx, sy));
IntersectResult result = scene.intersect(ray);
if (result.geometry) {
pixels[x].r = (UInt8)((result.normal.x + 1) * 128);
pixels[x].g = (UInt8)((result.normal.y + 1) * 128);
pixels[x].b = (UInt8)((result.normal.z + 1) * 128);
pixels[x].a = 255;
}
}
(UInt8*&)pixels += ctx.byte_width;
}
}
光线追踪部分:
void rayTrace(const TPixels32Ref& ctx) {
if (ctx.getIsEmpty())
return;
Plane* plane = new Plane(Vector3(0, 1, 0), 0);
Sphere* sphere1 = new Sphere(Vector3(-10, 10, -10), 10);
Sphere* sphere2 = new Sphere(Vector3(10, 10, -10), 10);
plane->material = new CheckerMaterial(0.1f);
sphere1->material = new PhongMaterial(Color::red(), Color::white(), 16);
sphere2->material = new PhongMaterial(Color::blue(), Color::white(), 16);
Union scene;
scene.push(plane);
scene.push(sphere1);
scene.push(sphere2);
PerspectiveCamera camera(Vector3(0, 5, 15), Vector3(0, 0, -1), Vector3(0, 1, 0), 90);
long w = ctx.width;
long h = ctx.height;
ctx.fillColor(Color32(0, 0, 0, 0));
Color32* pixels = ctx.pdata;
scene.initialize();
camera.initialize();
float dx = 1.0f / w;
float dy = 1.0f / h;
for (long y = 0; y < h; ++y){
float sy = 1 - dy*y;
for (long x = 0; x < w; ++x){
float sx = dx*x;
Ray3 ray(camera.generateRay(sx, sy));
IntersectResult result = scene.intersect(ray);
if (result.geometry) {
Color color = result.geometry->material->sample(ray, result.position, result.normal);
color.saturate();
pixels[x].r = (UInt8)(color.r * 255);
pixels[x].g = (UInt8)(color.g * 255);
pixels[x].b = (UInt8)(color.b * 255);
pixels[x].a = 255;
}
}
(UInt8*&)pixels += ctx.byte_width;
}
}
递归光照追踪部分:
Color rayTraceRecursive(IGeometry* scene, const Ray3& ray, long maxReflect) {
IntersectResult result = scene->intersect(ray);
if (result.geometry){
float reflectiveness = result.geometry->material->reflectiveness;
Color color = result.geometry->material->sample(ray, result.position, result.normal);
color = color.multiply(1 - reflectiveness);
if ((reflectiveness > 0) && (maxReflect > 0)) {
Vector3 r = result.normal.multiply(-2 * result.normal.dot(ray.direction)).add(ray.direction);
Ray3 ray = Ray3(result.position, r);
Color reflectedColor = rayTraceRecursive(scene, ray, maxReflect - 1);
color = color.add(reflectedColor.multiply(reflectiveness));
}
return color;
}
else
return Color::black();
}
void rayTraceRecursive(const TPixels32Ref& ctx) {
if (ctx.getIsEmpty())
return;
Plane* plane = new Plane(Vector3(0, 1, 0), 0);
Sphere* sphere1 = new Sphere(Vector3(-10, 10, -10), 10);
Sphere* sphere2 = new Sphere(Vector3(10, 10, -10), 10);
plane->material = new CheckerMaterial(0.1f, 0.5);
sphere1->material = new PhongMaterial(Color::red(), Color::white(), 16, 0.25);
sphere2->material = new PhongMaterial(Color::blue(), Color::white(), 16, 0.25);
Union scene;
scene.push(plane);
scene.push(sphere1);
scene.push(sphere2);
PerspectiveCamera camera(Vector3(0, 5, 15), Vector3(0, 0, -1), Vector3(0, 1, 0), 90);
long maxReflect = 3;
long w = ctx.width;
long h = ctx.height;
ctx.fillColor(Color32(0, 0, 0, 0));
Color32* pixels = ctx.pdata;
scene.initialize();
camera.initialize();
float dx = 1.0f / w;
float dy = 1.0f / h;
for (long y = 0; y < h; ++y){
float sy = 1 - dy*y;
for (long x = 0; x < w; ++x){
float sx = dx*x;
Ray3 ray(camera.generateRay(sx, sy));
Color color = rayTraceRecursive(&scene, ray, maxReflect);
color.saturate();
pixels[x].r = (UInt8)(color.r * 255);
pixels[x].g = (UInt8)(color.g * 255);
pixels[x].b = (UInt8)(color.b * 255);
pixels[x].a = 255;
}
(UInt8*&)pixels += ctx.byte_width;
}
}
主函数实现测试:
int main(){
std::cout << " 请输入回车键开始测试(可以把进程优先级设置为“实时”)> ";
waitInputChar();
std::cout << std::endl;
test("canvasTest", canvasTest, 2000);
test("renderDepth", renderDepth, 100);
test("renderNormal", renderNormal, 200);
test("rayTrace", rayTrace, 50);
test("rayTraceRecursive", rayTraceRecursive, 50);
std::cout << std::endl << " 测试完成. ";
waitInputChar();
return 0;
}

浙公网安备 33010602011771号