RayTracing在C++中计算颜色时出现错误。

huangapple go评论77阅读模式
英文:

RayTracing wrong color compute in C++

问题

我正在实现一个光线追踪框架,但是遇到了一些错误。

图像应该是这样的,但是在我的情况下看起来是这样的:

你可以看到,球体上有一些黑点,而且不够平滑。你有任何想法这个错误可能来自哪里吗?

在计算颜色时,我创建了光线的所有反弹。我对光线应用了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;
}

Phong光照模型,这是我计算光照的函数:

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;
}

Intersection函数,我使用这个函数来计算与各种对象的相交,这里是球体和三角形:

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);
}

编辑:
在移动光线时,我尝试了以下两种方法:

lightRay.o = lightRay.o + (epsilon * lightRay.direction); //(这个方法不能解决“癌症问题”,但不会破坏其他场景)
lightRay.o = lightRay.o * epsilon; //这个方法解决了问题,但其他场景不正确。
英文:

I'm implementing a rayTracing framework, but I'm having some error
RayTracing在C++中计算颜色时出现错误。

The image should look like this, but in my case looks like:
RayTracing在C++中计算颜色时出现错误。

As you can see, spheres have some black dots and not are smooth. Any idea where this error could come from?

Compute color, here I create all the rebounds of the rays. I applied the epsilon to the light ray:

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

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

	vec3 color = hitObject-&gt;materials.ambient + hitObject-&gt;materials.emission;

	for (const PointLightSource&amp; 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, &amp;lightHit) || IsSameVector(hitPoint, lightHit)) {
			color += Phong(light, hitObject, ray, hitPoint, scene.attenuation);
		}
	}

	if (glm::any(glm::greaterThan(hitObject-&gt;materials.specular, vec3(0.0f)))) {
		vec3 unit_normal = glm::normalize(hitObject-&gt;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-&gt;materials.specular * temp_color;
	}

	return color;
}

Phong light, this is my function where I compute the light:

vec3 Phong(const PointLightSource&amp; light, const Object* hitObject, const Ray&amp; ray, const vec3&amp; 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-&gt;InterpolatePointNormal(hitPoint));
	// Diffuse light
	const Materials&amp; materials = hitObject-&gt;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;
}

Intersection, I use this function to compute the intersection with all kind of objects, in this case spheres and triangles:

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

	for (const Object* obj : scene.objects) {
		Ray rayObject = Transform(ray, obj);
		float dist;
		if (obj-&gt;Intersect(rayObject, &amp;dist)) {
			vec3 localPoint = rayObject.o + rayObject.direction * dist;
			vec4 localPointHom(localPoint, 1.0);
			localPointHom = localPointHom * obj-&gt;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 &gt; 1e-4) {
				if (dist &lt; minDistance) {
					minDistance = dist;
					hitObject = obj;
					*hitPoint = hitWorld;
				}
			}

		
			
		}
	}

	return (hitObject != nullptr);
}

EDIT:
When moving the light ray I tested this:

lightRay.o = lightRay.o + (epsilon * lightRay.direction);//(this one does note solve &quot;cancer issue&quot; but do not destroy other scenes )
		lightRay.o = lightRay.o *(epsilon);//This one solves the issue but others scenes are not correct.

答案1

得分: 5

这被称为“癌症”,它是由于在表面上发射阴影光线时,光线再次与该表面相交而导致的伪影。结果是你确定有某个物体阻挡了光线,但并不知道是物体本身在阴影光线的源头上造成了阻挡。这是浮点精度误差的自然结果。

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

更一般地,你可以决定在光线碰撞测试中排除生成该阴影光线的物体。在更一般的三维场景中,其中所有物体都是三角形,你可以排除该三角形。但在高度细分的场景中,当你碰到相邻的三角形时,可能会再次遇到这个问题。

另一种方法是忽略交点,如果表面法线指向与阴影光线相反的方向。也就是说,只有当两者的点积为负时才计算交点。但如果场景中有单面几何体实际上可以阻挡光线,你应该小心使用这种方法。

实际上,上述措施的组合通常可以解决这个问题,但你也可以只选择其中一种。

需要注意的是,相同的问题也可能发生在反射光线上,而不仅仅是阴影光线。因此,你确实需要将这个逻辑仔细地构建到核心光线追踪算法中。

英文:

This is known as "cancer", which is an artifact caused by shooting shadow rays off your surface, but having the ray intersect with that surface again. The result is you determine something is blocking the light, without knowing the object itself did that at the source of the shadow ray. It happens as a natural result of floating point precision error.

One of the simplest ways to solve it is to introduce an "epsilon" value and do not count intersections with a distance below that threshold. This allows self-intersection of more complex geometry but is a little grimey.

More generally, you may decide to exclude the object that generated this shadow ray from being considered in the light hit-test. In a more generalized 3D scene where everything is triangles, you can exclude that triangle. But in heavily tessellated scenes, you may run into the issue again as you hit neighboring triangles.

Another approach is to ignore the intersection if the surface normal points in the opposite direction to the shadow ray. As in, only count the intersection if the dot product of the two is negative. But you should be careful with that one if you have single-sided geometry in the scene that can actually block light.

In practice, a combination of the above measures will typically solve this problem, but you can get by with selecting only one.

Note that the same issue can occur with reflected rays, not just shadow rays. So you really need to build this logic carefully into your core ray-tracing algorithm.

huangapple
  • 本文由 发表于 2023年8月9日 04:18:38
  • 转载请务必保留本文链接:https://go.coder-hub.com/76862950.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定