C++ 中的光线追踪错误颜色计算

Ale*_*rtí 0 c++ raytracing

我正在实现光线追踪框架,但遇到一些错误 在此输入图像描述

图像应该看起来像这样,但在我的例子中看起来像: 在此输入图像描述

正如您所看到的,球体有一些黑点并且不光滑。知道这个错误可能来自哪里吗?

计算颜色,在这里我创建了所有光线的反弹。我将 epsilon 应用于光线:

vec3 ComputeColor(const Ray& ray, const world& scene, int depth, float pH, float pW) {
    if (depth > scene.maxDepth)
        return vec3(0.0f);

    const Object* hitObject;
    vec3 hitPoint;
    if (!Intersection(ray, scene, hitObject, &hitPoint))
        return vec3(0.0f);

    vec3 color = hitObject->materials.ambient + hitObject->materials.emission;

    for (const PointLightSource& light : scene.lights) {
        Ray lightRay;
        if (light.type == PointLightSource::point) {
            lightRay = Ray(light.pos, glm::normalize(hitPoint - light.pos));
        }
        else {
            lightRay = Ray(light.pos,glm::normalize(- lightRay.direction));
        }
        const float epsilon = 1e-4;
        lightRay.o = lightRay.o * epsilon;
        const Object* obj;
        vec3 lightHit;
        if (!Intersection(lightRay, scene, obj, &lightHit) || IsSameVector(hitPoint, lightHit)) {
            color += Phong(light, hitObject, ray, hitPoint, scene.attenuation);
        }
    }

    if (glm::any(glm::greaterThan(hitObject->materials.specular, vec3(0.0f)))) {
        vec3 unit_normal = glm::normalize(hitObject->InterpolatePointNormal(hitPoint));
        vec3 reflect_dir = ray.direction - (unit_normal * (2.0f * glm::dot(ray.direction, unit_normal)));
        Ray reflect_ray(hitPoint, reflect_dir);

        vec3 temp_color = temp_color+ComputeColor(reflect_ray, scene, depth + 1, pH, pW);
        color += hitObject->materials.specular * temp_color;
    }

    return color;
}
Run Code Online (Sandbox Code Playgroud)

Phong light,这是我计算光的函数:

vec3 Phong(const PointLightSource& light, const Object* hitObject, const Ray& ray, const vec3& hitPoint, const float* attenuation) {
    vec3 lightDirection;
    if (light.type == PointLightSource::point) {
        lightDirection = glm::normalize(light.pos - hitPoint);
    }
    else {
        lightDirection = glm::normalize(light.pos);
    }

    vec3 normal = glm::normalize(hitObject->InterpolatePointNormal(hitPoint));
    // Diffuse light
    const Materials& materials = hitObject->materials;
    float nDotL = max(glm::dot(normal, lightDirection), 0.0f);
    vec3 diffuse = materials.diffuse * light.color * nDotL;

    // Specular
    vec3 viewDirection = glm::normalize(-ray.direction);
    vec3 halfvec = glm::normalize(lightDirection + viewDirection);
    float nDotH = max(glm::dot(normal, halfvec), 0.0f);
    vec3 specular = materials.specular * light.color * pow(nDotH, materials.shininess);

    vec3 result = diffuse + specular;
    if (light.type == PointLightSource::point) {
        float r = glm::length(light.pos - hitPoint);
        float attenuationFactor = 1.0f / (attenuation[2] * r * r + attenuation[1] * r + attenuation[0]);
        result *= attenuationFactor;
    }
    return result;
}
Run Code Online (Sandbox Code Playgroud)

交集,我使用此函数来计算与所有类型对象的交集,在本例中为球体和三角形:

bool Intersection(const Ray& ray, const world& scene, const Object*& hitObject, vec3* hitPoint) {
    double minDistance = INFINITY;
    hitObject = nullptr;

    for (const Object* obj : scene.objects) {
        Ray rayObject = Transform(ray, obj);
        float dist;
        if (obj->Intersect(rayObject, &dist)) {
            vec3 localPoint = rayObject.o + rayObject.direction * dist;
            vec4 localPointHom(localPoint, 1.0);
            localPointHom = localPointHom * obj->transform;
            vec3 hitWorld = vec3(localPointHom.x / localPointHom.w, localPointHom.y / localPointHom.w, localPointHom.z / localPointHom.w);

            dist = glm::dot(hitWorld - ray.o, ray.direction);
            if (dist > 1e-4) {
                if (dist < minDistance) {
                    minDistance = dist;
                    hitObject = obj;
                    *hitPoint = hitWorld;
                }
            }

        
            
        }
    }

    return (hitObject != nullptr);
}
Run Code Online (Sandbox Code Playgroud)

编辑:当移动光线时我测试了这个:

lightRay.o = lightRay.o + (epsilon * lightRay.direction);//(this one does note solve "cancer issue" but do not destroy other scenes )
        lightRay.o = lightRay.o *(epsilon);//This one solves the issue but others scenes are not correct.
Run Code Online (Sandbox Code Playgroud)

pad*_*ddy 5

这被称为“癌症”,这是由于从表面发射阴影光线,但光线再次与该表面相交而引起的伪影。结果是您确定某个物体阻挡了光线,但不知道该物体本身在阴影光线的来源处阻挡了光线。这是浮点精度误差的自然结果。

解决该问题的最简单方法之一是引入“epsilon”值,并且不计算距离低于该阈值的交叉点。这允许更复杂的几何体自相交,但有点脏。

更一般地说,您可能决定在光命中测试中排除生成此阴影光线的对象。在更广义的 3D 场景中,一切都是三角形,您可以排除该三角形。但在大量镶嵌的场景中,当您碰到相邻的三角形时,您可能会再次遇到这个问题。

另一种方法是,如果表面法线指向与阴影光线相反的方向,则忽略相交。如上所示,仅当两者的点积为负时才计算交集。但如果场景中存在实际上可以阻挡光线的单面几何体,那么您应该小心这一点。

实际上,结合使用上述措施通常可以解决此问题,但您也可以只选择其中一项。

请注意,反射光线(而不仅仅是阴影光线)也会出现同样的问题。因此,您确实需要仔细地将这个逻辑构建到您的核心光线追踪算法中。