Math 光线投射引擎渲染创建向屏幕边缘增加的轻微失真

Math 光线投射引擎渲染创建向屏幕边缘增加的轻微失真,math,canvas,3d,rendering,raycasting,Math,Canvas,3d,Rendering,Raycasting,我正在为HTML5画布开发一个基本的光线投射引擎,该引擎用于Wolfenstein 3D和Doom等游戏,作为一个学习练习/爱好项目。我已经到了使用纹理贴图在画布上渲染墙的地步,在经过相当多的努力使交叉点测试功能正确后,它工作得相当好 我正在纠正“鱼缸”/“鱼眼”失真效应(由于与屏幕中心的角度增大而增加到交点的距离),但屏幕边缘仍有非常轻微但明显的弯曲失真。这可以在下图中看到(我画了红色的直线以使效果更加明显): 有人能解释这种扭曲的根源吗?这不是一个大问题,但我还没能找出原因,所以我显然遗漏

我正在为HTML5画布开发一个基本的光线投射引擎,该引擎用于Wolfenstein 3D和Doom等游戏,作为一个学习练习/爱好项目。我已经到了使用纹理贴图在画布上渲染墙的地步,在经过相当多的努力使交叉点测试功能正确后,它工作得相当好

我正在纠正“鱼缸”/“鱼眼”失真效应(由于与屏幕中心的角度增大而增加到交点的距离),但屏幕边缘仍有非常轻微但明显的弯曲失真。这可以在下图中看到(我画了红色的直线以使效果更加明显):

有人能解释这种扭曲的根源吗?这不是一个大问题,但我还没能找出原因,所以我显然遗漏了一些东西,我相信一定有人知道答案。我已经广泛地搜索了这个问题,网上没有太多信息,但我确实在论坛帖子中找到了以下片段:

“使用恒定角度增量而不是水平投影引起的翘曲完全是另一回事——这是一种横向拉伸/聚束效应,尽管通常它只是一种几乎不明显的效应(对于合理的视场,尽管你可以定义999999度的视场这一事实应该会引起注意),除了一开始就做对之外,根本没有任何合理的补偿方法。使用固定增量的角度是错误的,这就是所有的原因。”

这听起来可能是指我正在经历的同一种失真,但除了建议固定角度增量是问题的根源(这是一种向屏幕边缘增加的弯曲失真,似乎与此相符)之外,它没有提供太多帮助或见解。我用来纠正失真的函数是:

function m_CorrectRayLengthDistortion( dist, angleFromCentre ){

    return dist * Math.cos( MOD_Maths.degToRad( angleFromCentre ) );
}
MOD_数学是一个实用模块(在本例中用于将角度从度转换为弧度,以便余弦函数可以使用它)

非常感谢在这方面提供的任何帮助,如果有人回答这个问题,它将有望为将来遇到这个问题的任何人提供一个指南,因为目前在线上缺乏关于这个问题的信息


谢谢:)

我很早以前就正确地解决了这个问题,但直到现在还没有时间更新答案。我已经删除了我以前不正确的答案(它给出了几乎正确的结果,但采用了间接的方法,因为我不了解问题的根本原因)

正如Sam在其早期评论中提到的,问题的根本原因是,如果要实现等距列(这是渲染结果看起来不失真所必需的),则固定角度增量实际上是不正确的。这是在论坛帖子中提到的,但尽管我发现了这一点,但直到很久以后,我才完全理解为什么会出现这种情况,或者如何解决这个问题

为了在屏幕上实现等间距的列,每个光线必须从视角出发,并穿过沿投影面等间距分布的像素,这意味着随着光线从屏幕中心像素进一步移动,从观察方向增加的角度的增量逐渐变小。下面的图片说明了这一点(抱歉,这不完全是一件艺术品):

对于小视场,问题不是很明显,但随着视场的增加,问题会变得更加严重(在我的图表中,视场相当大,可以清楚地说明问题)。要正确计算光线角度增量,必须使用以下过程:

其中:

ang = ray angle from the look direction, whose ray passes through the central x coordinate of the screen;
opp = opposite side (equivalent to the distance of the screen X coordinate through which the ray passes from the screen X coordinate of the central pixel);
adj = adjacent side (equivalent to the distance from the point of view to the projection surface, which will be predetermined in code somewhere);
我们可以使用以下公式(为了清楚起见,包括推导):

我的引擎中的Javascript代码示例:

for( var x = 0; x < canvasSizeX; x++ ){

    var xAng = _atan( ( x - canvasSizeHalfX ) / m_DistToProjSurf );
    xRayAngles.push( xAng );
}
for(var x=0;x

由于在线提供的有关光线投射引擎的信息有点稀缺,而且由于这一特定问题没有在任何主要教程中明确介绍,我想用正确的信息更新这篇文章,以防其他人也有和我一样的问题,并且不明白为什么。希望这会对某人有所帮助。

在我自己的光线投射引擎上花了几个小时试图解决这个精确的问题后,我想给出更详细的数学背景,说明为什么这是正确的答案,因为我一开始并不完全相信。尤其是在进行透视投影时,您已经需要纠正一些球形变形(鱼缸效应)。这里描述的效果是完全不同的效果

这就是我在引擎里看到的:相机在一个正方形的房间里,看着一个角落,角度大约为45°,FOV为90°。它似乎有轻微的球形变形。后来添加了红线,运动中的视觉效果也更为明显,但制作GIF是一个PITA:

这是相同的房间,相同的位置和角度,但FOV为70°。它不那么明显(而且在运动中更容易看到):

我的光线投射引擎的第一个版本从-FOV/2+摄影机角度到FOV/2+摄影机角度发射光线,每个角度的间隔为FOV/SCREEN\u宽度度(在我的例子中,SCREEN\u宽度为640)

这是一个俯视图模式,屏幕宽度=9:

你可以看到这里的问题:当我们使用一个固定的角度时,唯一能保证恒定的就是两条光线之间的圆弧。但投影平面上的线段应该是常数。我们可以看到
for( var x = 0; x < canvasSizeX; x++ ){

    var xAng = _atan( ( x - canvasSizeHalfX ) / m_DistToProjSurf );
    xRayAngles.push( xAng );
}