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