So while looking for visual examples to showcase Weave, I went a outside of my 
comfort zone and tried my hand at graphics programming, in particular 
raytracing. 

# Faster

I was pleasantly surprised to see Nim consistently faster than C++ on 
single-threaded code:

[SmallPT](https://www.kevinbeason.com/smallpt/) (Raytracer in 99 lines of C++)

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  
  
I've also implemented the first [Ray Tracing in One Weekend 
book](https://github.com/RayTracing/raytracing.github.io)

And written an extensive writeup in the README of the repo.

I find Nim especially suited for graphics computation.

For this raytracing project, it was once again faster than C++ by about 10% 
(though I had to use an iterative `radiance` function instead of the 
traditional recursive because I had too many nested generic instantiation when 
i wanted to use `Option[T]` which were removed anyway because it made the code 
5x slower).

Nim| C++  
---|---  
38.577s| 42.303s  
  
# Safer

Regarding safety, I've used distinct types to make sure I don't mix `Point3`, 
`Vec3`, `UnitVector`, `Degrees`, `Radians`, `Color`, `Attenuations`.

This reminds me of my physics teachers always asking to check for your 
units/dimensions. Except that now it's the compiler that does that for you.

For example substracting 2 points gives a vector, adding a vector to a point 
gives a point and adding 2 points is a compile-time error:
    
    
    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".}
    
    
    Run

As is multiplying color, you should multiply with attenuation instead: 
    
    
    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
    
    
    Run

And this might have caught a "semantic bug" upstream (where they took the 
length of a point instead of a vector): 
[https://github.com/RayTracing/raytracing.github.io/issues/609](https://github.com/RayTracing/raytracing.github.io/issues/609)

The repo is here: 
[https://github.com/mratsim/trace-of-radiance](https://github.com/mratsim/trace-of-radiance)

The README is a writeup that can be turned into a blog post 

Reply via email to