1
votes

I've made a ray tracer:

And I've been implementing the Blinn-Phong algorithm, but I've come to some issues when rendering in specific situations (otherwise it works quite well)

  • When a sphere is hovering in the air, lambertian shading isn't working correctly (seems to be only happening if the sphere is above the light source position) - Screenshot

  • When light source is below sphere - Screenshot

  • When a shadow is being cast onto a sphere (can also be seen in 1st example) - Screenshot

  • When there is more than 1 plane in the scene (Only if shadows are on) - Screenshot

I'm assuming there is something wrong with my shadow and lambertian shading algorithms, but I'm not sure what, could somebody help?

Code (there are no external libraries, just compile):

Color finalColor;
Color ambient;
Color diffuse;
FPType lambertian;
FPType phong;
Color specular;
bool shadowed = false;

// Ambient
if(AMBIENT_ON)
{
    ambient =  closestObjectMaterial.GetColor() * AMBIENT_LIGHT * closestObjectMaterial.GetAmbient();
    finalColor += ambient;
}

// Shadows, Diffuse, Specular
for(const auto &lightSource : lightSources)
{
    Vector lightDir = (lightSource->GetPosition() - point); // Calculate the directional vector towards the lightSource
    FPType distance = lightDir.Magnitude();
    lightDir = lightDir.Normalize();
    lambertian = closestObjectNormal.Dot(lightDir);

    // Shadows
    if(SHADOWS_ON && lambertian > 0)
    {
        Ray shadowRay(point, lightDir); // Cast a ray from the first intersection to the light

        std::vector<FPType> secondaryIntersections;
        for(const auto &sceneObject : sceneObjects)
        {
            secondaryIntersections.push_back(sceneObject->GetIntersection(shadowRay));
        }

        for(const auto &secondaryIntersection : secondaryIntersections)
        {
            if(secondaryIntersection > TOLERANCE) // If shadow ray intersects with some object along the way
            {
                shadowed = true;
                finalColor *= closestObjectMaterial.GetDiffuse() * AMBIENT_LIGHT;
                break;
            }
        }
    }

    // Diffuse
    if(DIFFUSE_ON && shadowed == false)
    {
        diffuse = closestObjectMaterial.GetColor().Average(lightSource->GetColor()) * closestObjectMaterial.GetDiffuse() * lightSource->GetIntensity() * std::fmax(lambertian, 0) / distance;
        finalColor += diffuse;
    }
    if(shadowed == false && SPECULAR_ON)
    {
        // Specular
        if(closestObjectMaterial.GetSpecular() > 0 && closestObjectMaterial.GetSpecular() <= 1)
        {
            Vector V = -sceneDirection;
            // Blinn-Phong
            Vector H = (lightDir + V).Normalize();
            FPType NdotH = closestObjectNormal.Dot(H);

            phong = pow(NdotH, 300);
            specular = lightSource->GetColor() * std::fmax(0, phong) * lightSource->GetIntensity() / distance; // closestObjectMaterial.GetSpecular(); add or no?
            finalColor += specular;
        }
    }
}

FPType Sphere::GetIntersection(Ray ray)
{
    Vector length = center - ray.GetOrigin(); // Length of the vector between the center and the ray origin (hypotenuse)
    FPType tca = length.Dot(ray.GetDirection()); // opposide side

    if(tca < 0) // No intersection registered
        return -1;

if(tca > 0) // Intersection registered
{
    FPType a = sqrt(length.Dot(length) - tca*tca); // Adjacent side (a = sqrt(c²-b²))

    if(a > radius || a < 0)
        return -1;

    FPType thc = sqrt(radius*radius - a*a); // the line between 2 intersection points / 2

    FPType primaryIntersection;
    primaryIntersection = tca - thc;
    if(primaryIntersection > 0)
        return primaryIntersection;
    else
    {
        FPType secondaryIntersection = thc + tca;
        return secondaryIntersection;
    }
}
return -1;

}

1
Can GetIntersection return a negative number?1201ProgramAlarm
Added GetIntersection code for spheresThe Dude

1 Answers

0
votes

I was unsure why your Sphere-Ray intersection had two square roots, Would you like to test my implementation based on this?

FPType Sphere::GetIntersection(Ray ray)
{
  Vector delta = ray.GetOrigin() - center;
  Vector dir = ray.GetDirection();

  //Quadratic equation describing the distance along ray to intersection
  FPType a = dir.Dot(dir);
  FPType b = dir.Dot(delta); //removed factor of 2 later divide by a, NOT 2a
  FPType c = delta.Dot(delta) - radius*radius;

  FPType descrim = b*b - a*c;
  if (descrim < FPType(0)) {
    return -1;
  }
  //Find solutions to quadratic equation
  descrim = sqrt(descrim) / a;
  b = -b / a;

  FPType intersection0 = b - descrim
  if(intersection0 >= FPType(0)) {
    return intersection0;
  }

  FPType intersection1 = b + descrim
  if(intersection1 >= FPType(0)) {
    return intersection1;
  }

  //Both solutions were negative
  return -1;
}