Exemplo n.º 1
0
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 * 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;
  }
}
Exemplo n.º 2
0
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 );
    }
}