#c #raytracing
#c #трассировка лучей
Вопрос:
Я читал и пробовал «Трассировку лучей за один уик-энд» Питера Ширли. Все шло отлично, пока не появилась часть с диффузным материалом. По сути, вместо диффузного материала мой алгоритм, похоже, отбрасывает тени только под определенным углом, и я понятия не имею, откуда может возникнуть проблема.
Обычно я следил за книгой шаг за шагом. Предыдущие разделы дают правильные результаты, и единственным кодом, который я добавил из последнего раздела в раздел диффузных материалов, являются функции, приведенные ниже.
Вот конкретные части кода для диффузного материала, которые в основном отражают луч в случайном направлении, выбранном из сферы, которая касается точки столкновения (извините, если мое объяснение недостаточно ясно).
Это функция, которая берет случайную точку из сферы, касательной к точке столкновения.
vec3 random_in_unitSphere(){
vec3 p;
std::default_random_engine generator;
std::uniform_real_distribution<float> distribution(0.0, 1.0);
do{
p = 2.0*vec3(distribution(generator),distribution(generator),distribution(generator)) - vec3(1,1,1);
}while (p.squared_length() >= 1.0);
return p;
}
Это функция, которая вычисляет цвет пикселя (отбрасывая лучи до тех пор, пока они не попадут в ничто)
vec3 color(const Rayamp; r,Hitable *world){
hit_record rec;
if(world->hit(r,0.0,FLT_MAX,rec)){
vec3 target = rec.p rec.normal random_in_unitSphere();
return 0.5*color(Ray(rec.p,target-rec.p),world);
}
else{
vec3 unit_direction = unit_vector(r.direction());
float t = 0.5*(unit_direction.y() 1.0);
return (1.0-t)*vec3(1.0,1.0,1.0) t*vec3(0.5,0.7,1.0);
}
}
И это цикл, отвечающий за отбрасывание лучей для каждого пикселя изображения.
for(int j = ny-1 ; j >= 0 ; j--){
for(int i = 0; i < nx ; i ){
vec3 col(0,0,0);
for(int s = 0; s < ns ; s ){
float u = float(i distribution(generator)) / float(nx);
float v = float(j distribution(generator)) / float(ny);
Ray r = camera.getRay(u,v);
vec3 p = r.pointAt(2.0);
col = color(r,world);
}
col /= float(ns);
int ir = int (255.99*col.r());
int ig = int (255.99*col.g());
int ib = int (255.99*col.b());
outfile<< ir << " " << ig << " " << ib << std::endl;
}
}
Вот ожидаемый результат: https://imgur.com/im5HNEK
И вот что я получаю: https://imgur.com/heNjEVV
Спасибо!
Ответ №1:
Проблема просто в том, что каждый раз, когда вы генерируете случайный вектор, вы используете новый, инициализированный по умолчанию генератор псевдослучайных чисел. Генератор случайных чисел содержит некоторое состояние, и это состояние необходимо сохранить, чтобы со временем видеть разные результаты.
Чтобы исправить это, просто сделайте ваш генератор случайных чисел статичным тем или иным способом:
vec3 random_in_unitSphere(){
vec3 p;
static std::default_random_engine generator{std::random_device{}()};
std::uniform_real_distribution<float> distribution(0.0, 1.0);
do{
p = 2.0*vec3(distribution(generator),distribution(generator),distribution(generator)) - vec3(1,1,1);
}while (p.squared_length() >= 1.0);
return p;
}
Здесь я также использовал std::random_device
(возможно) добавление некоторой случайности в реальном мире к генератору.
Комментарии:
1. @Ali, ты действительно используешь вывод
random_in_unitSphere()
в качестве направления?2. @SeverinPappadeux Если я правильно понял «Трассировку лучей за один уик-энд» Питера Ширли, тогда да. Я в основном отбрасываю луч из точки попадания в эту случайную точку вывода. РЕДАКТИРОВАТЬ вывод фактически используется для вычисления направления, плохое использование слова «направление» извините ^^»
Ответ №2:
Функция случайного направления мне кажется неправильной. Похоже, что он должен создавать три направленных косинуса (wx, wy, wz), которые являются однородными на сфере с радиусом = 1, так что
wx2 wy2 wz2 = 1
Первая проблема: вы создаете случайный движок каждый раз, когда вводите функцию, поэтому все ваши значения одинаковы. Я просто поместил его в Visual Studio 2017, C 14.1, x64, Win10 и произвел два вызова
-0.383666 -0.804919 0.0944412
-0.383666 -0.804919 0.0944412
Вторая проблема — это не случайное измерение, длина не равна 1.
Обновить
Следующая статья Вольфрама http://mathworld .wolfram.com/SpherePointPicking.html , вот код, который устраняет обе проблемы — у него есть RNG в качестве параметра, поэтому состояние изменится. И, во-вторых, точка теперь правильно выбрана на единичной сфере и может использоваться как случайное направление.Просто замените кортеж на vec3
#include <iostream>
#include <random>
#include <tuple>
std::tuple<float,float,float> random_in_unitSphere(std::mt19937amp; rng) {
std::uniform_real_distribution<float> distribution{};
float x1, x2, l;
do {
x1 = 2.0f * distribution(rng) - 1.0f;
x2 = 2.0f * distribution(rng) - 1.0f;
l = x1 * x1 x2 * x2;
} while (l >= 1.0f);
float s = sqrt(1.0f - l);
return std::make_tuple(2.0f*x1*s, 2.0f*x2*s, 1.0f - 2.0f*l);
}
int main() {
std::mt19937 rng{ 987654321ULL };
float wx, wy, wz, squared_length;
std::tie(wx, wy, wz) = random_in_unitSphere(rng);
std::cout << wx << " " << wy << " " << wz << 'n';
squared_length = wx * wx wy * wy wz * wz;
std::cout << squared_length << 'n';
std::tie(wx, wy, wz) = random_in_unitSphere(rng);
std::cout << wx << " " << wy << " " << wz << 'n';
squared_length = wx * wx wy * wy wz * wz;
std::cout << squared_length << 'n';
return 0;
}
ОБНОВЛЕНИЕ II
Вторая проблема заключается в том, что вы создали однородные точки ВНУТРИ единичной сферы. Итак, проблема не в направлениях — ваши wx, wy, wz хороши для направления, но с длиной вашего вектора направления. Типичный код трассировки лучей такой (в некотором псевдокоде)
auto [x0,y0,z0] = start_new_ray();
auto [wx,wy,wz] = sample_direction();
float path = compute_path_in_geometry(x0,y0,z0,wx,wy,wz); // compute path from start point 0 in the wx,wy,wz direction to next object
// move ray to new surface
x1 = x0 wx*path;
y1 = y0 wy*path;
z1 = z0 wz*path;
// do scattering, illumination, ... at (x1,y1,z1)
Если длина (wx, wy, wz) не равна 1, то длина, вычисленная как sqrt((x1-x0)2 (y1-y0)2 (z1-z0)2), НЕ БУДЕТ равна path
. Ваши основные правила геометрии просто нарушаются.
Комментарии:
1. Честно говоря, функция вызывается
random_in_unitSphere
, а неrandom_unit_vector
2. @alterigel Он по-прежнему не может служить производителем случайного направления, когда он это делает
newray = oldray direction*length
, результат будет неправильным. Пожалуйста, проверьте обновление3. Спасибо за ответ! Если я правильно понимаю, направление, сгенерированное моей исходной функцией, не является действительно «случайным», поскольку выбранные точки более склонны выбираться из определенных областей, верно?
4. @Ali ваша исходная функция не создает единичные векторы, и поэтому они на самом деле не являются действительными как «направления». Он создает равномерно распределенные единичные векторы в объеме единичной сферы. Я тоже немного смущен вашим описанием: «Это функция, которая принимает случайное направление от касательной сферы к точке столкновения». Где вступает в игру точка столкновения?
5. @alterigel мой плохой, функция фактически выбирает случайную точку из единичной сферы. и затем я направляю луч из точки попадания в эту выбранную случайным образом точку. Я сразу отредактирую свой вопрос