光线追踪器不会根据方向给出不同的光强度

Ray tracer not giving different light intensities based on direction

目标: 我正在尝试在 C 中创建一个光线追踪器。我刚刚添加了一个光源,它应该根据我的三个球体的位置为每个球体提供阴影效果莱特群岛如果光线在它们的左边,那么阴影应该在右边。

问题:当改变光强和光的位置时,所有的球体都统一改变。球体或多或少会被均匀照亮,并且球体上各个像素的光照没有变化。

我的调试尝试:我尝试通过打印出很多不同的信息来查看变量输出,我认为源来自我的变量

diffuse_light_intensity

变化不大(在屏幕上的所有迭代中,由于表面上的光线角度变化很大,值应该经常变化,但值却变化了两次)

我的代码:(我的理论是问题出在scene_intersect()或cast_ray())

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <math.h>
#include <limits.h>

typedef struct {
    float position[3];
    float intensity;
} Light;

typedef struct {
    float diffuse_color[3];

}  Material;

typedef struct {
    float center[3];
    float radius;
    Material material;

} Sphere;

int arrSub(const float arr1[], const float arr2[], float subArr[], int length) {
    /*
    Requires 3 equally sized arrays (denoted as length),
    arr1 - arr2 will result in the third array subArr
    */
    for (int i = 0; i < length; i++) {
        subArr[i] = arr1[i] - arr2[i];
    }
    return 0;
}

int arrAdd(const float arr1[], const float arr2[], float addArr[], int length) {
    /*
    Requires 3 equally sized arrays (denoted as length),
    arr1 + arr2 will result in the third array subArr
    */
    for (int i = 0; i < length; i++) {
        addArr[i] = arr1[i] + arr2[i];
    }
    return 0;
}

int arrScalarMult(const float arr1[], float scalar, float newArr[], int length) {
    /*
    Requires 3 equally sized arrays (denoted as length),
    arr1 - arr2 will result in the third array subArr
    */
    for (int i = 0; i < length; i++) {
        newArr[i] = arr1[i] * scalar;
    }
    return 0;
}

float dotProduct(const float arr1[], const float arr2[], int length) {
    /*
    Returns the dot product of two equal sized arrays 
    (treated as vectors)

    a (dot) b = a1b1 + a2b2 + ... anbn
    */
    float result = 0;

    for (int i = 0; i < length; i++) {
        result += arr1[i] * arr2[i];
    }

    return result;
}

int normalize(float arr[], int len) {
    //Normalize a vector (array)

    float sumSqr;
    float norm;

    for (int i = 0; i < len; i++) {
        sumSqr += arr[i] * arr[i];
    }

    norm = sqrt(sumSqr);

    for (int i = 0; i < len; i++) {
        arr[i] = arr[i] / norm;
    }

    return 0;
}

bool ray_intersect(const float origin[], const float dir[], float t0, Sphere s) {
    /*
    Ray-Sphere Intersection

    Vectors:
        origin (the zero vector)
        dir (direction vector)
        L (vector from origin to center of sphere)
    Scalars:
        tca
        d2
        thc
        t0
        t1    
    */
    float L[3] = {0,0,0}; //The zero vector
    arrSub(s.center, origin, L, 3); //L is now the vector from origin to the sphere's center

    float tca = dotProduct(L, dir, 3); //Projection of L onto dir
    float d2 = dotProduct(L, L, 3) - tca*tca;

    if (d2 > s.radius * s.radius) return false; //There is no intersection, so return false.

    float thc = sqrtf((s.radius*s.radius - d2));
    t0 = tca - thc;
    float t1 = tca + thc;
    if (t0 < 0) {
        t0 = t1;
    }
    if (t0 < 0) return false;

    return true;
}

