DistributionSample MirrorMaterial::sampleBxDF( RandomNumberGenerator & rng, const RayIntersection & intersection ) const { // Perfect mirror - PDF if a Dirac delta function DistributionSample sample; sample.direction = mirror( intersection.fromDirection(), intersection.normal ); sample.pdf_sample = DistributionSample::DIRAC_PDF_SAMPLE; return sample; }
void InspectionShader::shade( Scene & scene, RandomNumberGenerator & rng, RayIntersection & intersection ) { RGBColor color( 0.0f, 0.0f, 0.0f ); const float index_in = intersection.ray.index_of_refraction; const float index_out = intersection.material->index_of_refraction; const Vector4 from_dir = intersection.fromDirection(); switch( property ) { case FresnelDialectric: { Vector4 refracted = refract( from_dir, intersection.normal, index_in, index_out ); float fresnel = 1.0f; if( refracted.magnitude() > 0.0001 ) { fresnel = Fresnel::Dialectric::Unpolarized( dot( from_dir, intersection.normal ), dot( refracted, intersection.normal.negated() ), index_in, index_out ); } color = RGBColor( fresnel, fresnel, fresnel ); } break; case FresnelConductor: { float absorptionCoeff = 2.0; // FIXME: material specific float fresnel = Fresnel::Conductor::Unpolarized( dot( from_dir, intersection.normal ), index_out, absorptionCoeff ); color = RGBColor( fresnel, fresnel, fresnel ); } break; case Normal: { auto shifted = add( scale( intersection.normal, 0.5 ), Vector4( 0.5, 0.5, 0.5 ) ); color = RGBColor( shifted.x, shifted.y, shifted.z ); } break; case IndexOfRefraction: { float v = index_out / 3.0; color = RGBColor( v, v, v ); } break; case TextureUVCoordinate: { color = RGBColor( intersection.u, intersection.v, 0.0 ); } break; default: ; } intersection.sample.color = color; }
DistributionSample RefractiveMaterial::sampleBxDF( RandomNumberGenerator & rng, const RayIntersection & intersection ) const { // Perfect Fresnel refractor DistributionSample sample; const float index_in = intersection.ray.index_of_refraction; const float index_out = index_of_refraction; const Vector4 from_dir = intersection.fromDirection(); // FIXME: HACK - This assumes that if we hit the surface of an object with the same // index of refraction as the material we're in, then we are moving back into // free space. This might not be true if there are numerical errors tracing // abutting objects of the same material type, or for objects that are intersecting. if( index_out == index_in ) { sample.new_index_of_refraction = 1.0f; } Vector4 refracted = refract( from_dir, intersection.normal, index_in, index_out ); float fresnel = 1.0f; // default to total internal reflection if refract() returns // a zero length vector if( refracted.magnitude() > 0.0001 ) { fresnel = Fresnel::Dialectric::Unpolarized( dot( from_dir, intersection.normal ), dot( refracted, intersection.normal.negated() ), index_in, index_out ); } const float draw = rng.uniform01(); // Use RNG to choose whether to reflect or refract based on Fresnel coefficient. // Random selection accounts for fresnel or 1-fresnel scale factor. if( fresnel == 1.0f || draw < fresnel ) { // Trace reflection (mirror ray scaled by fresnel or 1 for total internal reflection) sample.direction = mirror( from_dir, intersection.normal ); // FIXME - How do we determine pdf_sample for total internal reflection? sample.pdf_sample = fresnel; sample.new_index_of_refraction = index_in; } else { // Trace refraction (refracted ray scaled by 1-fresnel) sample.direction = refracted; sample.pdf_sample = 1.0 - fresnel; sample.new_index_of_refraction = index_out; } 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 ); } }