为什么现代渲染器越来越像数据库

很多开发者第一次接触图形学时。

脑海中的渲染器通常是这样的:

scene.draw();

或者:

renderer.draw(mesh);

从软件工程角度看。

这是一种典型的对象模型。


对象拥有:

Mesh

Material

Texture

渲染器负责:

遍历对象
↓
提交 Draw Call

看起来十分自然。


但随着现代引擎不断发展。

人们开始发现一个有趣现象。


越来越多渲染器内部已经不再围绕对象运行。

而开始围绕数据运行。


它们越来越像数据库。


最初的渲染器是对象驱动的

例如:

class MeshRenderer {
public:
    Mesh* mesh;
    Material* material;
};

渲染时:

for(auto& renderer : renderers)
{
    renderer.draw();
}

逻辑简单。

容易理解。


对象本身就是系统中心。


许多早期引擎都是这种结构。


对象数量开始爆炸

问题出现在规模增长之后。


例如:

100 个物体

时。

没有任何问题。


但当场景变成:

100000 个物体

时。

情况开始改变。


此时渲染器真正关心的往往不是:

Object #3812

而是:

哪些物体可见?

或者:

哪些物体使用同一材质?

或者:

哪些物体需要进入 Shadow Pass?

注意。

这些已经不是对象问题。


而是查询问题。


查询开始成为核心操作

观察现代渲染器会发现。

每一帧都在执行类似操作:

查找可见对象

查找透明对象

查找阴影对象

查找相同材质对象

查找相同 Mesh 对象

这些行为非常像:

SELECT *
FROM RenderObjects
WHERE Visible = true;

或者:

SELECT *
FROM RenderObjects
WHERE Material = X;

当然。

引擎不会真的运行 SQL。


但本质上。

已经开始执行:

查询
过滤
分组

这样的操作。


而这正是数据库最擅长的事情。


ECS 本质上也是一种数据表

上一篇文章讨论 Archetype 时提到。

现代 ECS 经常采用:

Position[]

Velocity[]

MeshID[]

MaterialID[]

这样的布局。


如果换一个角度观察。

这其实非常像:

Entity Table

例如:

Entity Mesh Material
0 12 3
1 4 8
2 12 3

渲染系统真正关心的是:

Material = 3

的所有实体。


或者:

Mesh = 12

的所有实体。


本质上已经变成:

数据查询

问题。


而不是:

对象调用

问题。


GPU Driven 更进一步

GPU Driven 出现后。

这种趋势更加明显。


过去:

object.draw();

现在:

Cull

LOD

Generate Draw Command

GPU 并不关心:

Object

是什么。


GPU 只关心:

数据

在哪里。


例如:

Transform[]

Bounds[]

MaterialID[]

然后执行:

过滤

排序

分组

这与数据库执行查询十分相似。


Bindless 让资源变成记录

传统资源系统:

Texture*

直接引用对象。


Bindless 出现后。

资源开始变成:

TextureIndex

例如:

Material {
    uint32_t albedoTexture;
    uint32_t normalTexture;
}

Shader 执行时:

textureTable[albedoTexture]

访问资源。


此时:

资源

更像数据库中的:

记录

索引开始比指针更重要。


Render Graph 像查询规划器

Render Graph 的出现。

进一步强化了这种趋势。


过去:

ShadowPass();

GBufferPass();

LightingPass();

执行顺序由程序员决定。


而 Render Graph 中:

Resource A
↓
Pass B
↓
Resource C

系统根据依赖关系自动安排执行顺序。


这与数据库中的:

Query Planner

其实有相似思想。


开发者描述:

需要什么

系统决定:

如何执行

关注点再次从过程转向数据。


为什么会发生这种变化

原因其实很简单。


对象模型更适合:

表达逻辑

而现代渲染器更关心:

处理海量数据

当规模达到:

10 万对象

100 万实例

数万资源

时。


系统真正面临的问题变成:

如何过滤

如何查询

如何批处理

这些问题与数据库面临的问题非常接近。


于是相似的设计开始出现。


现代渲染器越来越重视数据

观察过去十年的趋势会发现。

越来越多技术都在推动同一个方向:


ECS:

数据表

Archetype:

数据分区

GPU Driven:

数据查询

Bindless:

资源索引

Render Graph:

依赖规划

它们看起来完全不同。


但背后都在推动一件事:

从对象驱动转向数据驱动。


这并不意味着 OOP 失败了

看到这里。

很容易产生误解:

现代引擎已经不需要对象了。


实际上并不是。


对象仍然非常重要。


例如:

EditorWindow

AssetImporter

Project

这些系统天然适合对象模型。


变化发生在:

高频数据处理

领域。


因为当数据规模足够大时。

数据组织方式开始比对象关系更重要。


结语

很多人第一次学习渲染器时。

看到的是:

mesh.draw();

这样的代码。


但随着系统规模增长。

渲染器内部逐渐变成:

过滤

查询

排序

分组

批处理

的世界。


对象仍然存在。


但它们越来越像用户界面。


而真正驱动渲染器运行的。

是大量连续的数据。


从这个角度看。

现代渲染器并没有变成数据库。


但它们正在解决越来越多与数据库相同的问题。


而这或许也是现代引擎架构不断演化后的一个共同方向:

不再围绕对象组织系统。

而开始围绕数据组织系统。

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