All Projects → mratsim → Trace Of Radiance

mratsim / Trace Of Radiance

An educational raytracer

Programming Languages

c
50402 projects - #5 most used programming language

Projects that are alternatives of or similar to Trace Of Radiance

Reactime
Chrome extension for improving and optimizing performance in React applications (Gatsby and Next.js compatible).
Stars: ✭ 1,219 (+1483.12%)
Mutual labels:  rendering, renderer
Gamemaniptutorial
A tutorial for manipulating the rendering of a game (generally to increase its quality) if you only have a binary available
Stars: ✭ 119 (+54.55%)
Mutual labels:  rendering, renderer
Flycube
Graphics API wrapper is written in C++ on top of Directx 12 and Vulkan. Provides main features including ray tracing.
Stars: ✭ 78 (+1.3%)
Mutual labels:  rendering, renderer
Diligentengine
A modern cross-platform low-level graphics library and rendering framework
Stars: ✭ 2,142 (+2681.82%)
Mutual labels:  rendering, renderer
DuEngine
An efficient interactive C++ renderer for ShaderToy-alike demos with 2D/3D/CubeMap/Video/Camera/LightField/Volume textures. (Partially used in my I3D 2018 papers)
Stars: ✭ 62 (-19.48%)
Mutual labels:  rendering, renderer
LuisaRender
High-Performance Multiple-Backend Renderer Based on LuisaCompute
Stars: ✭ 47 (-38.96%)
Mutual labels:  rendering, renderer
Graphicsrenderer
A drop-in UIGraphicsRenderer port -- CrossPlatform, Swift 4, Image & PDF
Stars: ✭ 85 (+10.39%)
Mutual labels:  rendering, renderer
Diligentsamples
Sample projects demonstrating the usage of Diligent Engine
Stars: ✭ 138 (+79.22%)
Mutual labels:  rendering, renderer
Photon-v2
A program that takes photographs of a virtual world.
Stars: ✭ 75 (-2.6%)
Mutual labels:  rendering, renderer
makma
Makma is a deferred Vulkan renderer written in C++.
Stars: ✭ 77 (+0%)
Mutual labels:  rendering, renderer
Pixi.js
The HTML5 Creation Engine: Create beautiful digital content with the fastest, most flexible 2D WebGL renderer.
Stars: ✭ 34,982 (+45331.17%)
Mutual labels:  rendering, renderer
Nebula Trifid
Nebula Trifid
Stars: ✭ 62 (-19.48%)
Mutual labels:  rendering
React Pixi
Write PIXI apps using React declarative style
Stars: ✭ 1,031 (+1238.96%)
Mutual labels:  renderer
Shaderworkshop
Interactive GLSL fragment shaders editor made with Qt
Stars: ✭ 43 (-44.16%)
Mutual labels:  rendering
Llgl
Low Level Graphics Library (LLGL) is a thin abstraction layer for the modern graphics APIs OpenGL, Direct3D, Vulkan, and Metal
Stars: ✭ 1,011 (+1212.99%)
Mutual labels:  renderer
Real Time Rendering 3rd Cn Summary Ebook
📘 电子书 -《Real-Time Rendering 3rd》提炼总结 | 全书共9万7千余字。你可以把它看做中文通俗版的《Real-Time Rendering 3rd》,也可以把它看做《Real-Time Rendering 3rd》的解读版与配套学习伴侣,或者《Real-Time Rendering 4th》的前置阅读材料。
Stars: ✭ 1,159 (+1405.19%)
Mutual labels:  rendering
Vulkan2drenderer
Easy to use 2D rendering engine using Vulkan API as backend.
Stars: ✭ 60 (-22.08%)
Mutual labels:  rendering
Shadergraph
Unity ShaderGraph project
Stars: ✭ 1,009 (+1210.39%)
Mutual labels:  rendering
Rgk
Photorealistic physically-based renderer
Stars: ✭ 41 (-46.75%)
Mutual labels:  renderer
Simplepbr
A Processing library providing physically based rendering and materials
Stars: ✭ 39 (-49.35%)
Mutual labels:  rendering

Trace of Radiance

Trace of Radiance is an implementation of Ray Tracing in One Weekend in the Nim programming language

