Color xray::Shade(const Ray &ray, SurfPoint pt, const Object *obj, int ray_depth) {
    Scene *scene = active_xrc->scene;
    bool entering = true;

    if(DotProduct(ray.dir, pt.normal) > 0.0) {
        pt.normal = -pt.normal;
        entering = false;
    }

    TexCoord tc = obj->GetTexCoords(pt.pos);
    Vector3 vref = ray.dir.Normalized().Reflection(pt.normal);

    MatPhong *mat;
    if(!(mat = dynamic_cast<MatPhong*>(obj->GetMaterial()))) {
        error("only phong materials supported currently\n");
        exit(0);
    }

    Color diffuse = 0.0;
    Color specular = 0.0;

    int light_count = scene->lights.size();
    for(int i=0; i<light_count; i++) {
        Light *light = scene->lights[i];

        Vector3 lpos = light->GetPRS(scene->anim.current_time).position;
        Vector3 ldir = lpos - pt.pos;
        //scalar_t light_dist = ldir.Length();
        ldir.Normalize();

        // cast shadow ray
        scalar_t shadow_factor = ShadowRay(pt.pos, light);
        if(shadow_factor == 0.0) continue;

        Color lcol = light->GetColor() * light->GetIntensity();


        // calculate diffuse contribution
        scalar_t diffuse_coef = std::max(0.0, DotProduct(ldir, pt.normal));
        diffuse += Color(mat->GetAttr(MAT_PHONG_DIFFUSE)(tc)) * lcol * diffuse_coef * shadow_factor;

        // calculate specular contribution
        scalar_t spec_pow = mat->GetAttr(MAT_PHONG_POWER)(tc).x;
        scalar_t specular_coef = pow(std::max(0.0, DotProduct(ldir, vref)), spec_pow);
        specular += Color(mat->GetAttr(MAT_PHONG_SPECULAR)(tc)) * lcol * specular_coef * shadow_factor;
    }

    scalar_t refl_coeff = mat->GetAttr(MAT_REFLECT)(tc).x;

    scalar_t mat_ior = mat->GetAttr(MAT_IOR)(tc).x;
    if(mat_ior > 1.0) {     // dielectric
        refl_coeff *= std::max(0.0, std::min(Fresnel(DotProduct(-ray.dir, pt.normal), mat_ior), 1.0));
    }

    Color refl;
    if(refl_coeff > active_xrc->ray_energy_threshold) {
        Ray refl_ray = ray;
        refl_ray.origin = pt.pos;
        refl_ray.dir = ray.dir.Reflection(pt.normal);
        refl_ray.energy *= refl_coeff;

        float glossiness = mat->GetAttr(MAT_GLOSSINESS)(tc).x;
        if(glossiness < small_number) {
            refl = ShootRay(refl_ray, ray_depth + 1);
        } else {
            int samples = std::max(1, (active_xrc->glossy_samples / (ray_depth + 1)) / active_xrc->rays_per_pixel);

            refl = SampleSolidAngle(refl_ray, glossiness, samples, ray_depth + 1);
            active_xrc->stats.gloss_rays += samples;
        }
    }

    scalar_t trans_coeff = mat->GetAttr(MAT_TRANSMIT)(tc).x;

    Color trans;
    if(trans_coeff > active_xrc->ray_energy_threshold) {
        Ray trans_ray = ray;
        trans_ray.origin = pt.pos;
        trans_ray.energy *= trans_coeff;

        scalar_t new_ior;
        if(entering) {
            new_ior = mat_ior;
            trans_ray.Enter(new_ior);
        } else {
            trans_ray.Leave();
            new_ior = trans_ray.ior;
        }
        trans_ray.dir = ray.dir.Refraction(pt.normal, ray.ior, new_ior);

        if(DotProduct(trans_ray.dir, pt.normal) > 0.0) {    // if TIR occured
            if(entering) {
                trans_ray.Leave();  // we did not actually enter...
            } else {
                trans_ray.Enter(mat_ior);   // we did not actually leave...
            }
        }

        trans = ShootRay(trans_ray, ray_depth + 1);
    }

    Color ambient = Color(mat->GetAttr(MAT_PHONG_AMBIENT)(tc)) * scene->ambient;
    Color emissive = Color(mat->GetAttr(MAT_PHONG_EMISSIVE)(tc));

    return ambient + emissive + diffuse + specular + refl * refl_coeff + trans * trans_coeff;
}