项目介绍

tcbrindle/raytracer.hpp 是一个托管在 GitHub 上的开源项目,主要使用 C 编写,目前获得 724 个 Star、28 次 Fork。

仓库全名
tcbrindle/raytracer.hpp
Star 数
724
Fork 数
28
未关闭 Issue
2

README 摘要

C++17 constexpr Compile-time Ray Tracer # Introduction ## This is a C++17 ray tracer using constexpr function evaluation to produce the above image entirely at compile-time. Unlike other compile-time ray tracers which use template metaprogramming, this code also works just as well (in fact, thousands of times faster) at run time. It is based on Microsoft's TypeScript ray tracer example, translated almost…

完整内容见 GitHub 原始 README 或本页 。

常见问题

raytracer.hpp 用什么语言开发?

主要使用 C 编写,目前有 724 个 Star。

如何获取 raytracer.hpp 的源码?

可以直接克隆仓库:git clone https://github.com/tcbrindle/raytracer.hpp.git,默认分支为 master;安装与运行步骤见仓库 README。

raytracer.hpp 最近还在维护吗?

仓库最近一次更新为 2021年11月8日,当前有 2 个未关闭 Issue;Star 数为 724,可作为社区活跃度的参考。

关于

Simple compile-time raytracer using C++17

描述取自 GitHub 官方 API,中文内容由 GitRead 中文社区 编辑团队整理。

简要分析

热度分
797
价值分
444
活跃状态
活跃

语言占比

C
C++
CMake

README

C++17 constexpr Compile-time Ray Tracer

50% centre

Introduction

This is a C++17 ray tracer using constexpr function evaluation to produce the above image entirely at compile-time. Unlike other compile-time ray tracers which use template metaprogramming, this code also works just as well (in fact, thousands of times faster) at run time.

It is based on Microsoft’s TypeScript ray tracer example, translated almost line-for-line into C++.

Requirements

This code requires a recent compiler with good support for the upcoming C++17 standard. It works with Clang 4.0 and GCC 7.1 (though see the note below regarding the latter). For MSVC 2017, the code works at run-time (with certain workarounds for unsupported C++17 features, see the msvc branch), but compile-time evaluation currently hits an ICE.


WARNING

While compile-time image generation works with GCC 7, compiler memory usage is extreme – tens of gigabytes for even modest image sizes. If you want to try it out with GCC, stick to very small images or prepare for violent retribution from the OOM-killer.

(I believe this is because GCC memoizes the result of every single intermediate constexpr evaluation, rather than because of any sort of memory leak – in other words, this seems to be intended behaviour rather than a bug.)


With Clang, you’ll need to use the -fconstexpr-steps parameter to increase the maximum permitted number of constexpr evaluations in order to generate compile-time images. The included CMake project sets this to the maximum allowed value (2^31-1), which is sufficient to generate an 800x800 pixel image but not much larger.

Brief implementation overview

The rt::ray_tracer class takes a Scene and renders it onto a Canvas, where both Scene and Canvas are template parameters given to the ray_tracer::render() method. A Scene is made up of a number of “things”, a number of lights, and a camera. In the language of the Concepts TS, the expected interface might be something like this:

template <typename S>
concept bool Scene() {
    return requires(const S& scene) {
        { scene.get_things() } -> Range<rt::any_thing>;
        { scene.get_lights() } -> Range<rt::light>;
        { scene.get_camera() } -> rt::camera;
    };
}

A Canvas is simpler, and basically just requires a set_pixel(x, y, rt::color) method. The file compile_time.cpp contains a scene (and canvas) using std::arrays, while run_time.cpp is the same but uses std::vectors instead (to deliberately prevent compile-time evaluation).

A Thing is an object in the world. The header provides two types of Thing, namely a sphere and a plane. To avoid virtual functions, these are used polymorphically via an any_thing class, which is a wrapper around a std::variant<sphere, plane>. If you wish to define your own kind of Thing in a scene (for example a box), you’ll need add it to the any_thing variant, and implement three member functions: intersect(), which tests whether a given ray interects with the Thing, get_normal() which returns the normal vector to the object at the given point, and get_surface() which returns the surface the object is made from. (Unfortunately, the interface for intersect() is slightly complicated by the need to return a pointer to an any_thing along with the intersection information, but it’s fairly straightforward – take a look at the code for the sphere and plane classes.)

Files

raytracer.hpp is the bit which contains all the magic. As mentioned above, the implementation is that from Microsoft’s TypeScript examples set, translated almost exactly into C++.

stb_image_write.h is one of Sean Barratt’s excellent single-header C libraries. It’s used for writing out PNGs in compile_time.cpp and run_time.cpp.

stb_image_write.c is the implementation file for the above.

compile_time.cpp contains a static description of a scene, which is then rendered into a constexpr std::array. The image size can be changed using the IMAGE_WIDTH and IMAGE_HEIGHT compiler defines. For larger image sizes, this will take a long time to compile. The upper image size is limited by (a) the amount of RAM on your system with GCC, or (b) the constexpr step limit with Clang, or © your patience. Outputs a file called render-ct.png.

run_time.cpp is almost identical to the above, except that the scene data is contained in run-time data structure, and the image is rendered into a std::vector. Rather than compile-time parameters, you can change the image size by providing command-line arguments to the generated program, e.g. renderer-rt 1024 1024 for a 1024x1024 image. Outputs a file called render-rt.png.

CMakeLists.txt contains a CMake project which builds the two targets listed above, as well as taking care of setting things like compiler flags for you.

Performance

Generating the above 512x512 image at compile time took around 45 minutes with Clang 4.0 on my Macbook Pro. For comparison, the same code executing at run time takes less than half a second on the same machine, or somewhere in the region of 6000x faster.

Believe it or not, this is actually decent performance compared to compile-time raytracers which use template metaprogramming.

Licence

The original TypeScript version and this C++ translation are licenced under the Apache-2.0 licence.

评论

暂无评论