// use phong shading model Vec4d PhongMaterial::shade( const RayIntersection& intersection, const Light& light) const { // calculate diffuse part of the equation Vec3d lightDirection = (light.position() - intersection.position()).normalize(); Vec3d diffuse = this->color() * std::max(dot(intersection.normal(), lightDirection), 0.d); // calculate specular part Vec3d viewDirection = intersection.ray().direction(); Vec3d reflection = reflect(lightDirection, intersection.normal()); Vec3d lightcolor = light.spectralIntensity() / 255; Vec3d specular = ((mShininess + 2) / (2 * M_PI)) * SPECULAR_REFLECTION_DEGREE * pow(std::max(dot(reflection, viewDirection), 0.d), mShininess) * lightcolor; // calculcate sum -> result return Vec4d(diffuse + specular, 1); }
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; }
Vec4d Raytracer::shade(const RayIntersection& intersection, size_t depth) const { // This offset must be added to intersection points for further // traced rays to avoid noise in the image const Vec3d offset(intersection.normal() * Math::safetyEps()); Vec4d color(0,0,0,1); std::shared_ptr<const Renderable> renderable = intersection.renderable(); std::shared_ptr<const Material> material = renderable->material(); for(size_t i=0;i <mScene->lights().size();++i) { const Light &light = *(mScene->lights()[i].get()); //Shadow ray from light to hit point. const Vec3d L = (intersection.position() + offset) - light.position(); const Ray shadowRay(light.position(), L); //Shade only if light in visible from intersection point. if (!mScene->anyIntersection(shadowRay,L.length())) color += material->shade(intersection,light); } // limit recursion depth if (depth >= mMaxDepth) return color; Vec3d dir = reflect(intersection.ray().direction(), intersection.normal()); Ray reflectedRay(intersection.position() + offset, dir); double reflectance = material->reflectance(); color = color * (1 - reflectance) + reflectance * trace(reflectedRay, depth - 1) + Vec4d(0.0,0.0,0.0,1.0); return color; }
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; }
Vec4d Raytracer::shade(const RayIntersection &intersection, size_t depth) const { // This offset must be added to intersection points for further // traced rays to avoid noise in the image const Vec3d offset(intersection.normal() * Math::safetyEps()); Vec4d color(0, 0, 0, 1); std::shared_ptr<const Renderable> renderable = intersection.renderable(); std::shared_ptr<const Material> material = renderable->material(); for (size_t i = 0; i < mScene->lights().size(); ++i) { const Light &light = *(mScene->lights()[i].get()); //Shadow ray from light to hit point. const Vec3d L = (intersection.position() + offset) - light.position(); const Ray shadowRay(light.position(), L); //Shade only if light in visible from intersection point. if (!mScene->anyIntersection(shadowRay, L.length())) color += material->shade(intersection, light); } return color; }
bool Scene::closestIntersection(const Ray &ray,RayIntersection& intersection, double maxLambda) const { double closestLambda = maxLambda; RayIntersection tmpIntersection; RayIntersection closestIntersection; bool hit(false); for (size_t i=0;i<mRenderables.size();++i) { std::shared_ptr<Renderable> r = mRenderables[i]; if(r->closestIntersection(ray,closestLambda,tmpIntersection)) { if(tmpIntersection.lambda() < closestLambda) { hit=true; closestLambda = tmpIntersection.lambda(); closestIntersection = tmpIntersection; } } } intersection=closestIntersection; return hit; }
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; }
Vec4d CheckerMaterial::shade(const RayIntersection& intersection, const Light& light) const { const Vec3d &uvw = intersection.uvw(); const bool left = (fmod(fabs(uvw[0]), 1/mTiles[0]) < (1/mTiles[0] / double(2))) ^ (uvw[0] < double(0)); const bool lower = (fmod(fabs(uvw[1]), 1/mTiles[1]) < (1/mTiles[1] / double(2))) ^ (uvw[1] < double(0)); if(left ^ lower) return mMaterial1->shade(intersection, light); else return mMaterial2->shade(intersection, light); }
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 ); } }
Spectrum Path::computeLi( const vec2f &pFilm, const SceneData &i_scene, const Camera &i_camera, const Sampler &i_sampler, int i_depth ) { Camera::CameraSample cameraSample; cameraSample.pixelSample = pFilm + i_sampler.sample2D(); cameraSample.lensSample = i_sampler.sample2D(); Ray ray; float weight = i_camera.generateRay( cameraSample, &ray ); // Radiance Spectrum L = Spectrum( 0.0f ); // Importance Spectrum beta = Spectrum( 1.0 ); for ( int bounces = 0;; bounces++ ) { RayIntersection intersection; bool foundIntersection = i_scene.intersect( ray, intersection ); float maxDepth = 5; if ( !foundIntersection || bounces >= maxDepth ) { break; } Spectrum Le = intersection.Le(); if ( Le.isBlack() ) { // Direct illumination sampling Spectrum Ld = beta * uniformSampleOneLight( intersection, i_sampler, i_scene ); L += Ld; // BSDF Sampling const shading::BSDF &bsdf = intersection.getBSDF(); const vec3f &surfacePosition = intersection.getSurfacePoint().pos; vec3f woLocal = -intersection.worldToSurface( -intersection.dir ); vec3f wiLocal; float pdf = 1.0; const vec2f &bsdfSample = i_sampler.sample2D(); Spectrum f = bsdf.sampleF( woLocal, &wiLocal, bsdfSample, &pdf ); // No point of continuing if no color if ( f.isBlack() || pdf == 0.0 ) { break; } // Transform to world space vec3f wiWorld = intersection.surfaceToWorld( wiLocal ); // Generate new ray ray = Ray( surfacePosition, wiWorld ); beta *= ( f * absDot( wiWorld, intersection.getSurfacePoint().normal ) ) / pdf; // Russian Roulette if ( beta.y() < 1.0f && bounces > 3 ) { float q = std::max( .05f, 1 - beta.maxComponentValue() ); if ( i_sampler.sample1D() > q ) { beta /= ( 1.0 - q ); } else { break; } } } else { L += beta * intersection.Le() / 50.0; break; } } return weight * L; }
Spectrum Path::estimateDirectLighting( const RayIntersection i_intersection, RenderablePtr i_light, const Sampler &i_sampler, const SceneData &i_scene ) { // Direct contribution Spectrum Ld = Spectrum::black(); float lightPdf = 0.0f; float surfacePdf = 0.0f; vec3f wiWorld; const vec3f &woWorld = -i_intersection.dir; const vec3f &woLocal = i_intersection.worldToSurface( woWorld ); const vec3f &normal = i_intersection.getSurfacePoint().normal; const shading::BSDF &bsdf = i_intersection.getBSDF(); /* ==================== */ /* Sample Light */ /* ==================== */ { VisibilityTester visibilityTester; const vec3f &uLight = i_sampler.sample3D(); Spectrum Li = i_light->sampleIncidenceRadiance( i_intersection, uLight, &wiWorld, &lightPdf, &visibilityTester ); // Convert to local (surface) coords const vec3f &wiLocal = i_intersection.worldToSurface( wiWorld ); // Sample light if ( !Li.isBlack() && lightPdf > 0.0f ) { const Spectrum &f = bsdf.computeF( woLocal, wiLocal ) * absDot( wiWorld, normal ); surfacePdf = bsdf.pdf( woLocal, wiLocal ); if ( !f.isBlack() ) { // Ray is in shadow if ( visibilityTester.isOccluded( i_scene ) ) { Li = Spectrum::black(); } if ( !Li.isBlack() ) { // Weight with MIS float misWeight = powerHeuristic( lightPdf, surfacePdf ); Ld += ( Li * f * misWeight ) / lightPdf; } } } } /* ==================== */ /* Sample BSDF */ /* ==================== */ { const vec2f &uSurface = i_sampler.sample2D(); vec3f wiLocal; const Spectrum &f = bsdf.sampleF( woLocal, &wiLocal, uSurface, &surfacePdf ) * absDot( wiWorld, normal ); if ( !f.isBlack() && surfacePdf > 0.0f ) { // TODO computer light PDF lightPdf = 0.0; // No contribution, return if ( lightPdf == 0.0 ) { return Ld; } // Convert to local (surface) coords vec3f wiWorld = i_intersection.surfaceToWorld( wiLocal ); const Ray bsdfRay = Ray( i_intersection.getSurfacePoint().pos, wiWorld ); RayIntersection lightIntersection; bool foundIntersection = i_scene.intersect( bsdfRay, lightIntersection ); Spectrum Li = Spectrum::black(); if ( foundIntersection ) { if ( lightIntersection.m_shapeId == i_light->getIdentifier() ) { Li = i_light->computeRadiance( lightIntersection.getSurfacePoint(), wiWorld ); } } if ( !Li.isBlack() ) { // Weight with MIS float misWeight = powerHeuristic( lightPdf, surfacePdf ); Ld += ( Li * f * misWeight ) / surfacePdf; } } } return Ld; }