22. 走进三维:光线投射法

22.Approaching the Third Dimension: Raycasting

Let’s think for a moment about how to implement 3D graphics. One 3D graphics technique, ray-tracing, takes inspiration from the physical world. In the physical world, roughly, sources of illumination (the sun, electric lights, etc) shine in straight lines, which bounce around (potentially changing colour as they bounce) and some reach your eye. Simulating graphics this way is horribly inefficient, because you simulate all the light that never even reaches your eye. So ray-tracing inverts this idea, and instead projects virtual rays from your eyes, Superman-style (one ray per pixel on the screen), and bounces them around until it finds a light-source. Ray-tracing produces good quality graphics, but slowly — so it’s used in 3D films (e.g. Pixar’s films), but not in 3D games.

让我思考一会如何去实现3D绘图。一个叫做光线追踪的3D图形技术,受到物质世界的启发而产生。大致来说,在物质世界里,光源(太阳、灯光等)沿着直线照射,并四处反弹(当它们反弹时有可能改变颜色)并被人眼接收。按照这样的方式模拟图形是极其低效的,因为你模拟了所有从不会进入人眼的光线。于是光线追踪颠倒了这种思路,让人眼发射虚拟的光线,像超人一样(在屏幕上每一个像素产生一束光线),直到它遇到光源时进行反弹。光线追踪产生了高质量的图形效果——因此它被应用于3D电影中(比如皮克斯的电影),但是并没有在3D游戏中使用。

There are various ways to greatly increase the speed of this technique. One that we’ll look at in this post is to use virtual scenes where one ray per column of pixels is sufficient: this reduces the number of rays from, say, 800*600 down to just 600. This optimised version is called raycasting, and was used in a few early “3D” games such as Wolfenstein 3D:

有很多途径来显著地提高这种技术的速度。在这篇帖子中我们将看到的一种方式是使用虚拟场景,其中每一列像素产生一束光线就足够了:这降低了光线的数量,比如,从800乘600降低到了仅仅600.这种优化了的版本叫做光线投射,它被应用在一些早期的“3D”游戏中,比如德军司令部3D:

Note how there is only one solid wall in each vertical slice (column) of the image. So if for each column you send a ray to find which wall it hits, you can draw this seemingly 3D scene quite quickly.

注意,图像的每一个垂直面(列)上只有一个实心墙壁。因此如果对于每一列你发送一束光线去找到它撞到了哪些墙面,你便可以非常快速地绘制这种看起来类似3D的场景。

Cubes

立方体

The simplest type of scene that raycasting can be used with is a grid of squares. Lots of games use grids (or close enough for games): Bomberman, Pac-Man, etc. Raycasting in this grid allows you to see the grid from your character’s perspective. Here’s an example world that we’ll be using for our raycasting, as seen from above — the white dot is the player, light grey is empty, and the coloured or black sections are walls:

能够应用光线投射法的最简单的场景类型是网格。许多游戏使用网格(或者为了游戏而足够接近):炸弹人,吃蛋饼,等等。在这样的网格里使用光线投射法将允许你从角色的视角看到网格。这儿是一个例子,我们将用它来展示光线投射法,如同上面所了解的——白色的点是玩家,浅灰色是空地,彩色或黑色的部分是墙壁:

So now we need a method that can take a straight line, and figure out which squares the ray passes through, in order to find the first non-empty square that it will hit. This sounds familiar: we’ve already seen usedour line-drawing algorithm to implementline of sight calculations. The only change is that instead of projecting rays in a circle, we’ll project them in front of us, through an imaginary screen.

于是现在我们需要一个方法,它能够获得一条直线,并且能计算出光线穿过了哪些方格,从而找到它撞到的第一个非空方格。这听起来很熟悉:我们已经知道使用直线绘制算法去实现视线计算。唯一的改变是我们在自己面前发射光线,让它穿过一个假想的屏幕,而不是在一个圆圈中发射光线。

Here is a zoomed in portion of our world. The white square is the one that the player is standing in. Their eye is in the centre of the square, and is looking through a screen in front of them (facing at 45 degrees). We project lines from the eye, through the imaginary screen and see where they hit:

这儿是我们世界的一个放大部分。白色方格代表玩家所处的位置。眼睛在方格的中央,并且透过面前的屏幕看出去(面向45度角)。我们从眼睛发出直线,穿过假想的屏幕,并且看一看它们碰到了哪儿:

So most of the screen will be displaying the green wall, and the red wall will be visible on the far left of the screen. The line that escapes the image ends up hitting a black wall, so a small portion of black wall will be visible.

因此屏幕的大部分会显示绿色的墙面,而红色的墙面将显示在屏幕左边的角落。避开图像的直线将会最终撞击到黑色墙面,于是一小部分黑色的墙面将会可见。

Height

高度

We can use our line-following algorithm to find out which square we’ve hit, and uses that as the colour for the column. But if we just draw that colour from the top to the bottom of the image, we get this:

