英文:
Why does my C# ray casting rendering engine produce curved lines when they should be straight?
问题
目前正在使用C#编写一个基于Wolfenstein 3D引擎的简单光线投射引擎。它接受一个字符串数组作为地图,并渲染摄像头所见的内容。具体操作是对输出窗口中的每个垂直像素切片投射960条光线,并测量该光线的行进距离。然后,将该值通过一个函数处理,并将其转换为垂直切片的一个像素宽度的矩形的高度。问题在于,对于非常近的墙壁,视野会使墙壁的直边弯曲,类似于y=ln(x)图形。
地图(c为摄像头,面朝东):
输出(带有注释):
对我来说,这样的系统在渲染这个小部分时出现错误似乎很奇怪,而其他部分则渲染正常。我已经在光线投射长度上添加了欧几里得距离修正计算,如下所示:
float adjusted_dist = (float)Math.Cos(angle) * length;
这解决了类似的鱼眼效应,但没有解决这个问题。
我的光线投射代码在这个顶视图的调试图像中表现正常:
屏幕大小为960px乘以540px,我将光线的距离转换为屏幕切片的高度,按照以下公式进行计算:
int pixels = (int)(540 - (540 * heights[i] / max_dist));
其中max_dist
是一个常量,值为150。我的理由是,这将准确地将光线的长度从范围(0-150)转换为范围(0-540),因为目前我的场景中没有任何东西距离摄像头超过150个单位(地图中的每个字符都是一个10个单位乘以10个单位的正方形)。然而,我怀疑错误的源头在于这一行,但确切的原因我不清楚。希望能得到一些意见,以及将光线长度转换为像素切片高度的潜在其他方法。
我已经排除了大部分渲染代码,因为它们运行正常。但如果需要更多的代码片段,我可以提供。
英文:
Currently writing a simplistic ray casting engine based off of the Wolfenstein 3D engine in C#. It takes a string[] as a map and renders what the camera would see. The way this is done is casting 960 rays, one for each vertical slice of pixels in the output window, and measuring the distance that that ray travels. It then runs that value through a function and converts it into a height for the one-pixel wide rectangle rendered for that vertical slice. My issue is that, for very close walls, the view distorts the straight edges of the wall to curve off akin to a y=ln(x) graph.
Map (c is camera, facing due East):
Output (with comments):
It just seems strange to me that such a system would incorrectly render this small part, when the rest renders fine. I have added a Euclidean distance correction calculation to the ray cast length already, see below.
float adjusted_dist = (float)Math.Cos(angle) * length;
This fixed a similar fisheye effect but did not resolve this issue.
My raycasting code works fine as shown by this top-down debug image I rendered:
With a screen size of 960px by 540px, I convert the distance of the ray into a height for the screen slice according to this formula:
int pixels = (int)(540 - (540 * heights[i] / max_dist));
Where max_dist
is a constant with a value of 150. My reasoning was that this would accurately convert the length of the ray into the range (0-540) from the range (0-150) as there is nothing currently in my scene that is more than 150 units away from the camera. (Each character in the map is a 10unit by 10unit square). However, this is the line in which I suspect the error originates from, however the exact cause evades me. Would love some input and potential other approaches to converting ray length to pixel slice height.
I have excluded most of the rendering code as it works fine, however if more code is needed I can provide more snippets.
答案1
得分: 1
int pixels = (int)(540 - (540 * heights[i] / max_dist));
仅在光线的长度为0时才会达到最大值(540),首先,这在任何情况下都不会发生(从调试渲染中可以看出),其次,我认为您希望在距离更远的地方绘制最大像素高度。从输出来看,屏幕的右侧约有三分之一的部分已经具有最大像素高度。
我不太确定如何更好地实施您的方法,我有一个想法是从光线长度中减去一些量(当然不能小于0),但这可能不会解决核心问题,一切看起来都会更接近,但也许值得一试。
我还有一个不同的方法的想法,从更“现代”的相机出发:
int wallheight = 20; //几乎只是猜测
float horizontal_view = angle; //以弧度表示
float vertical_view = (horizontal_view / screen_width) * screen_height;
int pixels = (int)Math.Round(Math.Min((wallheight / (2 * Math.Tan(0.5f * vertical_view) * ray_length)) * screen_height, screen_height), 0);
这里,角度基本上是您的视野,也是您用于光线投射的角度。然后,通过使用2 * tan(0.5 * vertical_view) * length
,我们计算出在那个距离上我们能看到的最大高度。如果我们将墙的高度除以它,我认为我们应该得到相对于我们能看到的高度的墙尺寸的百分比。现在将其乘以您的 540,我们得到要垂直绘制的像素数,这当然可以高于 540,所以我添加了Min()
,也许对您来说甚至不需要这个。
希望这个方法有效,因为我无法真正测试它。
英文:
I think the main problem here is that this:
int pixels = (int)(540 - (540 * heights[i] / max_dist));
will only be at its maximum (540), when the length of the ray is 0. First of all, this won't be the happening in any case (from looking at the debug render), and second: I think you would want to have the maximum pixel height drawn already at distances farther away than no distance at all. Looking at the ouput, about a third of the right side of the screen would have the maximum pixel height already.
I'm not sure how you could do your approach better, one idea I had is substracting some amount from the ray length (with a minimum at 0 of course), but that probably wouldn't fix the core issue and everything would look as if closer by, but maby it is worth a shot.
I also got the idea for a diffenrent approach, going from a more "modern" camera:
int wallheight = 20; //pretty much just a guess
float horizontal_view = angle; //in radians
float vertical_view = (horizontal_view / screen_width) * screen_height;
int pixels = (int)Math.Round(Math.Min((wallheight / (2 * Math.Tan(0.5f * vertical_view) * ray_length)) * screen_height, screen_height), 0);
here, angle is basically your field of view, the one you are using for tha raycasts aswell. Then, by using 2 * tan(0.5 * vertical_view) * length
, we calculate kind of the maximum height that we can see at that distance. If we divide the wallheight by that, we should, I think get the percentage of the wallsize compared to the height we can see. Now multiply that by your 540 and we get the amount of pixels we have to draw vertically, which can of course be higher than the 540, so I added the Min(), maby that won't even be necessary for you.
I hope this works, since I can't really test it
答案2
得分: 0
你遇到了一个名为"鱼眼"的问题。
你应该调整ray_distance
。
new_ray_distance = ray_distance * cos(ray_angle - player_angle)
英文:
You have a problem called Fisheye.
You should adject the ray_distance
new_ray_distance= ray_distance*cos(ray_angle - player_angle)
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论