Wednesday, January 27, 2016

Ray Tracing in One Weekend

Ray tracing was invented by Turner Whitted around 1980.   His classic paper is really amazing; it had bounding volume hierarchies, adaptive sampling, and it's future work suggested randomized reflection.   Since then it has branched into a ton of variations and is used in most movies for the indirect "bounce" lighting.

I've was assigned a 2D ray tracer in a physics class in 1984 and I was hooked.   Since then I have written a bunch of ray tracers in Pascal, Fortran, Scheme, C, C++, Java, and most recently Swift (which is awesome for writing ray tracers).   I've also taught classes with ray tracing about a dozen times, most recently at Westminster College in 2015.   My approach over the years has evolved to do what I think are the most fun parts of ray tracing that are still in the direction of writing a production-quality ray tracer.

Ray Tracing in One Weekend is a kindle book that goes through all of the details to generate a rudimentary ray tracer.   It's $2.99 on amazon.   It uses C plus classes plus operator overloading.   I have heard this referred to as "C plus" which I now call it.    Most production renderers are written in C++ so I opted for that as the driving language.    I put the most primal set of these assignments so that it's very doable in a weekend, the fundamental unit of coding self-improvement :)   Here is the book:




The resulting program generates this image:



Here's my MacOS code for the book.   Should be semi-portable but drand48() will need to be written on some systems and the fileio may be too.    I also have a little post on the tools I used to write the book for those interested in doing a little book of their own.   Please let me know if you do!


Links for further reading and exploration related to the book:


Chapter 0: Overview

Here is a list of basic graphics texts that cover the vector background the book assumes:

Fundamentals of Computer Graphics, Fourth Edition

Computer Graphics: Principles and Practice (3rd Edition)

The Graphics Codex

Real-Time Rendering, Third Edition



Chapter 1: Output an image

For output of most LDR and HDR images I love stb_image.

For more info on HDR images and tone mapping the I like the book  High Dynamic Range Imaging, Second Edition: Acquisition, Display, and Image-Based Lighting 


Chapter 2: The vec3 class

I advocate using the same class for points, displacements, colors, etc.   Some people like more structure and type checking (so for example multiplying two locations would be rejected by the compiler).   An example article where points and vectors are different is here.   Jim Arvo and Brian Smits experimented with not only distinguishing points and vectors, but using the compiler to do dimensional analysis (so velocity, length, and time would be different types for example).   They found this to be too cumbersome in 1990s C++ but I'd love to hear about anybody's experience.   Researchers at Dartmouth have taken a really serious effort at this and their code and paper are available at github.


Chapter 3:  Rays, a simple camera, and background

The first thing you might add to your background function is an environment map.   Paul Debevec has a terrific history of environment mapping.   The easiest mapping for this uses a single image for the entire sphere of directions.   Paul also provides some good images to use for your environment map.


Chapter 4:  Adding a sphere

There are a bunch of other object types you can add.   Triangles are usually first and I am a fan of barycentric methods.    After triangles, many people quit adding primitives because graphics has such a big infrastructure for triangles.    Ellipses are an easy thing to add but instancing is usually a more "ray tracey" approach (let the software do the heavy lifting).   Composite objects via CSG are surprisingly straightforward.


Chapter 5:  Surface normals and  multiple objects.

If you want your code to be more efficient for large numbers of objects, use a BVH-- they are as good as any other in efficiency and are the most robust and easiest to implement.


Chapter 6:  Antialiasing

Box filtering as done in the book suffices for most situations.   However, Guassian-like filters can have advantages and are pretty easy.   You can either uniformly sample the whole screen and weight the samples, or use non-uniform samples.   All approaches work.


Chapter 7:   Diffuse Materials

"Ideal" diffuse materials, also called "Lambertian" are used 99% of the time in graphics.     The wikipedia article on this approximation is good.   Real diffuse surfaces do not behave exactly as Lambertian (for example they get specular at grazing angle) but especially with interreflection in the mix the appearance differences are minor.   So this is probably not where you should push your renderer until many many other features are addressed.

