文件结构和渲染管线
本章节旨在让开发者对 OptiFine 的光影加载方法和处理管线进行快速认识,你只需要大致了解它们即可。
OptiFine 加载光影的方法
OptiFine 会遍历 shaderpacks
下的文件夹和 .zip
压缩包,将所有包含 shaders
文件夹的内容都视为光影外壳(shaders
也是这一层级下唯一的寻找目标,其中包含了所有着色器和配置文件),我们将它们称为外壳文件夹 。除了读取外壳文件夹,OptiFine 还会读取 .txt
文件,如果这个文件的名字与外壳文件夹相同(如果是压缩包,则包含 .zip
后缀名),则会视为光影设置文件。
需要注意的是,OptiFine 会先寻找外壳文件夹,然后在外壳内寻找
shaders
文件夹,因此,如果将一个包含着色器文件的shaders
文件夹直接放在shaderpacks
下,或者外壳内还嵌套了一个文件夹(常见于压缩包,一些压缩软件会在打包时自动新建一层文件夹),则在新版本的 OptiFine 下不会读取。└─ shaderpacks ├─ shaderNameA │ └─ shaders # 正常读取 │ └─ <着色器程序> ├─ shaders # 缺少外壳,无法读取 │ └─ <着色器程序> └─ shaderNameA └─ folderB └─ shaders # 多重嵌套,无法读取 └─ <着色器程序>
我们将 shaders
文件夹称为主文件夹。
在 shaders
下, OptiFine 首先会寻找配置文件
shaders.properties
,当前光影的内部配置文件,我们称为光影配置。entities.properties
、item.properties
和block.properties
,实体、物品和方块类型在当前光影中的映射 ID,我们称为实体/物品/方块 ID 配置。
获取到配置文件之后,OptiFine 会继续寻找 lang
和 world<ID>
文件夹。
lang
文件夹下包含对可调宏定义和const
修饰符变量的翻译文件,或者说语言文件 ,它做的实际上是将宏定义
和恒量
重映射为对应字符串。语言文件的命名规则是
<语言>_<国家或地区>.lang
,如zh_cn.lang
。其中地区可以大写,但在某些 OptiFine 版本下,可能会导致包括中文在内的一些语言无法读取,因此,我们建议都使用小写。光影设置会尝试读取对应当前游戏设置的语言文件,如果语言文件不存在,则先尝试读取英语语言文件(
en_us.lang
),如果英语语言文件也不存在,则直接显示代码名。
world<ID>
文件夹下是对应维度的着色器,world0
表示主世界,world-1
表示下界,world1
表示末地,其他模组额外生成的世界通常也会有一个序号,如暮色森林是world7
。OptiFine 会优先使用这里的着色器渲染对应的世界,当文件夹不存在时,会使用主文件夹下的着色器。我们将
world<ID>
称为维度文件夹 ,对应的着色器称为维度着色器。
回到 shaders
文件夹下,OptiFine 在检查了所有默认读取的文件和文件夹之后,开始编译光影。它会将主文件夹和每个维度文件夹下着色器文件的代码编译为程序。根据管线顺序,OptiFine 会主动读取并编译 .vsh
、 .gsh
、 .fsh
和 .csh
文件,分别对应顶点着色器、 几何着色器、 片段着色器和计算着色器。
OptiFine 允许我们使用类似 C / C++ 的 #include
宏来调用其不会主动读取的文件,路径的根目录为主文件夹(shaders
):
例如使用
来调用位于
下的文件 shadow.glsl
这里总结了 OptiFine 默认读取的光影文件。
管线
OptiFine 使用的是延迟渲染法:
在几何缓冲阶段将所有几何体按类型分批传入不同的着色器,并将计算结果写入缓冲区。
在延迟处理阶段,计算颜色并输出结果。
OptiFine 接管了从 GUI 开始直到场景输出的全部管线,下图列出了整个画面输出流程。
你可以在 这里 查看和保存附带深色背景的原图。
下面,我们从整个管线的起端开始,逐步为大家拆解 OptiFine 都在这些环节干了什么。