7
votes

I am working on a ray tracer, but I am stuck for days on the shadow part. My shadow is acting really weird. Here is an image of the ray tracer:

enter image description here

Image of shadow with two spheres

The black part should be the shadow.

The origin of the ray is always (0.f, -10.f, -500.f), because this is a perspective projection and that is the eye of the camera. When the ray hits a plane, the hit point is always the origin of the ray, but with the sphere it is different. It is different because it is based on the position of the sphere. There is never an intersection between the plane and sphere because the origin is huge difference. I also tried to add shadow on a box, but this doesn't work either. The shadow between two spheres does work!

If someone wants to see the intersection code, let me know.

Thanks for taking the time to help me!

Camera

Camera::Camera(float a_fFov, const Dimension& a_viewDimension, vec3 a_v3Eye, vec3 a_v3Center, vec3 a_v3Up) :
m_fFov(a_fFov),
m_viewDimension(a_viewDimension),
m_v3Eye(a_v3Eye),
m_v3Center(a_v3Center),
m_v3Up(a_v3Up)
{
  // Calculate the x, y and z axis
  vec3 v3ViewDirection = (m_v3Eye - m_v3Center).normalize();
  vec3 v3U = m_v3Up.cross(v3ViewDirection).normalize();
  vec3 v3V = v3ViewDirection.cross(v3U);

  // Calculate the aspect ratio of the screen
  float fAspectRatio = static_cast<float>(m_viewDimension.m_iHeight) / 
                       static_cast<float>(m_viewDimension.m_iWidth);
  float fViewPlaneHalfWidth = tanf(m_fFov / 2.f);
  float fViewPlaneHalfHeight = fAspectRatio * fViewPlaneHalfWidth;

  // The bottom left of the plane
  m_v3ViewPlaneBottomLeft = m_v3Center - v3V * fViewPlaneHalfHeight - v3U * fViewPlaneHalfWidth;

  // The amount we need to increment to get the direction. The width and height are based on the field of view.
  m_v3IncrementX = (v3U * 2.f * fViewPlaneHalfWidth);
  m_v3IncrementY = (v3V * 2.f * fViewPlaneHalfHeight);
}

Camera::~Camera()
{
}

const Ray Camera::GetCameraRay(float iPixelX, float iPixelY) const
{
  vec3 v3Target = m_v3ViewPlaneBottomLeft + m_v3IncrementX * iPixelX + m_v3IncrementY * iPixelY;
  vec3 v3Direction = (v3Target - m_v3Eye).normalize();

  return Ray(m_v3Eye, v3Direction);
}

Camera setup

Scene::Scene(const Dimension& a_Dimension) :
m_Camera(1.22173f, a_Dimension, vec3(0.f, -10.f, -500.f), vec3(0.f, 0.f, 0.f), vec3(0.f, 1.f, 0.f))
{
  // Setup sky light
  Color ambientLightColor(0.2f, 0.1f, 0.1f);
  m_AmbientLight = new AmbientLight(0.1f, ambientLightColor);

  // Setup shapes
  CreateShapes();

  // Setup lights
  CreateLights();

  // Setup buas
  m_fBias = 1.f;
}

Scene objects

Sphere* sphere2 = new Sphere();
sphere2->SetRadius(50.f);
sphere2->SetCenter(vec3(0.f, 0.f, 0.f));
sphere2->SetMaterial(matte3);

Plane* plane = new Plane(true);
plane->SetNormal(vec3(0.f, 1.f, 0.f));
plane->SetPoint(vec3(0.f, 0.f, 0.f));
plane->SetMaterial(matte1);

Scene light

PointLight* pointLight1 = new PointLight(1.f, Color(0.1f, 0.5f, 0.7f), vec3(0.f, -200.f, 0.f), 1.f, 0.09f, 0.032f);

Shade function

for (const Light* light : a_Lights) {
    vec3 v3LightDirection = (light->m_v3Position - a_Contact.m_v3Hitpoint).normalized();

    light->CalcDiffuseLight(a_Contact.m_v3Point, a_Contact.m_v3Normal, m_fKd, lightColor);

    Ray lightRay(a_Contact.m_v3Point + a_Contact.m_v3Normal * a_fBias, v3LightDirection);

    bool test = a_RayTracer.ShadowTrace(lightRay, a_Shapes);

    vec3 normTest = a_Contact.m_v3Normal;
    float test2 = normTest.dot(v3LightDirection);

    // No shadow
    if (!test) {
        a_ResultColor += lightColor * !test * test2;
    }
    else {
        a_ResultColor = Color(); // Test code - change color to black.
    }
}
1
It is actually passed as a reference in the CalcDiffuseLight function, but I changed it, so it would be clear to you guys. This is definitely wrong.. sorry! I will edit it. @gezaSander
@geza I added an older picture were the shadow on the two spheres work, except on the box.Sander
On that picture, shadows are non-black. But on your first picture, you have black pixels (I suppose that you haven't changed ambient color to black). Don't you have some floating-point problems somewhere? NaN, inf? It's hard to tell what could be the problem without the full source code. Btw., in the future, apply some gamma correction to your pictures, to have a more realistic look ;)geza
The black color is just to make it clear if it's shadow or not. It is just hard coded color to 0. I haven't seen any floating-point problem. Is there a program where I can show my full source code? @gezaSander
I will definitely look into gamma, but for now I want to start with the basics. I also need to add: reflection, fresnel, spatial partitioning, et cetera.Sander

1 Answers

5
votes

You have several bugs:

  • in Sphere::Collides, m_fCollisionTime is not set, when t2>=t1
  • in Sphere::Collides, if m_fCollisionTime is negative, then the ray actually doesn't intersect with the sphere (this causes the strange shadow on the top of the ball)
  • put the plane lower, and you'll see the shadow of the ball
  • you need to check for nearest collision, when shooting a ray from the eye (just try, swap the order of the objects, and the sphere suddenly becomes behind the plane)

With these fixed, you'll get this:enter image description here