C
发布于 2026/09/03 · 阅读 8

Voxel Space 体素空间引擎解析 (2017)

  • #体素
  • #渲染引擎
  • #游戏开发
  • #Hacker News
  • #s-macke.github.io
Voxel Space 体素空间引擎解析 (2017)

Voxel Space 体素空间引擎 Web 演示

历史回顾

让我们回到1992年。当时的CPU比现在慢1000倍,GPU加速尚未普及或价格高昂。3D游戏完全由CPU计算,渲染引擎仅以单一颜色绘制填充多边形。

就在那一年,NovaLogic发布了游戏《Comanche》。当时的画面令人叹为观止,在我看来领先时代3年。你可以在山脉和山谷上看到更多细节,比如纹理,并且首次出现了平滑的着色甚至阴影。当然,画面是像素化的,但那个年代的所有游戏都是像素化的。

渲染算法

《Comanche》使用了一种称为Voxel Space的技术,它基于与光线投射类似的思想。因此Voxel Space引擎是一个2.5D引擎,它不具备常规3D引擎的所有自由级别。

高度图和颜色图

表示地形的最简单方式是通过高度图和颜色图。对于游戏《Comanche》,使用了10241024的单字节高度图和10241024的单字节颜色图,你可以在本网站下载。这些地图是周期性的:这种地图将地形限制为“地图上每个位置只有一个高度”——无法表示建筑物或树木等复杂几何体。然而,颜色图的一个巨大优势是它已经包含了着色和阴影。Voxel Space引擎直接使用颜色,无需在渲染过程中计算光照。

基本算法

对于3D引擎来说,渲染算法出奇地简单。Voxel Space引擎对高度图和颜色图进行光栅化,并绘制垂直线。下图演示了这种技术。

  1. 清屏。为了保证遮挡正确,从后往前渲染,这称为画家算法。
  2. 确定地图上对应于观察者相同光学距离的线。考虑视场和透视投影(物体越远越小)。
  3. 对线进行光栅化,使其匹配屏幕的列数。
  4. 从2D地图中获取线段对应的高度和颜色。
  5. 对高度坐标执行透视投影。
  6. 使用从透视投影获得的高度,以相应颜色绘制垂直线。

核心算法的最简单形式只有几行代码(Python语法):

def Render(p, height, horizon, scale_height, distance, screen_width, screen_height):
    # 从后往前绘制(从高z坐标到低z坐标)
    for z in range(distance, 1, -1):
        # 在地图上找到对应的线。此计算对应90°视场
        pleft = Point(-z + p.x, -z + p.y)
        pright = Point( z + p.x, -z + p.y)
        # 分割线段
        dx = (pright.x - pleft.x) / screen_width
        # 光栅化线段并为每个分段绘制垂直线
        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])
            pleft.x += dx

# 使用相机参数调用渲染函数:位置、高度、地平线位置、高度缩放因子、最大距离、屏幕宽度和高度
Render(Point(0, 0), 50, 120, 120, 300, 800, 600)

添加旋转

使用上述算法我们只能朝北看。不同的角度需要多几行代码来旋转坐标。

def Render(p, phi, height, horizon, scale_height, distance, screen_width, screen_height):
    # 预计算视角参数
    sinphi = math.sin(phi)
    cosphi = math.cos(phi)
    # 从后往前绘制(从高z坐标到低z坐标)
    for z in range(distance, 1, -1):
        # 在地图上找到对应的线。此计算对应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)
        # 分割线段
        dx = (pright.x - pleft.x) / screen_width
        dy = (pright.y - pleft.y) / screen_width
        # 光栅化线段并为每个分段绘制垂直线
        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])
            pleft.x += dx
            pleft.y += dy

# 使用相机参数调用渲染函数:位置、视角、高度、地平线位置、高度缩放因子、最大距离、屏幕宽度和高度
Render(Point(0, 0), 0, 50, 120, 120, 300, 800, 600)

性能提升

当然有很多技巧可以实现更高的性能。我们可以从前往后绘制,而不是从后往前。这样做的好处是,由于遮挡,我们不必每次都将线绘制到屏幕底部。然而,为了保证遮挡,我们需要一个额外的y-buffer。对于每一列,存储最高的y位置。因为我们是从前向后绘制,下一条线的可见部分只能大于之前绘制的最高线。

细节层次。近处渲染更多细节,远处渲染更少细节。

def Render(p, phi, height, horizon, scale_height, distance, screen_width, screen_height):
    # 预计算视角参数
    sinphi = math.sin(phi)
    cosphi = math.cos(phi)
    # 初始化可见性数组。屏幕上每列的Y位置
    ybuffer = np.zeros(screen_width)
    for i in range(0, screen_width):
        ybuffer[i] = screen_height
    # 从前往后绘制(从低z坐标到高z坐标)
    dz = 1.
    z = 1.
    while z < distance:
        # 在地图上找到对应的线。此计算对应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)
        # 分割线段
        dx = (pright.x - pleft.x) / screen_width
        dy = (pright.y - pleft.y) / screen_width
        # 光栅化线段并为每个分段绘制垂直线
        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] = height_on_screen
            pleft.x += dx
            pleft.y += dy
        # 移到下一行,远处时增加步长
        z += dz
        dz += 0.2

# 使用相机参数调用渲染函数:位置、视角、高度、地平线位置、高度缩放因子、最大距离、屏幕宽度和高度
Render(Point(0, 0), 0, 50, 120, 120, 300, 800, 600)

链接

  • Web项目演示页面
  • Voxel地形引擎介绍
  • 个人网站
  • 地图(颜色、高度)

许可证

本仓库的软件部分采用MIT许可证。请阅读许可证文件以获取更多信息。请注意,Voxel Space技术在某些国家可能仍受专利保护。颜色和高度图是从游戏《Comanche》中逆向工程得到的,因此不包含在许可证范围内。

本站开源。改进此页面。

8 阅读0 评论0 点赞

评论

登录 / 注册即可发布评论!
暂无评论,成为第一个发表评论的用户吧。