Неожиданный результат при вычислении теней в трассировщике лучей

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

Мой код разделен следующим образом:

  • У меня есть базовый класс "SceneObject", который имеет виртуальный метод intersect(), который принимает луч (определенный по происхождению и направлению) и выводит логическое значение, а также возвращает аргументы для вычисленного значения t, точки попадания и нормального объекта.
  • Класс "Материал" содержит зеркальные и диффузные цвета (векторы), а также значение для показателя степени фонга (int).
  • У меня есть 3 производных класса от вышеупомянутого SceneObject: классы плоскости, треугольника и сферы, каждый со своей версией пересечения, определенной в базовом классе.
  • У меня есть функция, которая вычисляет выходной цвет для данного пикселя, используя нормаль объекта, точку попадания, источник света и материал объекта.
  • Все мои объекты для рендеринга хранятся в векторе.

Вот один из моих производных классов, Треугольник:

class Triangle: public SceneObject{
public:
    Triangle (vec3 a, vec3 b, vec3 c, Material mat){
        name = "Triangle";
        p0 = a;
        p1 = b;
        p2 = c;
        objectMaterial = mat;
        normal = normalize(cross(p0-p1, p0-p2));
    }

    //Möller-Trumbore algorithm
    bool intersect(Ray aRay, float &t, vec3 &hitPoint, vec3 &n){//we will use ray-plane intersection and barycentric coords:

        bool returnValue = false;
        //first we need to get a t of intersection between the passed ray and the triangle
        vec3 v0v1 = p1-p0;
        vec3 v0v2 = p2-p0;
        vec3 pvec = cross(aRay.getDirection(), v0v2);

        float det = dot(v0v1, pvec);

        if ( det >= 1e-6 ){ // Only draw if not backfacing

            float invDet = 1/det;

            float u = dot(-p0, pvec) * invDet;
            // No intersection if u < 0 or u > 1
            if (u >=0 && u <= 1) {
                vec3 qvec = cross(-p0, v0v1);
                float v = dot(aRay.getDirection(), qvec) * invDet;

                // No intersection if v < 0 or u + v > 1
                if (v >=0 && (u + v) <= 1){
                    t = dot(v0v2, qvec) * invDet;
                    returnValue = true;

                    hitPoint = aRay.getOrigin() + (t*aRay.getDirection());
                    n = normal;
                    //calculated_Out_Colour = calculateOutputColour(normal, aRay, lightSource, objectMaterial, t, hitPoint);

                }
            }
        }

        return returnValue; 
    }

private:
    vec3 p0;
    vec3 p1;
    vec3 p2;
    vec3 normal;
};

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

for(int i=0;i<imageBuffer.Height();i++){
for(int j=0;j<imageBuffer.Width();j++){
    float currentX = ((float)i-256);
            float currentY = ((float)j-256);
            //cout << currentX << ", " << currentY << ", " << currentZ << endl;
            //make a ray for this pixel (i,j)
            glm::vec3 rayDirection = glm::normalize(glm::vec3(currentX, currentY, -d));
    //make a ray for this pixel (i,j)
    Ray currentRay(vec3(0,0,0), rayDirection);

    vec3 hitPoint;
    vec3 normalAtHit;
    float tnear = 999; // closest intersection, set to INFINITY to start with
    SceneObject* object = NULL;
    for (int k = 0; k < objects.size(); k++) {
        float t; // intersection to the current object if any
        if (objects[k]->intersect(currentRay, t, hitPoint, normalAtHit) && t < tnear) {
            object = objects[k].get();
            tnear = t;

            vec3 shadowRayDirection = normalize(light1.getLightOrigin()-hitPoint);
            Ray shadowRay(hitPoint+vec3(0.03, 0.03,0.03), shadowRayDirection);
            float shadowT;
            vec3 shadowHitPoint;
            vec3 shadowN;
            for (int m = 0; m < objects.size(); ++m) {
                if (objects[m]->intersect(shadowRay, shadowT, shadowHitPoint, shadowN)) {
                    imageBuffer.SetPixel(i, j, ambientColour*ambientIntensity); 
                    break;
                } else {
                    imageBuffer.SetPixel(i, j, calculateOutputColour(normalAtHit, currentRay, light1, objects[k]->getMaterial(), hitPoint));
                }
            }
            }
        }
    }
}

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

Я ценю любую помощь в попытке отладить эту вещь. Благодарю.

1 ответ

Cornstalks прав, но одна вещь важна, если вы используете hitPoint+0.03*normalAtHit, вы должны быть уверены, что нормаль - это та, которая соответствует падающему лучу, из того, что мы видим в вашем коде, вы просто берете нормаль вашего примитива, но каждое лицо имеет 2 нормали (одно обратное от другого).

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

Для этого вы можете проверить точечное произведение вашего инцидентного луча с нормальным, в зависимости от результата (больше 0 или меньше 0), вы будете знать, нужно ли вам отменить нормальное.

Другие вопросы по тегам