I have been reading and trying out "Ray tracing in one weekend" by Peter Shirley. Everything has been going great until the diffuse material part. Basically, instead of a diffuse material, my algorithm seems to only be casting shadows from a specific angle and I have no idea from where the problem could originate from.
I have normally been following the book step by step. The previous sections give the correct results and the only code I have added from the last section to the diffuse material one are the functions below.
Here are the specific parts of the code for diffuse material, which basically reflect the ray into a random direction, chosen from a sphere that is tangent to the collision point (Sorry if my explanation isn't clear enough).
This is the function that take a random point from a sphere tangent to the collision point.
vec3 random_in_unitSphere(){
vec3 p;
std::default_random_engine generator;
std::uniform_real_distribution<float> distribution(0.0, 1.0);
do{
p = 2.0*vec3(distribution(generator),distribution(generator),distribution(generator)) - vec3(1,1,1);
}while (p.squared_length() >= 1.0);
return p;
}
This is the function that calculate the color of a pixel (By casting rays until it hits nothing)
vec3 color(const Ray& r,Hitable *world){
hit_record rec;
if(world->hit(r,0.0,FLT_MAX,rec)){
vec3 target = rec.p + rec.normal + random_in_unitSphere();
return 0.5*color(Ray(rec.p,target-rec.p),world);
}
else{
vec3 unit_direction = unit_vector(r.direction());
float t = 0.5*(unit_direction.y() + 1.0);
return (1.0-t)*vec3(1.0,1.0,1.0) + t*vec3(0.5,0.7,1.0);
}
}
And this is the loop responsible for casting rays for every pixel of the image.
for(int j = ny-1 ; j >= 0 ; j--){
for(int i = 0; i < nx ; i++){
vec3 col(0,0,0);
for(int s = 0; s < ns ; s++){
float u = float(i+ distribution(generator)) / float(nx);
float v = float(j+ distribution(generator)) / float(ny);
Ray r = camera.getRay(u,v);
vec3 p = r.pointAt(2.0);
col += color(r,world);
}
col /= float(ns);
int ir = int (255.99*col.r());
int ig = int (255.99*col.g());
int ib = int (255.99*col.b());
outfile<< ir << " " << ig << " " << ib << std::endl;
}
}
Here is the expected output : https://i.sstatic.net/G52MH.jpg
And here is what I get : https://i.sstatic.net/XYwhd.jpg
Thanks !
The problem is simply that every time you generate a random vector, you're using a new, default-initialized psuedorandom number generator. A random number generator contains some state, and this state needs to be preserved in order to see different results over time.
To fix this, simply make your random number generator static in one way or another:
vec3 random_in_unitSphere(){
vec3 p;
static std::default_random_engine generator{std::random_device{}()};
std::uniform_real_distribution<float> distribution(0.0, 1.0);
do{
p = 2.0*vec3(distribution(generator),distribution(generator),distribution(generator)) - vec3(1,1,1);
}while (p.squared_length() >= 1.0);
return p;
}
Here, I've also used std::random_device
to (possibly) add some real-world randomness to the generator.