我的软阴影代码有什么问题?
我正在尝试编写一个简单的光线跟踪器作为一个业余爱好项目,现在一切正常,除了我根本无法让软阴影工作。 我对软阴影的想法是光源被认为具有位置和半径。 为了对此光进行阴影测试,我选取主光线击中场景中物体的点,并向光源投射 n 条光线,其中每条新光线对每个轴都有一个随机分量,其中随机分量会变化-radius 和 radius 之间。
如果这样的光线击中场景中的一个对象,我会增加一个命中计数器(如果一条光线击中多个对象,它仍然只增加一个)。 如果它在没有碰撞的情况下到达光源,我会将主光线的交点到光源中心的距离添加到一个变量中。
当采集了 n 个样本后,我计算碰撞光线的比率,并将光的颜色乘以该比率(因此,颜色为 1000,1000,1000 的光将变成 500,500,500,比率为 0.5,其中一半光线发生碰撞)。 然后,我通过将先前的距离变量除以非碰撞光线的数量来计算到光源的平均距离。 我返回该变量并且函数退出。
问题是:它不起作用。 至少不完全是。 您可以在此处查看其外观。 如果你用力眯起眼睛,你会发现它有点像软阴影。
我不明白,我在这里犯了某种根本性的缺陷,还是只是很小的缺陷? 我相当确定问题出在这个方法中,因为当我计算这个方法直接产生的部分点亮的像素数量时,只有大约 250 个,而实际上应该有更多。 当您仔细观察图片时,您可以看到有一些部分亮起的像素,这表明代码的其余部分可以很好地处理部分亮起的像素。
这是软阴影类的实际光线:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace MyFirstRayTracer
{
public class AreaLight : ILight
{
private const int _radius = 5;
private const int _samples = 16;
public Color Color { get; set; }
public Vector Location { get; set; }
#region ILight Members
public float GetLightingInformation(Vector point, ISceneObject[] scene, out Color color)
{
int intersectCount = 0;
float distance = -1;
for(int i = 0; i < _samples; i++)
{
bool intersects = false;
float rand = 0;
rand = _radius - (float)(new Random().NextDouble()*(2*_radius));
foreach (ISceneObject obj in scene)
{
Vector iPoint;
Vector loc = new Vector(Location.X + rand, Location.Y + rand, Location.Z + rand);
if (!obj.Intersect(new Ray(point, loc), out iPoint))
{
distance += (Location - point).SqLength;
}
else
{
intersects = true;
distance -= (Location - point).SqLength;
}
}
if (intersects)
intersectCount++;
}
float factor = 1-((float)intersectCount/_samples);
color = new Color(factor*Color.R, factor*Color.G, factor*Color.B);
return (float)Math.Sqrt(distance / (_samples - intersectCount));
}
#endregion
}
}
I'm trying to write a simple raytracer as a hobby project and it's all working fine now, except I can't get soft-shadows to work at all. My idea of soft-shadows is that the lightsource is considered to have a location and a radius. To do a shadow test on this light I take the point where the primary ray hit an object in the scene and cast an n-amount of rays towards the lightsource where each new ray has a random component to every axis, where the random component varies between -radius and radius.
If such a ray hits an object in the scene, I increment a hitcounter (if a ray hits multiple objects, it still only increments with one). If it makes it to the lightsource without collisions, I add the distance of the primary ray's intersect point to the lightsource's center to a variable.
When n samples have been taken, I calculate the ratio of rays that have collided and multiply the color of the light by this ratio (so a light with color 1000,1000,1000 will become 500,500,500 with a ratio of 0.5, where half the rays have collided). Then I calculate the average distance to the lightsource by dividing the distance variable of earlier by the amount of non-colliding rays. I return that variable and the function exits.
The problem is: it doesn't work. Not quite at least. What it looks like can be seen here. You can see it sort of resembles soft-shadows, if you squint real hard.
I don't get it, am I making some sort of fundamental flaw here, or is it something tiny? I'm fairly sure the problem is in this method, because when I count the number of partially lit pixels produced directly by this method, there are only about 250, when there should be a lot more. And when you look closely at the picture, you can see there's some partially lit pixels, suggesting the rest of the code processes the partially lit pixels just fine.
Here's the actual light for soft-shadows class:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace MyFirstRayTracer
{
public class AreaLight : ILight
{
private const int _radius = 5;
private const int _samples = 16;
public Color Color { get; set; }
public Vector Location { get; set; }
#region ILight Members
public float GetLightingInformation(Vector point, ISceneObject[] scene, out Color color)
{
int intersectCount = 0;
float distance = -1;
for(int i = 0; i < _samples; i++)
{
bool intersects = false;
float rand = 0;
rand = _radius - (float)(new Random().NextDouble()*(2*_radius));
foreach (ISceneObject obj in scene)
{
Vector iPoint;
Vector loc = new Vector(Location.X + rand, Location.Y + rand, Location.Z + rand);
if (!obj.Intersect(new Ray(point, loc), out iPoint))
{
distance += (Location - point).SqLength;
}
else
{
intersects = true;
distance -= (Location - point).SqLength;
}
}
if (intersects)
intersectCount++;
}
float factor = 1-((float)intersectCount/_samples);
color = new Color(factor*Color.R, factor*Color.G, factor*Color.B);
return (float)Math.Sqrt(distance / (_samples - intersectCount));
}
#endregion
}
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
次要的一点,但这是否是随机类的最佳用途..
这不应该是..
minor point but is this the best use of the random class..
should this not be..
尝试为“loc”的每个组件生成不同的“rand”。 事实上,你的抖动点都位于一条线上。
Try generating a different "rand" for each component of "loc". As is, your jittered points all lie on a line.
您实际上是在方向为 (1, 1, 1) 的线上生成线上的点。 光源真的是线性的吗?
另外,我在你的例子中几乎看不到任何东西。 你能让你的相机靠近未来的影子而不是指向光线的方向吗?
You actually generate the point on the line on a line with direction (1, 1, 1). Is the lightsource really linear?
Also, I can barely see anything in your example. Could you make your camera nearer the to-be shadow and not pointing from the direction of the light?
看,这就是我来到这个网站的原因:)
现在每个轴都有自己的随机数,而且看起来好多了。 看起来仍然有点奇怪,但增加样本数量会有所帮助。 现在看起来像这个。
您知道减少图案形成的更有效方法吗?
最大的帮助是:不要为每个样本实例化随机。 它通过柔和的阴影使我的渲染速度大大提高了三倍! 我从来不知道 Random 的实例化成本如此之高。 哇。
多谢。
See, this is why I come to this site :)
Every axis has its own random now, and it looks a lot better. It's still a little weird looking, increasing the number of samples helps though. It now looks like this.
Do you know a more efficient way to reduce the pattern-forming?
The biggest help though: not instantiating Random for every sample. It seriously tripled my rendering speed with soft shadows! I never knew that Random was so costly to instantiate. Wow.
Thanks a lot.
在您的回复中,您要求一种改进的方法来制作柔和的阴影。 一种改进可能是,不是随机化来自同一点的所有光线,而是在所有轴上为每条光线提供不同的偏移量,以有效地为它们提供一个单独的小窗口来随机化。这应该会导致更均匀的分布。 我不知道这是否清楚,但描述它的另一种方式是作为垂直于阴影光线的网格。 网格中的每个图块都包含 n 条阴影射线之一,但网格中的位置是随机的。 在这里您可以找到教程的一部分,其中描述了如何使用它用于柔和的阴影。
In your response you asked for an improved way to make soft shadows. An improvement could be, instead of randomizing all the rays from the same point, to give each ray a different offset on all axes to effectively give them a seperate little window to randomize in. This should result in a more even distribution. I don't know if that was clear but another way to describe it is as a grid which is perpendicular to the shadow ray. Each tile in the grid contains one of the n shadow rays but the location in the grid is random. Here you can find a part of a tutorial which describes how this can be used for soft shadows.