Voxel Space:不到20行代码实现地形渲染
2017-11-30 15:19
621 查看
Voxel Space
![](https://i.imgur.com/Kz8p3Xv.gif)
体素空间引擎的Web Demo
追溯历史
让我们把时间拨回到1992年。当时的CPU处理速度比现在的要慢1000倍,通过GPU加速当时还未问世,而且CPU也是无法承受的。3D游戏仅在CPU上进行计算,渲染引擎使用单一颜色对多边形进行渲染填充。![](https://i.imgur.com/7nQO1Zg.gif)
MicroProse于1991年发布的游戏Gunship 2000
同年NovaLogic也发布了游戏科曼奇。
![](https://oscdn.geek-share.com/Uploads/Images/Content/201711/f19b86917adabf95294fdb730ff5cc8e.gif)
NovaLogic于1992年发布的游戏Comanche
在我看来,当时这种图形出来以后简直叹为观止,它最起码提前了3年。用户可以看到很多的细节,比如山脉,甚至山谷的纹理,这是第一次有一个比较清晰的阴影。当然,它是像素化的,但那时候所有的游戏都是像素化的。
渲染算法
科曼奇使用了一种名为体素空间(VoxelSpace)的技术,它和ray casting是基于同一个想法。因此,体素空间引擎是2.5D引擎,它不具有规则的3D引擎提供的所有自由度。。
高度地图和颜色地图
高度地图和颜色图是表示地形最简单的方法。科曼奇使用了1024 * 1024一个字节代表了高度地图,同样使用了1024 * 1024一个字节表示颜色地图,你可以在这个网站上下载。这些地图是周期性:
![](https://oscdn.geek-share.com/Uploads/Images/Content/201711/47f5dcf7449ab5f25e4f3c96bfddfa33.gif)
这样的地图将地形限制为“地图上每个位置一个高度” - 因此像建筑物或树木这样的复杂几何形状不可能表示出来。然而,色彩地图的一大优点是,它已经包含了色彩和阴影。体素空间引擎只需要颜色,在渲染过程中不需要计算光照。
基本算法
对于3D引擎来说,渲染算法非常简单。体素空间引擎负责渲染高度地图和颜色地图,并绘制垂直线。下图演示了这种技术。
![](https://oscdn.geek-share.com/Uploads/Images/Content/201711/1ddf945cc27ac2ba816dbe25ad1e9e43.gif)
清除屏幕。
为了保证遮挡从后面开始并呈现在前面。这被称为画家算法。
确定地图上的线,它对应于与观察者相同的光距离。考虑视场和透视投影(物体在更远的地方)
光栅线是用来匹配屏幕的列数。
从线段对应的二维地图中检索高度和颜色。
执行高度坐标的透视投影。
用透视投影中检索到的高度画一条垂直线。
核心算法以最简单的形式包含了几行代码(python语法):
def Render(p, height, horizon, scale_height, distance, screen_width, screen_height): # Draw from back to the front (high z coordinate to low z coordinate) for z in range(distance, 1, -1): # Find line on map. This calculation corresponds to a field of view of 90° pleft = Point(-z + p.x, -z + p.y) pright = Point( z + p.x, -z + p.y) # segment the line dx = (pright.x - pleft.x) / screen_width # Raster line and draw a vertical line for each segment for i in range(0, screen_width): height_on_screen = (height - heightmap[pleft.x, pleft.y]) / z * scale_height. + horizon DrawVerticalLine(i, height_on_screen, screen_height, colormap[pleft.x, pleft.y]) p1eft.x += dx # Call the render function with the camera parameters: # position, height, horizon line position, # scaling factor for the height, the largest distance, # screen width and the screen height parameter Render( Point(0, 0), 50, 120, 120, 300, 800, 600 )1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
添加旋转
按照上面的算法我们只能看到北面。不同的角度需要多行代码来旋转坐标。
![](https://oscdn.geek-share.com/Uploads/Images/Content/201711/b290ad548a073ba8936498d6a7f909be.gif)
def Render(p, phi, height, horizon, scale_height, distance, screen_width, screen_height): # precalculate viewing angle parameters var sinphi = math.sin(phi); var cosphi = math.cos(phi); # Draw from back to the front (high z coordinate to low z coordinate) for z in range(distance, 1, -1): # Find line on map. This calculation corresponds to a field of view of 90° pleft = Point( (-cosphi*z - sinphi*z) + p.x, ( sinphi*z - cosphi*z) + p.y) pright = Point( ( cosphi*z - sinphi*z) + p.x, (-sinphi*z - cosphi*z) + p.y) # segment the line dx = (pright.x - pleft.x) / screen_width dy = (pright.y - pleft.y) / screen_width # Raster line and draw a vertical line for each segment for i in range(0, screen_width): height_on_screen = (height - heightmap[pleft.x, pleft.y]) / z * scale_height. + horizon DrawVerticalLine(i, height_on_screen, screen_height, colormap[pleft.x, pleft.y]) p1eft.x += dx p1eft.y += dy # Call the render function with the camera parameters: # position, viewing angle, height, horizon line position, # scaling factor for the height, the largest distance, # screen width and the screen height parameter Render( Point(0, 0), 0, 50, 120, 120, 300, 800, 600 )1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
更多的性能说明
当然,要想达到更高的性能,还有很多小技巧可以使用。
与从后面到前面绘制相比,从前面到后面进行绘制会更好。优点之一就是我们不必每次都因为遮挡而需要在屏幕的底部画线。但是,为了保证遮挡,我们需要一个额外的Y缓冲区。对于每一列来说,相当于y的最高位置已经存储了。因为我们是按照从前面到后面这个顺序进行绘制的,那么下一行的可见部分只能大于先前绘制的最高行。
详细的渲染程度。前面的细节渲染多一点,远处的细节渲染的少一点。
![](https://oscdn.geek-share.com/Uploads/Images/Content/201711/fcfcf37263cfa7fd13e64e3010f16e84.gif)
def Render(p, phi, height, horizon, scale_height, distance, screen_width, screen_height): # precalculate viewing angle parameters var sinphi = math.sin(phi); var cosphi = math.cos(phi); # initialize visibility array. Y position for each column on screen ybuffer = np.zeros(screen_width) for i in range(0, screen_width): ybuffer[i] = screen_height # Draw from front to the back (low z coordinate to high z coordinate) dz = 1. z = 1. while z < distance # Find line on map. This calculation corresponds to a field of view of 90° pleft = Point( (-cosphi*z - sinphi*z) + p.x, ( sinphi*z - cosphi*z) + p.y) pright = Point( ( cosphi*z - sinphi*z) + p.x, (-sinphi*z - cosphi*z) + p.y) # segment the line dx = (pright.x - pleft.x) / screen_width dy = (pright.y - pleft.y) / screen_width # Raster line and draw a vertical line for each segment for i in range(0, screen_width): height_on_screen = (height - heightmap[pleft.x, pleft.y]) / z * scale_height. + horizon DrawVerticalLine(i, height_on_screen, ybuffer[i], colormap[pleft.x, pleft.y]) if height_on_screen < ybuffer[i]: ybuffer[i] = heightonscreen p1eft.x += dx p1eft.y += dy # Go to next line and increase step size when you are far away z += dz dz += 0.2 # Call the render function with the camera parameters: # position, viewing angle, height, horizon line position, # scaling factor for the height, the largest distance, # screen width and the screen height parameter Render( Point(0, 0), 0, 50, 120, 120, 300, 800, 600 )1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
链接
Web项目demo 页面体素地形引擎简介
个人网站
地图
颜色,高度![](https://i.imgur.com/bxgM1xO.png)
![](https://i.imgur.com/nBCSqFW.png)
颜色,高度
![](https://i.imgur.com/PzYZ5n6.png)
![](https://i.imgur.com/M72Jpy0.png)
颜色,高度
![](https://i.imgur.com/jFaoiCA.png)
![](https://i.imgur.com/vEj2LK8.png)
颜色,高度
![](https://i.imgur.com/jwRPasf.png)
![](https://i.imgur.com/PC0iNso.png)
颜色,高度
![](https://i.imgur.com/eewjyic.png)
![](https://i.imgur.com/PvtqQav.png)
颜色,高度
![](https://i.imgur.com/H2y94Vf.png)
![](https://i.imgur.com/QCerCBa.png)
颜色,高度
![](https://i.imgur.com/0OyDFaq.png)
![](https://i.imgur.com/ejD0W9g.png)
颜色,高度
![](https://i.imgur.com/VGYL5o0.png)
![](https://i.imgur.com/QCerCBa.png)
颜色,高度
![](https://i.imgur.com/fs8D1mE.png)
![](https://i.imgur.com/2PPJGVy.png)
颜色,高度
![](https://i.imgur.com/AQzWClb.png)
![](https://i.imgur.com/86UkrTb.png)
颜色,高度
![](https://i.imgur.com/ArcNaR4.png)
![](https://i.imgur.com/QJXXmr5.png)
颜色,高度
![](https://i.imgur.com/egZbQWE.png)
![](https://i.imgur.com/QJXXmr5.png)
颜色,高度
![](https://i.imgur.com/t6zGqam.png)
![](https://i.imgur.com/isz42ZD.png)
颜色,高度
![](https://i.imgur.com/CYEWbRQ.png)
![](https://i.imgur.com/YnCOn4J.png)
颜色,高度
![](https://i.imgur.com/3IHi8Wy.png)
![](https://i.imgur.com/JwhvGEp.png)
颜色,高度
![](https://i.imgur.com/4avvZz6.png)
![](https://i.imgur.com/dzPZAVg.png)
颜色,高度
![](https://i.imgur.com/NhAyJ6Z.png)
![](https://i.imgur.com/X5IWdiF.png)
颜色,高度
![](https://i.imgur.com/xh2Y6p9.png)
![](https://i.imgur.com/1Bo579Y.png)
颜色,高度
![](https://i.imgur.com/1Asdx5V.png)
![](https://i.imgur.com/rxtkYXn.png)
颜色,高度
![](https://i.imgur.com/4jOf2fs.png)
![](https://i.imgur.com/wCBlNlA.png)
颜色,高度
![](https://i.imgur.com/HunNybw.png)
![](https://i.imgur.com/VWp1OGk.png)
颜色,高度
![](https://i.imgur.com/YtMEp3n.png)
![](https://i.imgur.com/KMB2TTf.png)
颜色,高度
![](https://i.imgur.com/MDM5CUh.png)
![](https://i.imgur.com/3JpDemW.png)
颜色,高度
![](https://i.imgur.com/u2BJCqS.png)
![](https://i.imgur.com/6HvWQik.png)
颜色,高度
![](https://i.imgur.com/vdgCamJ.png)
![](https://i.imgur.com/yBmVDWX.png)
颜色,高度
![](https://i.imgur.com/zzTUMg1.png)
![](https://i.imgur.com/dxtFSNQ.png)
颜色,高度
![](https://i.imgur.com/egvtsMB.png)
![](https://i.imgur.com/KAAtLvr.png)
颜色,高度
![](https://i.imgur.com/fdBFqmD.png)
![](https://i.imgur.com/v8SVVTG.png)
颜色,高度
![](https://i.imgur.com/KP8T4UU.png)
![](https://i.imgur.com/GyGCPYs.png)
颜色,高度
![](https://i.imgur.com/xFyo9OE.png)
![](https://i.imgur.com/LK85ya6.png)
相关文章推荐
- Voxel Space:不到20行代码实现地形渲染
- Voxel Space:不到20行代码实现地形渲染
- golang利用不到20行代码实现路由调度详解
- 133 行代码实现质感地形
- 20行代码实现的一个CSS覆盖率测试脚本
- 不到200行 JavaScript 代码如何实现富文本编辑器
- HTML5游戏实战(4): 20行代码实现FlappyBird
- 每日20行之4~~~两种方法java实现简易计算器 附带复杂计算器代码(优先级顺序)
- vue.js实现条件渲染的实例代码
- 20行代码实现微信拜年消息智能自动回复
- js开发实现简单贪吃蛇游戏(20行代码)
- 不到36行代码实现Scala版本的Kmeans
- [C#]使用 C# 代码实现拓扑排序 dotNet Core WEB程序使用 Nginx反向代理 C#里面获得应用程序的当前路径 关于Nginx设置端口号,在Asp.net 获取不到的,解决办法 .Net程序员 初学Ubuntu ,配置Nignix 夜深了,写了个JQuery的省市区三级级联效果
- 20行代码实现网页图片抓取。(待完善
- 最简单的区块链实现,不到50行代码!(一)
- golang不到30行代码实现依赖注入的方法
- PHP不到100行代码实现SSO单点登录
- VB.NET轻松实现任务栏程序 [不到10行代码]
- 实现法线贴图3D模型渲染的脚本代码(附源码)
- 地形渲染的动态LOD四叉树算法详细实现