The alledged goals are:

  • Learn more about Raytracing
  • Have fun
  • Serve as a testbed for own multithreading runtime, Weave (TODO)
  • Showcase Nim capabilities

In particular, I am convinced (an obviously biaised) that if you want to start a rendering project (raytracing or otherwise) from scratch, Nim is the best language to use, in particular if you are focus on:

  • speed
  • correctness
  • compilation times and compile-time compute
  • approachability & development agility

Changelog

The writeup below corresponds to v0.1.0. Since then the following updates (not in Shirley's book) have been made:

  • Parallelization via Weave, my own state-of-the-art multithreading runtime.
    • Rework of the RNG and sampler for parallel Monte-Carlo simulations
  • Simple animation (i.e. no Motion Blur ... yet, only vertical axis, ...)
    • Simple physics engine that can simulate gravity and bounce
    • Exports animation to PPM series
    • Exports animation to MP4 file format:
      • RGB to Y'CbCr 420 color conversion (often called YUV)
      • Poor Man's H264 encoder
      • MP4 Muxer
    • ⚠️ Warning: very slow since the engine has no acceleration structures (Bounding Volume Hierarchy, Kd-Trees, ...)
    • Credits to https://github.com/nwtgck/ray-tracing-iow-scala for the animation idea.

Table of Contents

Reproducing

git clone https://github.com/mratsim/trace-of-radiance
cd trace-of-radiance
git checkout v0.1.0
nim c -d:danger --outdir:build trace_of_radiance.nim
./build/trace_of_radiance > image.ppm

Speed

Rendering is an extremely time-consuming task. Nim is fast, very fast, even faster than C++ from time to time.

Raytracing in one Weekend

This applies to v0.1.0 of the repo, the repo has been reworked and in particular adds parallelism.

In the RayTracing in One Weekend first book

  • Nim 1.2 (GCC 10.1), flag -d:danger
  • C++ with GCC 10.1, flag -O3

On Intel Skylake-X i9-9980XE, overclocked at 4.1GHz all-core turbo. Compiled in x86-64 mode (SSE2 only)

Nim C++
38.577s 42.303s

384x216 image, 100 rays per pixel

The Nim sources for this benchmark can be retrived from the first release:

https://github.com/mratsim/trace-of-radiance/tree/v0.1.0

Smallpt

SmallPT is an even smaller raytracing project

Benchmark from Weave multithreading runtime raytracing demo

GCC 10 has a significant OpenMP regression

Bench Nim Clang C++ OpenMP GCC 10 C++ OpenMP GCC 8 C++ OpenMP
Single-threaded 4min43.369s 4m51.052s 4min50.934s 4m50.648s
Multithreaded 12.977s 14.428s 2min14.616s 12.244s
Nested-parallel 12.981s
Parallel speedup 21.83x 20.17x 2.16x 23.74x

Single-threaded Nim is 2.7% faster than Clang C++.
Multithreaded Nim via Weave is 11.1% faster Clang C++.

Correctness

Distinct types and modeling physics units

Nim is one of the few languages that can properly model physics units and enforce proper usage of those units at compile-time.

For example, a vector and an unit vector have the same representation but are distinct types. Unit vectors are auto-convertible to vectors when passed to a function:

type
  Vec3* = object
    x*, y*, z*: float64

  UnitVector* {.borrow:`.`.} = distinct Vec3
    # The `.` annotation ensures that field access is possible

converter toVec3*(uv: UnitVector): Vec3 {.inline.} =
  ## UnitVector are seamlessly convertible to Vec3 (but not the otherway around)
  Vec3(uv)

Point3 uses the same internal representation as Vec3 and can borrow (share) the implementation of common operators (i.e. no code-size impact)

type Point3* {.borrow: `.`.} = distinct Vec3
  # The `.` annotation ensures that field access is possible

func `*=`*(a: var Point3, scalar: float64) {.borrow.}
func `*`*(a: Point3, scalar: float64): Point3 {.borrow.}
func `*`*(scalar: float64, a: Point3): Point3 {.borrow.}

Also:

  • Substracting 2 points gives a vector,
  • Adding a vector to a point gives a point,
  • Adding 2 points is disallowed, with a nice custom error message instead of the compiler default errors.
func `-`*(a, b: Point3): Vec3 {.inline.}=
  ## Substracting points from one point to the other
  ## gives a vector
  result.x = a.x - b.x
  result.y = a.y - b.y
  result.z = a.z - b.z

func `+`*(p: Point3, v: Vec3): Point3 {.inline.}=
  ## Adding a vector to a point results in a point
  Point3(Vec3(p) + v)

func `-`*(p: Point3, v: Vec3): Point3 {.inline.}=
  ## Substracting a vector to a point results in a point
  Point3(Vec3(p) - v)

func `+`*(a, b: Point3): Point3 {.error: "Adding 2 Point3 doesn't make physical sense".}

I also have 2 types for colors and their attenuations (in percentage), so you cannot multiply colors:

type Color* {.borrow: `.`.} = distinct Vec3

func `*`*(a, b: Color): Color {.error: "Multiplying 2 Colors doesn't make physical sense".}

type Attenuation* {.borrow: `.`.} = distinct Color

func `*=`*(a: var Attenuation, b: Attenuation) {.inline.} =
  # Multiply a color by a per-channel attenuation factor
  a.x *= b.x
  a.y *= b.y
  a.z *= b.z

func `*`*(a, b: Attenuation): Attenuation {.inline.} =
  # Multiply a color by a per-channel attenuation factor
  result.x = a.x * b.x
  result.y = a.y * b.y
  result.z = a.z * b.z

func `*=`*(a: var Color, b: Attenuation) {.inline.} =
  # Multiply a color by a per-channel attenuation factor
  a.x *= b.x
  a.y *= b.y
  a.z *= b.z

Last but not least, ensure you don't mix and match degrees and radians, with auto-conversion of radians to float (and degree would stay at a higher level)

type
  Degrees* = distinct float64
  Radians* = distinct float64

template degToRad*(deg: Degrees): Radians =
  Radians degToRad float64 deg

template radToDeg*(rad: Radians): Degrees =
  Degrees radToDeg float64 rad

# For now we don't create our full safe unit library
# with proper cos/sin/tan radians enforcing
# and auto-convert to float
converter toF64*(rad: Radians): float64 {.inline.} =
  float64 rad

Tracking mutability

When using value types (stack objects, seq and strings), Nim requires an explicit var when passing parameters or assigning to a variable for a parameter to be mutable.

This makes it clear what can or can't move under your feet.

Furthermore, C and C++ developers might be interested to know that when passing a large object (over 12 bytes on 32-bit and 24 bytes on 64-bit platforms), Nim does the Right Thing™ and will pass by reference by default (you can override that on a per-type basis to always pass-by-copy or always by reference). This means that function signatures are motivated by mutability concerns and uncluttered from performance (and performance is great as proven by the benchmarks)

Tracking side-effects

Nim functions can be declared with proc or func.

A func enforces the absence of side-effect or the code will not compile. For example:

  • (non-deterministic) random functions
  • accessing a global variable
  • multithreading
  • IO

If you have a Heisenbug, it's likely not in a side-effect free function (unless you corrupt memory)

This is particularly suited to physics and raytracing computational kernels as physics equations are side-effect free.

Compilation times & Meta-programming

Despite all its features, Nim compiles extremely fast with both C and C++ targets. In particular template metaprogramming has a low low low compilation-time cost.

Nim is the language with the widest support for compile-time metaprogramming, including dependently-typed languages.

This is supported by a fast VM that gives Nim the speed of Python at compile-time and is used for:

  • Writing domain specific languages
    • State machine generators
    • Seamless multidimensional arrays uses, including Einstein summation
    • Shader generators
    • SIMD kernel generators
    • ...
  • Sompile-time precomputations
  • Buffers, vectors, matrices parametrized by integer generics

C and C++ interop

Nim can compile to both C and C++ and seamlessly call libraries written in either languages. It can even use CUDA with a bit of configuration to call nvcc

For example, bindings to SFML, from the Nim website features

Approachability, productivity & development agility

While Nim offers many advanced features, it does not force you to swim or drown on first approach.
Actually many people have reported that Nim felt like a compiled scripting language.

Caveats

Not all is ponies and rainbows, Nim's main issue is that it is a very young language with a small ecosystem of support libraries. That said, you can reuse the C and C++ ecosystem (and Javascript as well since Nim can also compile to it).

Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].