我们可以使用直线追踪算法去找到我们碰到了哪个方格,并且使用其颜色作为(像素)列的颜色。但是如果我们仅仅是从上到下绘制那种颜色的话,我们将看到:

That doesn’t look 3D! The key addition is to draw the right height in each column, by working out how high the wall is when projected on to the screen. For each ray that’s projected, we know the distance from the eye to the screen (always 0.5 for us), we know the height of the wall compared to where the eye hits (again, always 0.5 for us), so it just remains to use the distance of the wall from the eye (the length of the ray, which we get using Pythagoras) to calculate the height on the screen:

那看起来不是3D的!关键的改进是为每一列绘制正确的高度,这要计算出当墙面投射到屏幕上时它有多高。对于发射处的每一条光线,我们知道眼睛距离屏幕的距离(对我们来说始终是0.5),我们也知道视线所达墙壁的高度(同样,对我们来说始终是0.5),于是只剩下使用人眼与墙面的距离(光线的长度,使用勾股定理)去计算墙面在屏幕上的高度了。

To work out h, we can use the law of similar triangles. The small triangle on the left (with red sides 0.5 and h) is a scale replica of the larger triangle (with purple sides 1.5 and 0.5). When triangles are scale replicas, the ratio of any two sides is the same in each triangle. So:

为了计算出h,我们可以使用相似三角形法则。左边的小三角形(红边0.5和h)是与大三角形成比例的(紫色变1.5和0.5)。当三角形成比例时,在每个三角形里任意两边的比例是相同的。于是:

\displaystyle\frac{h}{0.5} = \displaystyle\frac{0.5}{1.5}

So in this case:

因此在这个例子里:

h = \displaystyle\frac{0.5 \times 0.5}{1.5} = \displaystyle\frac{1}{6}

More generally, where the distance from the eye to the screen is screenDist, and the distance from the eye to the wall is rayLength:

更一般地,从眼睛到屏幕的距离表示为screenDist,而从眼睛到墙壁的距离表示为rayLength,则:

h = \displaystyle\frac{0.5 \times \text{screenDist}}{\text{rayLength}}

You can then multiply h(which will be between 0 and 0.5) by the height of the screen to get how high the wall is above the vertical-centre line. And because our eye is exactly halfway up the wall, the wall is the same “height” below the vertical-centre line. Here’s the code:

接下来你可以用屏幕的高度乘以h(值得范围从0到0.5)来得到墙壁在垂直中心线上有多高。同时因为人眼是完全对着墙壁中间的,所以墙面在垂直中心线的下部是相同的“高度”。代码如下:

double distX = r.getHitX() - eyeX;
double distY = r.getHitY() - eyeY;
double rayLength = Math.sqrt(distX * distX + distY * distY);
height = (int)(((0.5 * screenDist) / rayLength) * img.getHeight());
            
img.drawLine(column, img.getHeight()/2 - height, column, img.getHeight()/2 + height);

We’re drawing on to an image named “img”. When we display this in Greenfoot, our previously block-colour picture now looks 3D:

我们在一个命名为“img”的图像上绘图。当我们在Greenfoot中显示它时,我们之前彩色方块的图片现在看起来有3D效果:

The effect is even more convincing when you can turn around, so goplay the scenario. Left and right turn your view, and the WASD keys move you around the grid (but not necessarily the way you’re facing).

当你转向的时候效果更加明显,因此去玩一下游戏剧本。用左右键旋转你的视角,用WASD键在网格中移动(但不是必定朝着你的方向前进)。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
31套vtk3d图像体绘制/vtk光线投射/vtk三维重建程序源码是一套用于可视化和处理三维图像数据的工具集。其中包括了vtk库中的一些功能和算。 vtk3d图像体绘制是指利用vtk库中的相关函数来创建和绘制三维体数据图像。vtk库提供了一系列绘制基本图形的函数,并可以通过组合这些基本图形来创建并显示三维体数据。利用vtk3d图像体绘制,我们可以将三维图像数据以一种生动直观的方式呈现出来。 vtk光线投射是指利用光线投射三维图像数据进行可视化和模拟。该算通过投射一束光线三维图像中,并根据光线与不同物体的相交情况来计算光线在图像中的传播和衰减。通过vtk提供的光线投射函数,我们可以将计算得到的光线三维图像上进行显示,从而达到模拟真实光线传输的效果。 vtk三维重建程序源码通过利用vtk库中的三维重建函数,实现对二维图像数据进行三维重建的功能。该算通过对多个二维图像进行处理和比对,来推测出图像所代表物体的三维形状。vtk提供了一系列三维重建函数和算,可以根据不同的需求选择适用的方。利用vtk三维重建程序源码,我们可以将多个二维图像数据转化为一个三维模型,并进行后续的可视化和分析。 总的来说,这套31套vtk3d图像体绘制/vtk光线投射/vtk三维重建程序源码是一个功能强大的工具集,可以帮助我们处理和可视化三维图像数据,并进行相应的模拟和分析。无论是在科学研究、医学影像还是工程设计等领域,这套工具都具有广泛的应用价值。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值