【发布时间】:2012-06-16 15:08:44
【问题描述】:
我有一个 3D 场景,我想在视锥体内随机放置 X 个对象来“乱扔”它。
我尝试使用 3 个随机数:X + Y(视口 0..1),然后与相机的距离为 Z,然后投影使用相机距离。但是,这不会产生均匀分布,并且靠近相机的物体太多而远离相机的物体太少。
可以选择在立方体内生成一个随机点并过滤视锥体之外的那些点,但我正在生成大量这些点并且我担心性能。
如何以均匀分布的方式在截锥体内生成随机点?
【问题讨论】:
我有一个 3D 场景,我想在视锥体内随机放置 X 个对象来“乱扔”它。
我尝试使用 3 个随机数:X + Y(视口 0..1),然后与相机的距离为 Z,然后投影使用相机距离。但是,这不会产生均匀分布,并且靠近相机的物体太多而远离相机的物体太少。
可以选择在立方体内生成一个随机点并过滤视锥体之外的那些点,但我正在生成大量这些点并且我担心性能。
如何以均匀分布的方式在截锥体内生成随机点?
【问题讨论】:
这只是来自Amir Abiri 答案中链接中最终解决方案的格式良好的代码,因为链接中的代码具有糟糕的格式质量。
在 java 中,因为这就是我正在使用的,并且更改了一些变量名(此外,测试通过了;它工作得很好)
请注意,这可以通过多种方式变得更好,具体取决于应用程序(例如,正如Amir Abiri 的答案中的链接站点中提到的那样,一些变量可以计算一次并多次使用,只要相同正在使用截锥体)
public static final double ONE_THIRD = 1d / 3, EPSILON = 0.0000001;
public static double getUniformRandZInFrustum(Random random, double nearW, double nearH, double farW, double farH, double depth)
{
double a = (farW - nearW) * (farH - nearH) / (depth * depth);
double b = (nearH * (farW - nearW) + nearW * (farH - nearH)) / depth;
if (Math.abs(a) < EPSILON)
{
if (Math.abs(b) < EPSILON)
{
//Rectangular prism
return (random.nextDouble(depth));
}
//Trapezoidal prism
double c = nearW * nearH;
double area = depth * (c + depth * 0.5 * b);
double r = random.nextDouble(area);
return (-c + Math.sqrt(c * c + 2 * b * r)) / b;
}
//General case
double c = nearW * nearH;
double area = depth * (c + depth * (0.5 * b + depth * ONE_THIRD * a));
double r = random.nextDouble(area);
double det = b * b - 4 * a * c;
double part1 = b * (b * b - 6 * a * c) - 12 * a * a * r;
double part2 = Math.sqrt(part1 * part1 - det * det * det);
if (part1 < 0.0) part2 = -part2;
double part3 = part1 + part2;
if (part3 < 0.0) part3 = -Math.pow(-part3, ONE_THIRD);
else part3 = Math.pow(part3, ONE_THIRD);
return -(b + det / part3 + part3) / (2 * a);
}
【讨论】:
在 NDC 空间中分布点,并通过反截锥体变换将它们反向投影并划分到视图空间中。
【讨论】:
r_clip = P * r,然后,为了引入透视效果,应用 w 除以达到 NDC r_NDC.{x,y,z,w} = r_clip.{x,y,z,w} / r_clip.w。你必须走完全相反的方向。我建议看一下gluUnProject 的源代码(你可以在 Mesa3D 源代码中找到它)。
我在 gamedev.net 上的一篇旧论坛帖子中找到了解决方案:
【讨论】: