C ++ - Работа с ошибками с плавающей запятой в геометрической интерполяции - PullRequest
0 голосов
/ 05 июня 2019

Задача

Я пишу трассировщик лучей как пример использования конкретного подхода машинного обучения в компьютерной графике.

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

В принципе, если я рассеиваю луч от точки O к поверхности, расположенной в (x, y, z) , где z = 81, я бы ожидал, что решение быть чем-то вроде S = (x, y, 81) .
Проблема в том, что я получаю решение типа (x, y, 81.000000005) .
Это, конечно, проблема, потому что от этого решения зависят следующие операции, и оно должно быть точным.

Вопрос

У меня вопрос: как люди в компьютерной графике справляются с этой проблемой? Я попытался изменить мои переменные с float на double, и это не решило проблему.

Альтернативные решения

Я пытался использовать функцию std::round(). Это может помочь только в определенных ситуациях, но не тогда, когда точное решение содержит одну или несколько значащих цифр.
То же самое для std::ceil() и std::floor().

EDIT

Вот так я вычисляю пересечение с поверхностью (прямоугольником), параллельной осям xz .
Прежде всего, я вычисляю расстояние t между началом моего Луча и поверхностью. Если мой Луч в указанном направлении не попадает на поверхность, t возвращается как 0.

class Rectangle_xy: public Hitable {
public:
    float x1, x2, y1, y2, z;
    ... 

    float intersect(const Ray &r) const { // returns distance, 0 if no hit
        float t = (y - r.o.y) / r.d.y;      // ray.y = t* dir.y
        const float& x = r.o.x + r.d.x * t;
        const float& z = r.o.z + r.d.z * t;
        if (x < x1 || x > x2 || z < z1 || z > z2 || t < 0) {
            t = 0;
            return 0;
        } else {
            return t;
        }
    ....
    }

В частности, с учетом Луча и id объекта в списке (который я хочу ударить):

inline Vec hittingPoint(const Ray &r, int &id) {
    float t;                             // distance to intersection
    if (!intersect(r, t, id))
        return Vec();
    const Vec& x = r.o + r.d * t;// ray intersection point (t calculated in intersect())
    return x ;
}

Функция intersect() в предыдущем фрагменте кода проверяет каждый прямоугольник в списке rect, если я пересекаю некоторый объект:

inline bool intersect(const Ray &r, float &t, int &id) {
    const float& n = NUMBER_OBJ; //Divide allocation of byte of the whole scene, by allocation in byte of one single element
    float d;
    float inf = t = 1e20;
    for (int i = 0; i < n; i++) {
        if ((d = rect[i]->intersect(r)) && d < t) { // Distance of hit point
            t = d;
            id = i;
        }
    }

    // Return the closest intersection, as a bool
    return t < inf;
}

Координата затем получается с использованием геометрической интерполяции между линией и поверхностью в трехмерном пространстве:

Vec& x = r.o + r.d * t;

где:
r.o: он представляет происхождение луча. Он определяется как r.o: Vec (float a, float b, float c)
r.d: это направление луча. Как и прежде: r.d: Vec (float d, float e, float f). t: float представляет расстояние между объектом и началом координат.

1 Ответ

0 голосов
/ 05 июня 2019

Вы можете использовать std::numeric_limits<T>::epsilon для сравнения чисел с плавающей запятой / двойного. И посмотрите, если ваш результат в регионе + -epsilon.

Альтернативой может быть отсутствие трассировки лучей к точке. Может быть, просто поместите туда сравнительно маленькую коробочку или сферу.

...