Я пытаюсь написать простой raytracer как хобби-проект, и теперь все работает нормально, за исключением того, что я не могу заставить работать мягкие тени. Моя идея мягких теней состоит в том, что источник света имеет местоположение и радиус. Чтобы провести тест тени на этом источнике света, я беру точку, в которой первичный луч попадает на объект в сцене, и отбрасываю n лучей в направлении источника света, где каждый новый луч имеет случайный компонент для каждой оси, где случайный компонент изменяется между -радиусом и радиусом.
Если такой луч попадает на объект на сцене, я увеличиваю счетчик обращений (если луч попадает на несколько объектов, он все еще увеличивается только на единицу). Если он попадает в источник света без столкновений, я добавляю расстояние от точки пересечения первичного луча до центра источника света к переменной.
Когда было взято n образцов, я вычисляю соотношение лучей, которые столкнулись, и умножаю цвет света на это соотношение (таким образом, свет с цветом 1000, 1000, 1000 станет 500 500 500 с соотношением 0,5, где половина лучей столкнулась). Затем я вычисляю среднее расстояние до источника света, разделив переменную расстояния ранее на количество не сталкивающихся лучей. Я возвращаю эту переменную, и функция завершается.
Проблема в том, что это не работает. По крайней мере, не совсем. Как это выглядит, можно увидеть здесь . Вы можете видеть, что это напоминает мягкие тени, если вы щуритесь очень сильно.
Я не понимаю, я делаю здесь какой-то фундаментальный недостаток или это что-то крошечное? Я вполне уверен, что проблема в этом методе, потому что, когда я подсчитываю количество частично освещенных пикселей, полученных непосредственно этим методом, их всего около 250, а их должно быть намного больше. И когда вы внимательно посмотрите на картинку, вы увидите, что некоторые частично освещенные пиксели предполагают, что остальная часть кода обрабатывает частично освещенные пиксели просто отлично.
Вот фактический свет для класса мягких теней:
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
}
}