views:

381

answers:

5

I'm trying to write a simple raytracer as a hobby project and it's all working fine now, except I can't get soft-shadows to work at all. My idea of soft-shadows is that the lightsource is considered to have a location and a radius. To do a shadow test on this light I take the point where the primary ray hit an object in the scene and cast an n-amount of rays towards the lightsource where each new ray has a random component to every axis, where the random component varies between -radius and radius.

If such a ray hits an object in the scene, I increment a hitcounter (if a ray hits multiple objects, it still only increments with one). If it makes it to the lightsource without collisions, I add the distance of the primary ray's intersect point to the lightsource's center to a variable.

When n samples have been taken, I calculate the ratio of rays that have collided and multiply the color of the light by this ratio (so a light with color 1000,1000,1000 will become 500,500,500 with a ratio of 0.5, where half the rays have collided). Then I calculate the average distance to the lightsource by dividing the distance variable of earlier by the amount of non-colliding rays. I return that variable and the function exits.

The problem is: it doesn't work. Not quite at least. What it looks like can be seen here. You can see it sort of resembles soft-shadows, if you squint real hard.

I don't get it, am I making some sort of fundamental flaw here, or is it something tiny? I'm fairly sure the problem is in this method, because when I count the number of partially lit pixels produced directly by this method, there are only about 250, when there should be a lot more. And when you look closely at the picture, you can see there's some partially lit pixels, suggesting the rest of the code processes the partially lit pixels just fine.

Here's the actual light for soft-shadows class:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace MyFirstRayTracer
{
  public class AreaLight : ILight
  {
    private const int _radius = 5;
    private const int _samples = 16;
    public Color Color { get; set; }
    public Vector Location { get; set; }
    #region ILight Members

    public float GetLightingInformation(Vector point, ISceneObject[] scene, out Color color)
    {
      int intersectCount = 0;
      float distance = -1;
      for(int i = 0; i < _samples; i++)
      {
 bool intersects = false;
 float rand = 0;
 rand = _radius - (float)(new Random().NextDouble()*(2*_radius));
 foreach (ISceneObject obj in scene)
 {
   Vector iPoint;

   Vector loc = new Vector(Location.X + rand, Location.Y + rand, Location.Z + rand);

   if (!obj.Intersect(new Ray(point, loc), out iPoint))
   {
     distance += (Location - point).SqLength;

   }
   else
   {
     intersects = true;
     distance -= (Location - point).SqLength;
   }
 }
 if (intersects)
   intersectCount++;
      }
      float factor = 1-((float)intersectCount/_samples);

      color = new Color(factor*Color.R, factor*Color.G, factor*Color.B);

      return (float)Math.Sqrt(distance / (_samples - intersectCount));
    }


    #endregion
  }
}
+4  A: 

minor point but is this the best use of the random class..

 for(int i = 0; i < _samples; i++)
      {
        bool intersects = false;
        float rand = 0;
        rand = _radius - (float)(new Random().NextDouble()*(2*_radius));

should this not be..

    var rnd = new Random()    
    for(int i = 0; i < _samples; i++)
              {
                bool intersects = false;
                float rand = 0;
                rand = _radius - (float)(rnd.NextDouble()*(2*_radius));
Hath
+3  A: 

Try generating a different "rand" for each component of "loc". As is, your jittered points all lie on a line.

timday
+1  A: 

You actually generate the point on the line on a line with direction (1, 1, 1). Is the lightsource really linear?

Also, I can barely see anything in your example. Could you make your camera nearer the to-be shadow and not pointing from the direction of the light?

jpalecek
+1  A: 

See, this is why I come to this site :)

Every axis has its own random now, and it looks a lot better. It's still a little weird looking, increasing the number of samples helps though. It now looks like this.

Do you know a more efficient way to reduce the pattern-forming?

The biggest help though: not instantiating Random for every sample. It seriously tripled my rendering speed with soft shadows! I never knew that Random was so costly to instantiate. Wow.

Thanks a lot.

JulianR
timday
+1  A: 

In your response you asked for an improved way to make soft shadows. An improvement could be, instead of randomizing all the rays from the same point, to give each ray a different offset on all axes to effectively give them a seperate little window to randomize in. This should result in a more even distribution. I don't know if that was clear but another way to describe it is as a grid which is perpendicular to the shadow ray. Each tile in the grid contains one of the n shadow rays but the location in the grid is random. Here you can find a part of a tutorial which describes how this can be used for soft shadows.

Morten Christiansen