Chapter 8:  Metal

The first improvement you might make is to have the color of the metal go to white at grazing angle.   The Schlick approximation (used in Chapter 9 for glass where grazing behavior matters more) works for that.    Full-bore Fresnel equations will describe color variation with angle, but in my experience getting normal incident color is good enough.


Chapter 9:  Dielectrics

The first thing you might add is filtering of light within the dielectric (like the subtle "greenness" of much glass).   This is a classic exponential decay and is covered well in the Beer's Law section of this nice page.


Chapter 10:  Positionable camera
Camera parameter setting is just plain ugly.  The system used in the book is relatively common and is in my opinion the prettiest.   I avoid the matrix formulation wherever possible because I never understand my code when I am done.   But it is very elegant and works.

Chapter 11:  Defocus blur

If you ever need to match a real camera, there is a nice survey of models used in graphics.   And here is a very good presentation on real lenses written for a technical audience.


Chapter 12:  Where next?

You should have the core of a very serious ray tracer now.    I would now take it in one of three directions.   They are not mutually exclusive but explicitly deciding your goals will simplify architectural decisions.
  1. Make it physically accurate.   This will imply using spectra instead of RGB (I like just using a big array of wavelengths) and get something where you know the reflectances.   Popular is to get a X-Rite MSCCC ColorChecker Classic whose data is available online.
  2. Make it good for generating animations.    Lots of movies use a ray traced system, and Disney, Pixar, and the Solid Angle teams have both disclosed a remarkable amount about their code.   Work on features and then efficiency.   I think you will be amazed how soon you can produce amazing images.
  3. Make it fast.   Here you can roll your own, or start using a commercial API.   To see exactly where that community is now, go to the 2016 HPG conference.   Or backtrack in their previous papers.   They are a very open community and the papers go into much detail relative to many sub-fields in computer graphics.
Please send me your questions, comments, and pictures.   Have fun with ray tracing!





 

