/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * TRACE ALGORITHM * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ ngl::Colour Renderer::trace(ngl::Vec3 _from, ngl::Vec3 _direction, int depth) { // create vector that will store intersection values for parameter t in the primary ray // a primary ray has a form like R = O + t * D where O is the origin vector, and D direction. std::vector<double> intersections; geo::Ray cam_ray(_from,_direction); // iterate over objects in the scene and find intersections for(unsigned int i = 0; i < m_scene->m_objects.size(); i++) { // each Shape subclass (Sphere, Plane...) has its own method for calculating intersections intersections.push_back( m_scene->m_objects.at(i)->getIntersection(cam_ray)); } // find closest object int closest_index = getIndexClosest(intersections); // if no intersections are found RETURN black = if(closest_index == -1) {return ngl::Colour(0,0,0,1);} // calculate pHit (position of the new intersection) and nHit (normal at hit point) ngl::Vec3 pHit = _from + intersections.at(closest_index) * _direction; ngl::Vec3 nHit = m_scene->m_objects.at(closest_index)->getNormalAt(pHit); // calculate if we are inside or outside bool inside = false; if(_direction.dot(nHit) > 0) { nHit = -nHit; inside = true; } float bias = 0.01; // calculate if point is obscured or shadowed bool isObscured = raycast(pHit + nHit * bias, closest_index); // // // // // // // // // // // // // // put all contributions together // // // // // // // // // // // // // // // is the object reflective or refractive??? if ((m_scene->m_objects.at(closest_index)->getMaterial()->isReflective() || m_scene->m_objects.at(closest_index)->getMaterial()->isRefractive()) && depth < m_max_depth) { ngl::Colour crfr(0,0,0,1); ngl::Colour crfl(0,0,0,1); // check whether it is REFLECTIVE if (m_scene->m_objects.at(closest_index)->getMaterial()->isReflective()) { // calculate reflection dir float bias = 0.01; ngl::Vec3 refl_dir = _direction - nHit * 2 * _direction.dot(nHit); refl_dir.normalize(); // fire ray along reflection direction from hit point crfl = trace(pHit + bias*nHit, refl_dir, depth+1); } // check whether it is REFRACTIVE if (m_scene->m_objects.at(closest_index)->getMaterial()->isRefractive()) { // calculate refrection dir (transmission ray) float ior = m_scene->m_objects.at(closest_index)->getMaterial()->getIOR(); float eta = inside; float bias = 0.01; float cosi = -nHit.dot(_direction); if (eta == true) // we are inside { eta = ior; } else // we are outside { eta = 1 / ior; } float k = 1 - eta * eta * (1 - cosi * cosi); ngl::Vec3 refr_dir = _direction * eta + nHit * (eta * cosi - sqrt(k)); refr_dir.normalize(); crfr = trace(pHit - nHit * bias, refr_dir, depth+1); } ngl::Colour surfaceColor = m_scene->m_objects.at(closest_index)->getColour(pHit); float cosineFactor = std::max(-nHit.dot(cam_ray.getDirection()),(float)0); float attenuation; ngl::Colour Ka(1,0,0.4,1); ngl::Colour Kd; ngl::Colour Ks; float ambient_intensity = 0.05; ngl::Colour ambient_contrib = Ka * surfaceColor * ambient_intensity; ngl::Colour diffuse_contrib(0,0,0,1); ngl::Colour specular_contrib(0,0,0,1); for(unsigned int m = 0; m < m_scene->m_lights.size(); m++) { ngl::Vec3 v_distance = m_scene->m_lights.at(m)->m_pos - pHit; float distance = v_distance.length(); float radius = 8; attenuation = 1 - pow(distance/radius,2); Kd = m_scene->m_lights.at(m)->m_diff_col; Ks = m_scene->m_lights.at(m)->m_spec_col; ngl::Vec3 L = m_scene->m_lights.at(m)->m_pos - pHit; L.normalize(); ngl::Vec3 N = nHit; ngl::Vec3 R = 2 * (L.dot(N) * N) - L; R.normalize(); diffuse_contrib += (surfaceColor * (Kd * pow(std::max(L.dot(N),(float)0),2) * m_scene->m_lights.at(m)->m_diff_int))*attenuation; specular_contrib += ((Ks * pow(std::max(R.dot(-_direction),(float)0),900)*400 * m_scene->m_lights.at(m)->m_spec_int))*attenuation; } specular_contrib.clamp(0,0.8); ngl::Colour s01 = crfl * m_scene->m_objects.at(closest_index)->getMaterial()->getReflIntensity(); ngl::Colour s02 = crfr * m_scene->m_objects.at(closest_index)->getMaterial()->getTransparency(); ngl::Colour s03 = s01 + s02; ngl::Colour diffuseColor = m_scene->m_objects.at(closest_index)->getColour(pHit) * cosineFactor * m_scene->m_objects.at(closest_index)->getMaterial()->getDiffuseIntensity(); // Do PHONG MODEL calculations stuff. By now I keep it VERY VERY simple ngl::Colour outRadiance = diffuseColor + s03 + specular_contrib + ambient_contrib; outRadiance.clamp(0,1); return isObscured ? outRadiance * 0.7f : outRadiance; } // if it is not REFLECTIVE nor REFRACTIVE else { ngl::Colour surfaceColor = m_scene->m_objects.at(closest_index)->getColour(pHit); float attenuation; ngl::Colour Ka(1,0,0.4,1); ngl::Colour Kd; ngl::Colour Ks; float ambient_intensity = 0.05; ngl::Colour ambient_contrib = Ka * surfaceColor * ambient_intensity; ngl::Colour diffuse_contrib(0,0,0,1); ngl::Colour specular_contrib(0,0,0,1); for(unsigned int m = 0; m < m_scene->m_lights.size(); m++) { ngl::Vec3 v_distance = m_scene->m_lights.at(m)->m_pos - pHit; float distance = v_distance.length(); float radius = 8; attenuation = 1 - pow(distance/radius,2); Kd = m_scene->m_lights.at(m)->m_diff_col; Ks = m_scene->m_lights.at(m)->m_spec_col; ngl::Vec3 L = m_scene->m_lights.at(m)->m_pos - pHit; L.normalize(); ngl::Vec3 N = nHit; ngl::Vec3 R = 2 * (L.dot(N) * N) - L; R.normalize(); float spec_hardness = m_scene->m_objects.at(closest_index)->getMaterial()->m_spec_hardness; diffuse_contrib += (surfaceColor * (Kd * pow(std::max(L.dot(N),(float)0),2) * m_scene->m_lights.at(m)->m_diff_int))*attenuation; specular_contrib += ((Ks * pow(std::max(R.dot(-_direction),(float)0),spec_hardness) * m_scene->m_lights.at(m)->m_spec_int))*attenuation; } diffuse_contrib.clamp(0,1); specular_contrib.clamp(0,1); ngl::Colour outRadiance = diffuse_contrib + specular_contrib + ambient_contrib; outRadiance.clamp(0,1); return isObscured ? outRadiance * 0.7f : outRadiance; } }
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 ); } }