Skip to content
Permalink
Browse files

The crucial step: casting the rays + ray/sphere intersection routine

  • Loading branch information
ssloy committed Jan 20, 2019
1 parent bd36c98 commit 5806eb45e93dab225ab335824cbc3f537d511b28
Showing with 38 additions and 3 deletions.
  1. BIN out.jpg
  2. +38 −3 tinyraytracer.cpp
BIN -31.6 KB (32%) out.jpg
Binary file not shown.
@@ -5,14 +5,48 @@
#include <vector>
#include "geometry.h"

void render() {
struct Sphere {
Vec3f center;
float radius;

Sphere(const Vec3f &c, const float &r) : center(c), radius(r) {}

bool ray_intersect(const Vec3f &orig, const Vec3f &dir, float &t0) const {
Vec3f L = center - orig;
float tca = L*dir;
float d2 = L*L - tca*tca;
if (d2 > radius*radius) return false;
float thc = sqrtf(radius*radius - d2);
t0 = tca - thc;
float t1 = tca + thc;
if (t0 < 0) t0 = t1;
if (t0 < 0) return false;
return true;
}
};

Vec3f cast_ray(const Vec3f &orig, const Vec3f &dir, const Sphere &sphere) {
float sphere_dist = std::numeric_limits<float>::max();
if (!sphere.ray_intersect(orig, dir, sphere_dist)) {
return Vec3f(0.2, 0.7, 0.8); // background color
}

return Vec3f(0.4, 0.4, 0.3);
}

void render(const Sphere &sphere) {
const int width = 1024;
const int height = 768;
const int fov = M_PI/2.;
std::vector<Vec3f> framebuffer(width*height);

#pragma omp parallel for
for (size_t j = 0; j<height; j++) {
for (size_t i = 0; i<width; i++) {
framebuffer[i+j*width] = Vec3f(j/float(height),i/float(width), 0);
float x = (2*(i + 0.5)/(float)width - 1)*tan(fov/2.)*width/(float)height;
float y = -(2*(j + 0.5)/(float)height - 1)*tan(fov/2.);
Vec3f dir = Vec3f(x, y, -1).normalize();
framebuffer[i+j*width] = cast_ray(Vec3f(0,0,0), dir, sphere);
}
}

@@ -28,7 +62,8 @@ void render() {
}

int main() {
render();
Sphere sphere(Vec3f(-3, 0, -16), 2);
render(sphere);

return 0;
}

11 comments on commit 5806eb4

@techcentaur

This comment has been minimized.

Copy link

techcentaur replied Jan 22, 2019

Among the lines 46-48, you try to form a ray to shoot from each pixel. Is it correct? And I do not understand the maths that you applied. Can you explain a little bit?

@ssloy

This comment has been minimized.

Copy link
Owner Author

ssloy replied Jan 22, 2019

Check the wiki, I have added an explanation.

@cl0ne

This comment has been minimized.

Copy link

cl0ne replied Jan 23, 2019

why we assume that z distance from camera to screen equal to -1?

@cl0ne

This comment has been minimized.

Copy link

cl0ne replied Jan 23, 2019

Also since we need only direction, we probably can use

x = x + 0.5 - width/2
y = y + 0.5 - height/2
z = -height/(2.0*math.tan(fov/2))

(if FOV angle is measured vertically)

@ssloy

This comment has been minimized.

Copy link
Owner Author

ssloy replied Jan 23, 2019

Good point, thank you. Made the corresponding changes.

@norinrg

This comment has been minimized.

Copy link

norinrg replied Feb 7, 2019

Hi,

in line 40, const int fov = M_PI/2.; will evaluate to 1. As fov is used later in evaluating the tan, I assume fov's type should rather be float, right? I tried and all that changed was the size of the spheres (they got smaller). But the result should be more accurate, right?

@TomasHubelbauer

This comment has been minimized.

Copy link

TomasHubelbauer replied Feb 7, 2019

@norinrg That was fixed here:
4595c17

@ansonl

This comment has been minimized.

Copy link

ansonl replied Feb 12, 2019

Hi, on line 16, float tca = L*dir;. I assume tca is the projection of L onto dir. Why are there no more steps than just getting the dot product of L and dir?
On a linked page on site linked in the wiki http://www.lighthouse3d.com/tutorials/maths/vector-projection/, it says that the projection would need dividing the dot product of L*dir by |L| and then multiplying by L.
Is there a simplification used in the code related to line 31 in geometry.h? Thank you.

@ssloy

This comment has been minimized.

Copy link
Owner Author

ssloy replied Feb 12, 2019

Hi @ansonl , it is pretty simple. tca is indeed the projection length of L onto dir. The dot product between L and dir is equal to |L|*|dir|*cos(angle between them). Vector d was previously normalized (have you seen .normalize() everywhere?). Thus |dir| = 1. Thus cos(angle between the vectors)*|L| = L*dir. Cosine multiplied by the length is the length of the projected vector.

As I need only a scalar value, the length, I do not multiply the value I have obtained by any vector.

@techcentaur

This comment has been minimized.

Copy link

techcentaur replied Feb 12, 2019

This would be pretty understandable.
gra

@123survesh

This comment has been minimized.

Copy link

123survesh replied Mar 4, 2019

This tutorial too uses the same naming conventions, it can be helpful in understanding the ray intersection algorithm : tutorial

Please sign in to comment.
You can’t perform that action at this time.