77 comments:

  1. You should consider publishing in iBooks too because it's easier to push updates. I imagine the book could be perfect but most computer books seem to have a few mistakes. It's just the nature of the topic.

    ReplyDelete
    Replies
    1. Thanks-- I already pushed an update so no not perfect! I'll look into iBooks. I think currently I have enabled some amazon features related to loaning and kindle unlimited free reading. I'll do some due diligence on the ecosystem once I finish part 2. I've had requests for pdf as well and there are a bunch of options there I don't yet understand...

      Delete
  2. SafeGI is a good example of the usage of dimensional analysis in a rendering system.
    https://github.com/ouj/safegi

    ReplyDelete
    Replies
    1. Really interesting work. Thanks for the link. The github has the EGSR paper as well. I'll be very interested in the authors' thoughts on this in a couple of years as it is clearly a double-edged sword. I added your link above. Thanks!

      Delete
  3. In the chapter 8, where you add the Metal material, can you provide the changes to the sphere class? The modifications to main() call sphere() with an additional 'new lambertian()' parameter, but I can't figure out what needs to be done in the sphere class to make it work.

    Thanks!

    ReplyDelete
    Replies
    1. I'll take a close look at this part of the discussion. In the meantime, here is the final sphere class.

      #ifndef SPHEREH
      #define SPHEREH

      #include "hitable.h"

      class sphere: public hitable {
      public:
      sphere() {}
      sphere(vec3 cen, float r, material *m) : center(cen), radius(r), mat_ptr(m) {};
      virtual bool hit(const ray& r, float tmin, float tmax, hit_record& rec) const;
      vec3 center;
      float radius;
      material *mat_ptr;
      };

      bool sphere::hit(const ray& r, float t_min, float t_max, hit_record& rec) const {
      vec3 oc = r.origin() - center;
      float a = dot(r.direction(), r.direction());
      float b = dot(oc, r.direction());
      float c = dot(oc, oc) - radius*radius;
      float discriminant = b*b - a*c;
      if (discriminant > 0) {
      float temp = (-b - sqrt(b*b-a*c))/a;
      if (temp < t_max && temp > t_min) {
      rec.t = temp;
      rec.p = r.point_at_parameter(rec.t);
      rec.normal = (rec.p - center) / radius;
      rec.mat_ptr = mat_ptr;
      return true;
      }
      temp = (-b + sqrt(b*b-a*c))/a;
      if (temp < t_max && temp > t_min) {
      rec.t = temp;
      rec.p = r.point_at_parameter(rec.t);
      rec.normal = (rec.p - center) / radius;
      rec.mat_ptr = mat_ptr;
      return true;
      }
      }
      return false;
      }


      #endif

      Delete
    2. This discriminant does not work for me. When you removed the 2.0 (the 4 in the numerator and the 2 of the denominator) factors, the results are not valid any more. Am I missing something? from a mathematical point of view, I don't think that is correct, as the 4 is inside the square root affecting only one of the terms. Also, you already calculated the discriminant, you don't need to write it again inside the sqrt.

      Delete
  4. There also appears to be a circular dependency between the mat_ptr field in the hit_record struct, and the material class. It's not clear how you resolved this.

    Thanks

    ReplyDelete
    Replies
    1. I think the hit_record only needs to know that mat_ptr is a pointer but doesn't need to know the size of what it points to, so "class material;" at the beginning of the file where hit_record is defined takes care of it. (I say "I think" because I never trust my own compiler's acceptance of something that it works under the actual language spec.)

      Delete
    2. I looked it over and this is not mentioned in the book at all! I have fixed this and sent a new copy to amazon. It will probably update sometime today. Thanks for telling me about this!

      Delete
  5. That worked, thanks! I can reproduce the images in chapter 8 now. On to chapter 9!

    ReplyDelete
    Replies
    1. Awesome. Thanks for your help-- a book has to be debugged too and the readers are the compiler :)

      Delete
  6. Some more comments. Not sure how to specify location, as my Kindle reader just uses 'Location' -- hopefully that is universal:

    Location 283:
    list[2] sphere color doesn't match color in the picture on next page (blue vs. red)

    Location 314:
    The formula for the 'discrimant' variable gives bad results for me. But when I change it to:
    float discrimant = 2.0 - ni_over_nt*ni_over_nt*(1-dt*dt);

    my results closer to yours.

    Location 316:
    attenuation = vec3(1.0, 1.0, 0.0);
    should be:
    attenuation = vec3(1.0, 1.0, 1.0);

    ReplyDelete
    Replies
    1. Thanks! It looks like they are either close to universal or close enough.

      On 283 I think list[2] is the big sphere and (0.8, 0.6, 0.2) is some yellowish color. The blue sky will shift toward blue so the greenish pale "ground sphere should be ok?

      Delete
    2. I think that the formula in the book is ok. Here is what rtn says (rtn was a very active list among ray tracing nerds in the dark ages) http://www.realtimerendering.com/resources/RTNews/html/rtnv10n1.html#art3

      Delete
  7. Hey Peter,

    I'm not sure if it's me or the code, but I couldn't get the refraction in the dielectric working right until I switched to passing unit vectors to the refract method. After that it matches the renders in the book perfectly.

    ReplyDelete
    Replies
    1. Which vector? I do this in the first line of refract: vec3 uv = unit_vector(v);

      Delete
    2. Yes, but the normal also seems to need to be 'normalised':


      bool refract(const vec3& v, const vec3& n, float ni_over_nt, vec3& refracted) {
      vec3 uv = v.unit_vector();
      vec3 un = n.unit_vector();

      float dt = dot(uv, un);
      float discriminant = 2.0 - ni_over_nt*ni_over_nt*(1.0 - dt*dt);
      if (discriminant > 0) {
      refracted = ni_over_nt*(uv - un*dt) - un*sqrt(discriminant);
      return true;
      } else {
      return false;
      }
      }

      Delete
    3. I also had problems, but the fix was that the line which reads:

      refracted = ni_over_nt*(v - n*dt) - n*sqrt(discriminant);

      should read:

      refracted = ni_over_nt*(uv - n*dt) - n*sqrt(discriminant);

      Note the use of "uv" instead of the non-normalized "v". After making that one-character change I had the same results as in the book. I didn't need to normalize the normal.

      Delete
    4. I had the same problem, thanks for posting your fix!

      Delete
  8. Using unit vectors for the refract method also worked for me -- thanks for the tip.

    ReplyDelete
  9. Not having too much time, it took me more than one weekend to complete the raytracer :) but I'm quite happy with it. I adapted the C++ code to JavaScript and it now runs inside a browser. It is a bit slow, but I think it serves its educational purpose very well.

    I am curious about the camera parameters you used to generate the final image from chapter 12 (lookfrom, lookat,... , aperture, focus_dist). After some experimentation my image was similar to yours, but not quite so. In particular the perspective looks a bit strange, the small balls to the left were distorted like ovals.

    ReplyDelete
    Replies
    1. Cool! Could you post a link?-- ray tracing in a browser is always fun.

      The camera I use could be wrong. Let me set up some unit tests.

      Also, spheres projected to an image from ovals. If you get close enough to your image so your eye has the same field of view as your virtual eye does, you will see those ovals in perspective so on your retina you will get approximately a disk. In my first ray tracer I chased this "bug" for weeks...

      Delete
    2. Yes, I will post a link, but I need to clean up the code first and this will require another weekend.

      Delete
    3. Here's a link to my JavaScript version:

      https://www.googledrive.com/host/0BwNzpRS7-ABHeDFuRXJ6NmV0TFE/raytracer-ch12.html

      This code will run in your browser, so beware that it is slow! I reduced the number of small spheres and the number of samples for antialiasing, but even so, it takes a few minutes to render the image on a relatively fast notebook.

      The same directory also has versions that generate the images in each chapter. For example, for chapter 8:

      https://www.googledrive.com/host/0BwNzpRS7-ABHeDFuRXJ6NmV0TFE/raytracer-ch08.html

      Chapter 9 has two images, "ch09-1" and "ch09-2".

      I think I can make this a bit faster by exploring some features of JS 6 (like typed arrays). But for now I'm just waiting for the scond book in the series :)

      Delete
    4. Would it be possible to elaborate more on the issue where spheres appear as ovals? I have also been chasing this issue for a few weeks!

      Delete
    5. dargouder, there's a good explanation of this in the book "Real-Time Rendering" 3rd Ed (M öller et. al.) p 449. They explain that it's not an error and relates to a discrepancy between the field of view of the image and the FOV of your eye viewing the image on a monitor, so if the two FOVs match, the warping goes away. There should be some guides on the internet stating how far away from your monitor you need to be, given its width, to get a specific FOV. (I know Lengyel's "Mathematics for 3D Game Programming & Computer Graphics" has a discussion on this, and I would be surprised if Möller's book didn't also have a discussion on how viewing distance to a monitor relates to perceived FOV.)

      Delete
  10. A few comments:

    In location 139 the formula:
    t*t*dot(B,B) + 2*t*dot(A-C) + dot(C,C) - R*R = 0
    seems to be wrong (dot(A-C) doesn't even make sense).
    By looking at the code I think it should be something like:
    t*t*dot(B,B) + 2*t*dot(A-C, B) + dot(A-C, A-C) - R*R = 0

    All the images I generate seem to have a fish eye effect unless I use a small angle in the camera (like 20 degrees). It also happens on most pictures of the book, where the sphere in centre looks fine but the ones on the sides look distorted. Is it common to use such a small angle in the camera or to do something to compensate?

    Could anyone else replicate the bubble effect with the sphere with negative radius inside the glass sphere in 327? I even tried copying the code line by line and still couldn't get it (it's as if the negative sphere is not there at all).

    ReplyDelete
    Replies
    1. What's your sphere intersection and normal code look like.

      And good eye on the formula. The correct formula (which you got) is with luck in the updated version on the kindle store.

      Delete
    2. Noticed the same problem with the formula -- got Kindle version on Feb 23, 2016 and still not fixed.

      Also, just prior to that one there is "dot((p - C),(p - C)) = (y-cy)*(y-cy) + (z-cz)*(z-cz)" -- missing (x-cx)*(x-cx).

      Delete
  11. I also needed to normalize the normal vector in the refraction function. After that I was able to reproduce all images in the book (including the sphere with negative radius). As for the circular dependency, I just added 'class Material;' in Hitable file. Just in case, I've uploaded it to my github as https://github.com/netolcc06/BabyRayTracer.

    ReplyDelete
    Replies
    1. You code doesn't quite compile for me. What is your platform? I like that you templated vec3 and it would be fun to race floats and double....

      I also like your drand48() substitute. Not needing to know how big rand()'s range is is clever. Mind if I steal your rand for the book?

      Delete
    2. You can steal everything you want. :)

      I'm coding on Visual C++. A friend of mine also had trouble to compile my code on Linux. After I finish everything I want to add to the ray tracer, I'll port the code.

      Besides, I've posted an image with many spheres to the main page of github. Check it out: https://github.com/netolcc06/BabyRayTracer


      I've added planes and boxes. After triangles, textures and meshes, I intend to play with lights. Any hints on how to proceed?

      Delete
  12. Prof Shirley,

    Hope you consider writing a similar book for Swift. I would definitely buy it!


    ReplyDelete
  13. In your second dielectric code example, the first calculation of cosine multiplies by the ref_idx but the second calculation of cosine does not. Is that a bug or intentional? If it's intentional why is there a difference?

    You also calculate scattered = Ray(rec.p, reflected); twice.

    ReplyDelete
    Replies
    1. Thanks! That first "scattered =" can go. The cosine is intentional and I should add some words on it in the book. The Fresnel reflectance is the same no matter which direction you are traveling in or out. The Schlick approximation to Fresnel reflectance is framed in terms of the refractive index = 1.0 cosine (the larger angle side) of the material. HOWEVER, it may be both intentional and a bug-- Snell's Law applies to sines. I will need to do some digging here. Great eye!

      Delete
    2. Yep it's a bug! Here is the fix:
      http://psgraphics.blogspot.com/2016/03/my-buggy-implimentation-of-schlick.html
      Once I clean it up a bit I will add it to the book.

      Delete
  14. Hi,thanks for writing this short book. I took a class in college and we did a ray tracing program so it was a bit of a refresher.

    I implemented mine in Python to brush up on my Python skills.

    Can you provide the camera parameters for the last image?

    Thanks

    ReplyDelete
    Replies
    1. Cool-- I will be interested to see how fast your python version is. The efficiency tools keep getting better.

      Here is my main with all the scene parameters:
      http://www.cs.utah.edu/~shirley/main.cc

      Delete
    2. I'll get some numbers. I modified it to do parallel processing to speed it up.

      Delete
    3. It's pretty slow. For comparison, the Python version took 10min and 20 seconds. The C++ version took 10.5 seconds. My picture was 200x100 with a sample of 5. Single CPU for each program.

      Maybe there is a bug in my Python code somewhere, but the pictures do come out the same.

      Delete
  15. I found the discussion about diffuse material a little bit complicated. In addition, in this code

    vec3 target = rec.p + rec.normal + random_in_unit_sphere();
    return 0.5 * color(ray(rec.p, target - rec.p), world);

    why you add rec.p in target and after you subtract it in ray function?

    ReplyDelete
    Replies
    1. You are right! I found that less confusing for myself when I wrote it, but I think your way is much better. I probably will need to change the figure but I think this will be a rare "100% better on all dimensions" code change....

      Delete
  16. Hi! Awesome book for ray tracing and I enjoy my first tour very much! I am reading the refraction part, but I confront the problem that I cannot get rid of the black stuff(edge). Base on my standing, if the dielectric scatter fail it would return false to the calling color function, which will return vec3(0,0,0) and cause black edge. Any one with the issue?

    ReplyDelete
    Replies
    1. Sorry I missed this comment until just now. I have often had similar things come up and refraction I **never** get right the first time. I would start with known cases and step debug. Painful, but what I always have to do.

      Delete
  17. Has the new version of the book been published with the fixes for the comment above (regarding the missing mat_ptr)

    http://in1weekend.blogspot.com/2016/01/ray-tracing-in-one-weekend.html?showComment=1454599661068#c3278073328434186794

    I ran into this problem as well and am looking for corrections. Also, is there a reason the source code for the book isn't available as reference?

    ReplyDelete
    Replies
    1. This is a good point-- I was unaware when I started that kindle doesn't auto-update changes. I think I will make the code available as a partial fix. For now, here's my uncleaned version: http://www.cs.utah.edu/~shirley/rtbook1/

      Delete
    2. The reason BTW was my belief that looking and typing was best. I am now thinking that is not a **good** reason :)

      Delete
    3. Awesome, thanks so much. I agree, I think looking and typing is the best as well, but my main nit is, when the code is embedding in images (and the type is really small), it is kind of hard to read, even when reading the book in the Kindle Web Viewer. I would prefer that that code was in some text form the I could then use the native zoom feature of the browser to enlarge the fonts properly.

      Again, thanks for making this book. I look forward to working through the others in the series as well.

      Delete
  18. Finished the book in Go. Was able to make a very parallel version: https://twitter.com/cwalker/status/727357592926711808

    ReplyDelete
  19. I'd be curious to see your implementation in Swift. I'm nearly done revisiting the book, in Swift this time, to learn both the language and your material on ray tracking more deeply. Trying to get it nearly as performant as the C++ implementation is interesting. I write Swift for my day job, but sadly I don't get to spend much time getting things to run fast (or understanding why they run slow), so this has been quite eye opening. I've found good use cases for optionals (returning a hit record) and for tuples (returning scattering, attenuation, and scatter success), and I can make initializers a little more terse when necessary and when intent is obvious, so that's kind of nice.

    ReplyDelete
    Replies
    1. My implementation in swift was VERY basic. It didn't compile anymore last time I checked. I will dig it up and get it to compile again. Email me a reminder if I don't post it soon!

      Delete
  20. Whats the point of this line of code
    vec3 p = r.point_at_parameter(2.0);
    At the end of the surface normal chapter in main function?
    p is not used in the rest of the function.

    ReplyDelete
    Replies
    1. D'oh! It is dead code. Good eye. Just trying to make the compiler writers proud of that optimization.

      Delete
  21. This comment has been removed by the author.

    ReplyDelete
    Replies
    1. looks like first lower_left_corner calculation at the end of chapter 10 is pointless too.

      P.S Great Book. Thanks

      Delete
  22. Programming is combination of intelligent and creative work. Programmers can do anything with code. The entire Programming tutorials that you mention here on this blog are awesome. Beginners Heap also provides latest tutorials of Programming from beginning to advance level.
    Be with us to learn programming in new and creative way.

    ReplyDelete
  23. Hi Peter, thanks for this book, or really, thank you for book 3, because the material that book discusses is pretty hard to follow in the "reference tomes".

    I wrote the dielectric as it was in the book, then thought the ref_idx term for the cosine was really strange. So it seems someone else here thought so as well and you had adressed it.

    I am better with math than physics sadly, here's the code and my comments anyway if you wish to take a look.
    https://github.com/skurmedel/strahl0/blob/master/inc/dielectric.hpp

    I probably misunderstood everything, but I think the derivation for Snell's is correct :D

    ReplyDelete
    Replies
    1. Great comments! Nice version of the code.

      Delete
  24. Would anybody attending SIGGRAPH 2016 be interested in a Birds of a Feather meeting? There's still time to propose one.

    ReplyDelete
    Replies
    1. Dr. Shirley, would you have any objection to a BOF session referring to "Ray-Tracing In One Weekend" in the title?

      Delete
    2. I'm really sorry I missed this until just now. Yes I have no objection. For future reference the answer to all such questions is go for it!

      Delete
  25. Hi!, starting with your series of book.

    It would be great if you write something on the lines of: Rasterization/game rendering in one weekend/month or so.

    I think it would be possible and even more demanded than this series.

    Cheers!

    ReplyDelete
    Replies
    1. I agree that book would have a market. I would buy it! I don't think I have the games background. But good idea.

      Delete
  26. Hello, I was wondering if there was any chance you could also have this available in amazon.ca as a Canadian who is really interested in this series.

    Cheers,

    Vishahan Thilagakumar

    ReplyDelete
  27. Hi, I decided to implement my version in Haskell. I'm pretty sure that means a harder time eliminating performance bottlenecks, but hey...

    I can use both Float or Double in the implementation, but haven't really noticed a performance difference either way. One thing I did notice though is artifacts caused by rounding errors when I use Float, so I'll stick to Double as a default for now: https://github.com/tvh/raytracinginoneweekend.

    ReplyDelete
    Replies
    1. Cool! I don't know Haskell but it's very readable.

      I usually use double for my ray tracers. Precision with float is usually doable but requires care. However, lots of people will complain if you use doubles. But once you are using anything but C you will get hassled anyway :)

      Delete
    2. Yeah, that gigantic sphere was maybe a bit silly anyway, but I didn't get around to implementing other primitives yet. I managed to optimize the code enough so that the difference Float/Double is about 10% over the whole process now. Before it was completely shadowed by packing/unpacking of boxed values.

      Delete
  28. Regarding doubles vs floats, I've been implementing this in Swift, and noticed some dramatic (and very visible) difference in brightness in the diffuse materials example (ch7). I dug and dug and finally isolated it to float/double rounding errors.

    The largest, and the one that caused the brightness difference, was in sphere::hit. In the assignments of temp, using sqrtf rather than sqrt has a dramatic difference on the output. This really surprised me, since I assumed the impact of that would be detectible in the output, but too subtle to see easily.

    So far, to get my Swift code to exactly match the C++ output, I've had to switch all sqrt() to sqrtf(), and also apply float casts here:

    float u = float(i + float(drand48())) / float(nx);
    float v = float(j + float(drand48())) / float(ny);

    and here:

    int ir = int(float(255.99)*col[0]);
    int ig = int(float(255.99)*col[1]);
    int ib = int(float(255.99)*col[2]);

    (I could also explicitly promote the floats to doubles in Swift and then truncate back to floats, but that's a lot uglier than the equivalent C++.)

    The float casts had a much smaller impact on the final results than those sqrt's in the discriminant.

    Anyway, my suspicion is that the C++ would be easier to exactly match in other languages if it used double, since then there would be fewer implicit promotions/demotions and double tends to be the more natural type (i.e the type of literals) in many languages.

    Thanks for the book; this has been a great weekend project over the holidays.

    ReplyDelete
  29. I, thank you for this awesome book, i have a little problem, i don't speak well english so i make a screenshot:
    http://i68.tinypic.com/2zyyk2x.png
    :y display is reversed, it is a problem ? i try many thing thing but im not able to make it like yours.
    Thank you for your help.

    ReplyDelete
  30. The link for the image http://imgur.com/a/hxQ8L

    ReplyDelete
    Replies
    1. Think i have resolved my issue, it was the library i use inverse the order of putting pixel in image

      Delete
    2. And that happened all before I got to this. Sometimes procrastination pays :) Good luck with the project!

      Delete
  31. This comment has been removed by the author.

    ReplyDelete
  32. Hey, Pete. I'm curious about something in the Lambertian material. You scatter rays by sampling within a unit sphere that just touches the surface at the point of intersection. That's a nice simple technique, and I like it. But I'm wondering if it is equivalent to sampling the hemisphere over the point of intersection (which I haven't implemented, but seems more complicated to do). If they are not equivalent, is one more physically correct than the other? I suppose that depends on the desired material characteristics?

    ReplyDelete
    Replies
    1. Lambertian but I was sure it was diffuse (fuzzy but not necessarily a cosine distribution). In the third book I do it "right" and get a slightly different answer. On my to-do list is to see how close it is.

      Delete