void testUniformSurfaceUnitHalfSphere() { Plot2D plot( output_path + "/random_half_sphere.png", plot_size, plot_size ); Vector4 dir( 1.0, 1.0, 1.0 ), v; for( auto i = 0; i < points_per_plot; i++ ) { rng.uniformSurfaceUnitHalfSphere( dir, v ); plot.addPoint( v.x, v.y ); } }
DistributionSample Material::sampleHemisphereUniform( RandomNumberGenerator & rng, const RayIntersection & intersection ) const { DistributionSample sample; rng.uniformSurfaceUnitHalfSphere( intersection.normal, sample.direction ); //sample.pdf_sample = 1.0f / (2.0f * M_PI); // Special case for diffuse. reflectedRadiance() already accounts for the divide by // pi for diffuse, so as long as we're doing uniform sampling in sampleBxDF, we should // return unity here so things balance out appropriately. sample.pdf_sample = 1.0f; sample.direction.makeDirection(); return sample; }
void BasicDiffuseSpecularShader::shade( Scene & scene, RandomNumberGenerator & rng, RayIntersection & intersection ) { RGBColor diffuse_contrib( 0.0, 0.0, 0.0 ); RGBColor specular_contrib( 0.0, 0.0, 0.0 ); RGBColor direct_contrib( 0.0, 0.0, 0.0 ); Ray new_ray; RayIntersection new_intersection; new_intersection.min_distance = 0.01; Vector4 offset( 0.0, 0.0, 0.0 ); //offset = scale( intersection.normal, 0.01 ); // NOTE - Seems to help new_ray.origin = add( intersection.position, offset ); new_ray.depth = intersection.ray.depth + 1; const unsigned char max_depth = 5; //const unsigned char max_depth = 4; //const unsigned char max_depth = 3; //const unsigned char max_depth = 2; Vector4 from_dir = intersection.fromDirection(); // Asserts intersection.normal.assertIsUnity(); intersection.normal.assertIsDirection(); // Direct lighting // // Area lights //for( Traceable * traceable : scene.lights ) { // // TODO - sample lights //} // Point lights for( const PointLight & light : scene.point_lights ) { Vector4 to_light = subtract( light.position, intersection.position ); float dist_sq_to_light = to_light.magnitude_sq(); Vector4 direction = to_light.normalized(); direction.makeDirection(); // Shoot a ray toward the light to see if we are in shadow Ray shadow_ray( intersection.position, direction ); RayIntersection shadow_isect; shadow_isect.min_distance = 0.01; if( scene.intersect( shadow_ray, shadow_isect ) && sq(shadow_isect.distance) < dist_sq_to_light ) { continue; } // Not in shadow float cos_r_n = fabs( dot( direction, intersection.normal ) ); RGBColor color = light.band_power; color.scale(cos_r_n); color.scale(1.0f / to_light.magnitude_sq()); // distance falloff // TODO: use actual material parameters properly so we can get specular here, too if( intersection.material ) direct_contrib.accum( mult( color, intersection.material->diffuse ) ); else direct_contrib.accum( color ); } // TODO - How best should we choose between diffuse and specular? // FIXME: Should I scale by the cosine for a perfect reflector? if( intersection.ray.depth < max_depth ) { const float diffuse_chance = 0.9; float diff_spec_select = rng.uniform01(); if( intersection.material->perfect_reflector || intersection.material->perfect_refractor ) { auto sample = intersection.material->sampleBxDF( rng, intersection ); new_ray.direction = sample.direction; new_ray.index_of_refraction = sample.new_index_of_refraction; if( scene.intersect( new_ray, new_intersection ) ) { if( new_intersection.distance != FLT_MAX ) { shade( scene, rng, new_intersection ); } specular_contrib.accum( new_intersection.sample.color ); } } else if( diff_spec_select < diffuse_chance ) { // Diffuse rng.uniformSurfaceUnitHalfSphere( intersection.normal, new_ray.direction ); new_ray.direction.makeDirection(); if( scene.intersect( new_ray, new_intersection ) ) { if( new_intersection.distance != FLT_MAX ) shade( scene, rng, new_intersection ); float cos_r_n = dot( new_ray.direction, intersection.normal ); new_intersection.sample.color.scale( cos_r_n ); diffuse_contrib.accum( new_intersection.sample.color ); } } else { // Specular // TODO - sample the specular lobe, not just the mirror direction new_ray.direction = mirror( from_dir, intersection.normal ); new_ray.direction.makeDirection(); if( scene.intersect( new_ray, new_intersection ) ) { if( new_intersection.distance != FLT_MAX ) shade( scene, rng, new_intersection ); float cos_r_n = dot( new_ray.direction, intersection.normal ); new_intersection.sample.color.scale( cos_r_n ); specular_contrib.accum( new_intersection.sample.color ); } } } if( intersection.material ) { intersection.sample.color = mult( diffuse_contrib, intersection.material->diffuse ); intersection.sample.color.accum( mult( specular_contrib, intersection.material->specular ) ); intersection.sample.color.accum( intersection.material->emittance ); intersection.sample.color.accum( direct_contrib ); } else { intersection.sample.color = diffuse_contrib; intersection.sample.color.accum( direct_contrib ); } }