Я пытаюсь написать простой трассировщик лучей в качестве хобби-проекта, и теперь все работает нормально, за исключением того, что я вообще не могу заставить работать мягкие тени. Моя идея мягких теней заключается в том, что источник света считается имеющим местоположение и радиус. Чтобы провести теневой тест на этом свете, я беру точку, в которой первичный луч попадает в объект в сцене, и бросаю n-количество лучей в сторону источника света, где каждый новый луч имеет случайный компонент для каждой оси, где случайный компонент меняется. между -radius и радиусом.
Если такой луч попадает в объект в сцене, я увеличиваю счетчик попаданий (если луч попадает в несколько объектов, он все равно увеличивается только на один). Если он попадает в источник света без столкновений, я добавляю в переменную расстояние от точки пересечения первичного луча до центра источника света.
Когда было взято 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
}
}