bool scene_intersect(const float origin[], const float dir[], const Sphere s[], int len, float hit[], float N[], Material * ptr_m) {
    float sphere_dist = INT_MAX;

    for (size_t i=0; i < len; i++) {
        float dist_i;
        if (ray_intersect(origin, dir, dist_i, s[i]) && dist_i < sphere_dist) {
            sphere_dist = dist_i;

            float dirDist[3];
            arrScalarMult(dir, dist_i, dirDist, 3);
            arrAdd(origin, dirDist, hit, 3);

            float hitMinusCenter[3];
            arrSub(hit, s[i].center, hitMinusCenter, 3);
            normalize(hitMinusCenter, 3);

            N[0] = hitMinusCenter[0];
            N[1] = hitMinusCenter[1];
            N[2] = hitMinusCenter[2];

            * ptr_m = s[i].material;
        }
    }
    return sphere_dist<1000;
}

int cast_ray(const float origin[], const float dir[], const Sphere s[], const Light l[], int l_size, unsigned char colorArr[]) {
    float point[3], N[3];
    Material m;
    Material * ptr_m = &m;

    if (!scene_intersect(origin, dir, s, 3, point, N, ptr_m)) {
        //background
        colorArr[0] = 5; //red
        colorArr[1] = 100; //green
        colorArr[2] = 250; //blue
    } else {
        float diffuse_light_intensity = 0;
        float light_dir[3];

        for (size_t i = 0; i < l_size; i++) {
            arrSub(l[i].position, point, light_dir, 3);
            normalize(light_dir, 3);
            diffuse_light_intensity += l[i].intensity * ((0.f >= dotProduct(light_dir, N, 3) ? (0.f) : (dotProduct(light_dir, N, 3))));
        }
        //light up pixel
        colorArr[0] = m.diffuse_color[0] * diffuse_light_intensity;
        colorArr[1] = m.diffuse_color[1] * diffuse_light_intensity;
        colorArr[2] = m.diffuse_color[2] * diffuse_light_intensity;
    }

    return 0;
}

int render(const Sphere s[], const Light l[], int l_length) {
    /*
    Creates image in a new color each step.
    */
    const int width = 1024;
    const int height = 768;

    FILE *fp = fopen("fourth.ppm", "wb"); // Write in binary mode
    (void) fprintf(fp, "P6\n%d %d\n255\n", width, height);

    float fov = 3.1415926535/2.; // Field of View

    #pragma omp parallel for
    for (size_t j = 0; j < height; j++) {
        for (size_t i = 0; i < width; i++) {

            float x = (2*(i+.5)/(float)width - 1)*tan(fov/2.)*width/(float)height;
            float y = -(2*(j+.5)/(float)height - 1)*tan(fov/2.);

            float dir[] = {x,y,-1};
            normalize(dir, 3);

            unsigned char color[3];
            const float origin[] = {0,0,0};
            cast_ray(origin, dir, s, l, l_length, color);
            (void) fwrite(color, 1, 3, fp);
        }
    }
    (void) fclose(fp);
    return 0;
}

int main(void) {
    Material red = {255,0,0};
    Material pink = {150,10,150};
    Material gold = {255, 195, 0};

    //Populate with spheres
    Sphere s[3];
    Sphere originalS = {{-3,0,-16},2,gold};
    Sphere bigS = {{-1.0, -1.5, -12}, 3, red};
    Sphere anotherS = {{7,5,-18},2,pink};

    s[0] = originalS;
    s[1] = bigS;
    s[2] = anotherS;

    //Add light source
    Light l[1];

    Light test_light = {{-20,20,20}, 1.5};

    l[0] = test_light;

    render(s,l, 1);
    printf("Run success!\n");
    return 0;
}

如果我的代码需要任何说明,请告诉我,我对 C 和 Whosebug 都很陌生。

ray_intersect 中存在一个基本错误,您按值而不是指针传递 t0 变量,因此在 scene_intersect 函数中它的值始终是零。

另一个问题是您没有在 normalize 函数中初始化 sumSqr,导致该函数为每个向量分量返回 NaN

修复了这两个问题后,我得到了一些近似于阴影球的东西。该图像中的错误是由于未能确保您的输出像素值落在 [0, 255] 范围内造成的。

注意:如果您打开完整的编译器错误检查,两个 的第一个错误都会被检测到,警告您正在使用未初始化的变量。