Странное поведение трассировки лучей для диффузных материалов - PullRequest
1 голос
/ 16 апреля 2019

Я читал и пробовал "Трассировка лучей за один уикенд" Питера Ширли.Все шло отлично, пока не рассыпалась материальная часть.По сути, вместо рассеянного материала мой алгоритм, похоже, отбрасывает тени только под определенным углом, и я понятия не имею, откуда возникла проблема.

Обычно я следил за книгой шаг за шагом.Предыдущие разделы дают правильные результаты, и единственный код, который я добавил из последнего раздела к диффузному материалу, - это функции ниже.

Вот конкретные части кода для диффузного материала, которые в основном отражаютлуч в случайном направлении, выбранный из сферы, которая касается точки столкновения (извините, если мое объяснение недостаточно ясно).

Это функция, которая переводит случайную точку из сферы, касательной кточка столкновения.

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 Ray& 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

Спасибо!

Ответы [ 2 ]

3 голосов
/ 16 апреля 2019

Проблема заключается в том, что каждый раз, когда вы генерируете случайный вектор, вы используете новый инициализированный по умолчанию генератор псевдослучайных чисел .Генератор случайных чисел содержит некоторое состояние, и это состояние необходимо сохранить, чтобы увидеть различные результаты с течением времени.

Чтобы это исправить, просто сделайте генератор случайных чисел статическим тем или иным образом:

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 для (возможно) добавления некоторой реальной случайности к генератору.

2 голосов
/ 16 апреля 2019

Функция произвольного направления выглядит неправильно для меня.Похоже, что он должен создать три косинуса (wx, wy, wz), которые являются однородными на сфере с радиусом = 1, так что

wx 2 + wy 2 + wz 2 = 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::mt19937& 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;
}

UPDATE 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.Ваши основные правила геометрии просто нарушаются.

...